diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e9a4e11b85be899e1e19ca2a6523bfb080e69501
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
+*.toDelete
+output/**
+*.class
+*~
+*.iml
+*/.idea/**
+.idea/**
+.idea
+*.log
+*.log.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
+*/.classpath
+.classpath
+*/.project
+.project
+.cache/**
+target/
+build/
+tmp_deb_control/
+tmp_rpm_control/
+tmp_sh/
+.gwt/
+.settings/
+/bin
+bin/
+**/dependency-reduced-pom.xml
+pom.xml.versionsBackup
+.DS_Store
+**/.gradle
+**/local.properties
+**/build
+**/target
+**/Californium.properties
+**/Californium3.properties
+**/.env
+.instance_id
+rebuild-docker.sh
+*/.run/**
+.run/**
+.run
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..c8f142f0bf9312fe669e9294da541b36ad2e48f2
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2016 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index c632287cf9f36028056582910540c0d6ee8bbcc3..581e71977d20747a26c72a3b41be34950b62e900 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,42 @@
-# thingsboard
+# ThingsBoard
+[](https://gitter.im/thingsboard/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](https://builds.thingsboard.io/viewType.html?buildTypeId=ThingsBoard_Build&guest=1)
+ThingsBoard is an open-source IoT platform for data collection, processing, visualization, and device management.
+
-## Getting started
+## Documentation
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+ThingsBoard documentation is hosted on [thingsboard.io](https://thingsboard.io/docs).
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+## IoT use cases
-## Add your files
+[**Smart energy**](https://thingsboard.io/smart-energy/)
+[](https://thingsboard.io/smart-energy/)
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
+[**Fleet tracking**](https://thingsboard.io/fleet-tracking/)
+[](https://thingsboard.io/fleet-tracking/)
-```
-cd existing_repo
-git remote add origin https://interne.hydatis.fr/gitlab/Yassmine.Mestiri/thingsboard.git
-git branch -M main
-git push -uf origin main
-```
+[**Smart farming**](https://thingsboard.io/smart-farming/)
+[](https://thingsboard.io/smart-farming/)
-## Integrate with your tools
+[**IoT Rule Engine**](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/)
+[](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/)
-- [ ] [Set up project integrations](https://interne.hydatis.fr/gitlab/Yassmine.Mestiri/thingsboard/-/settings/integrations)
+[**Smart metering**](https://thingsboard.io/smart-metering/)
+[](https://thingsboard.io/smart-metering/)
-## Collaborate with your team
+## Getting Started
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
-
-***
-
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
-
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
-
-## Name
-Choose a self-explaining name for your project.
-
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+Collect and Visualize your IoT data in minutes by following this [guide](https://thingsboard.io/docs/getting-started-guides/helloworld/).
## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
-
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
+ - [Community chat](https://gitter.im/thingsboard/chat)
+ - [Q&A forum](https://groups.google.com/forum/#!forum/thingsboard)
+ - [Stackoverflow](http://stackoverflow.com/questions/tagged/thingsboard)
-## License
-For open source projects, say how it is licensed.
+## Licenses
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+This project is released under [Apache 2.0 License](./LICENSE).
diff --git a/application/.gitignore b/application/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b5246c6bdded176d05a8bacd058e03c5b1bbbdbe
--- /dev/null
+++ b/application/.gitignore
@@ -0,0 +1,2 @@
+!bin/
+/bin/
diff --git a/application/pom.xml b/application/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..597f878174e8ed2b90ffcd724de683e0216da8d6
--- /dev/null
+++ b/application/pom.xml
@@ -0,0 +1,451 @@
+
+
+ 4.0.0
+
+ org.thingsboard
+ 3.4.3
+ thingsboard
+
+ application
+ jar
+
+ ThingsBoard Server Application
+ https://thingsboard.io
+ Open-source IoT Platform - Device management, data collection, processing and visualization
+
+
+
+ UTF-8
+ ${basedir}/..
+ java
+ false
+ process-resources
+ package
+ thingsboard
+ ${project.build.directory}/windows
+ true
+ ThingsBoard
+ org.thingsboard.server.ThingsboardServerApplication
+
+
+
+
+ io.netty
+ netty-transport-native-epoll
+ ${netty.version}
+
+ linux-x86_64
+
+
+ org.thingsboard.common
+ actor
+
+
+ org.thingsboard.common
+ util
+
+
+ org.thingsboard.rule-engine
+ rule-engine-api
+
+
+ org.thingsboard.common
+ cluster-api
+
+
+ org.thingsboard.common
+ version-control
+
+
+ org.thingsboard.rule-engine
+ rule-engine-components
+
+
+ org.thingsboard.common.transport
+ transport-api
+
+
+ org.thingsboard.common.transport
+ mqtt
+
+
+ org.thingsboard.common.transport
+ http
+
+
+ org.thingsboard.common.transport
+ coap
+
+
+ org.thingsboard.common.transport
+ lwm2m
+
+
+ org.thingsboard.common.transport
+ snmp
+
+
+ org.thingsboard
+ dao
+
+
+ org.thingsboard.common
+ queue
+
+
+ org.thingsboard.common.script
+ script-api
+
+
+ org.thingsboard.common.script
+ remote-js-client
+
+
+ org.thingsboard.common
+ stats
+
+
+ org.thingsboard.common
+ edge-api
+
+
+ org.thingsboard
+ dao
+ test-jar
+ test
+
+
+ io.takari.junit
+ takari-cpsuite
+ test
+
+
+ org.eclipse.paho
+ org.eclipse.paho.client.mqttv3
+
+
+ org.cassandraunit
+ cassandra-unit
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+ org.hibernate
+ hibernate-validator
+
+
+ test
+
+
+ org.thingsboard
+ ui-ngx
+ ${project.version}
+ runtime
+
+
+ org.springframework.integration
+ spring-integration-redis
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+ org.springframework.security
+ spring-security-oauth2-client
+
+
+ org.springframework.security
+ spring-security-oauth2-jose
+
+
+ io.jsonwebtoken
+ jjwt
+
+
+ org.freemarker
+ freemarker
+
+
+ commons-io
+ commons-io
+
+
+ org.apache.commons
+ commons-csv
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ com.sun.mail
+ javax.mail
+
+
+ com.twilio.sdk
+ twilio
+
+
+ com.amazonaws
+ aws-java-sdk-sns
+
+
+ org.apache.curator
+ curator-recipes
+
+
+ com.google.protobuf
+ protobuf-java
+
+
+ io.netty
+ netty-all
+
+
+ io.netty
+ netty-tcnative-boringssl-static
+
+
+ io.grpc
+ grpc-netty-shaded
+
+
+ io.grpc
+ grpc-protobuf
+
+
+ io.grpc
+ grpc-stub
+
+
+ org.opensmpp
+ opensmpp-core
+
+
+ org.thingsboard
+ springfox-boot-starter
+
+
+ com.sun.winsw
+ winsw
+ bin
+ exe
+ provided
+
+
+ org.thingsboard
+ tools
+ test
+
+
+ org.thingsboard
+ rest-client
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+ com.jayway.jsonpath
+ json-path
+
+
+ com.jayway.jsonpath
+ json-path-assert
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.dbunit
+ dbunit
+ test
+
+
+ com.github.springtestdbunit
+ spring-test-dbunit
+ test
+
+
+ org.testcontainers
+ postgresql
+ test
+
+
+ org.testcontainers
+ jdbc
+ test
+
+
+ org.javadelight
+ delight-nashorn-sandbox
+
+
+ org.passay
+ passay
+
+
+ com.github.ua-parser
+ uap-java
+
+
+ org.java-websocket
+ Java-WebSocket
+ test
+
+
+ org.jboss.aerogear
+ aerogear-otp-java
+
+
+
+
+ ${pkg.name}-${project.version}
+
+
+ ${project.basedir}/src/main/resources
+ true
+
+ thingsboard.yml
+
+
+
+ ${project.basedir}/src/main/resources
+ false
+
+ thingsboard.yml
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ thingsboard
+
+
+ **/nosql/*Test.java
+
+
+ **/*Test.java
+ **/*TestSuite.java
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.places.Main
+
+
+
+ org.thingsboard
+ gradle-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ https://repo.jenkins-ci.org/releases
+
+ false
+
+
+
+
diff --git a/application/src/main/conf/logback.xml b/application/src/main/conf/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..284af719539a078ea99a7c10f6e01839f3a044ec
--- /dev/null
+++ b/application/src/main/conf/logback.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ ${pkg.logFolder}/${pkg.name}.log
+
+ ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/src/main/conf/thingsboard.conf b/application/src/main/conf/thingsboard.conf
new file mode 100644
index 0000000000000000000000000000000000000000..0ac9eb11adb4eee263410d3857171650538150d9
--- /dev/null
+++ b/application/src/main/conf/thingsboard.conf
@@ -0,0 +1,24 @@
+#
+# Copyright © 2016-2022 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@/data"
+export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=@pkg.logFolder@/gc.log:time,uptime,level,tags:filecount=10,filesize=10M"
+export JAVA_OPTS="$JAVA_OPTS -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError"
+export JAVA_OPTS="$JAVA_OPTS -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
+export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql
diff --git a/application/src/main/data/certs/azure/BaltimoreCyberTrustRoot.crt.pem b/application/src/main/data/certs/azure/BaltimoreCyberTrustRoot.crt.pem
new file mode 100644
index 0000000000000000000000000000000000000000..2bd16ebd4768e10704debe29ce3c117ce54639c3
--- /dev/null
+++ b/application/src/main/data/certs/azure/BaltimoreCyberTrustRoot.crt.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
+DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
+ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
+VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
+mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
+IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
+mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
+XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
+dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
+jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
+BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
+DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
+9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
+Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
+ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
+R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+
diff --git a/application/src/main/data/json/demo/dashboards/firmware.json b/application/src/main/data/json/demo/dashboards/firmware.json
new file mode 100644
index 0000000000000000000000000000000000000000..779f2cb93cdda64ee25a6aabe38ba3d24d9e7381
--- /dev/null
+++ b/application/src/main/data/json/demo/dashboards/firmware.json
@@ -0,0 +1,2492 @@
+{
+ "title": "Firmware",
+ "image": null,
+ "configuration": {
+ "description": "",
+ "widgets": {
+ "cd03188e-cd9d-9601-fd57-da4cb95fc016": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": false,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "8fdb88d0-50ac-2232-fdb7-69c30c16544e",
+ "dataKeys": [
+ {
+ "name": "current_fw_title",
+ "type": "timeseries",
+ "label": "Current FW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_fw_version",
+ "type": "timeseries",
+ "label": "Current FW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_title",
+ "type": "timeseries",
+ "label": "Target FW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_version",
+ "type": "timeseries",
+ "label": "Target FW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_ts",
+ "type": "timeseries",
+ "label": "Target FW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_checksum",
+ "type": "attribute",
+ "label": "fw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_url",
+ "type": "attribute",
+ "label": "fw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.4204673738685043,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History firmware update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_firmware_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit firmware",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "
",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download firmware",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016"
+ },
+ "100b756c-0082-6505-3ae1-3603e6deea48": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "timeseries_table",
+ "type": "timeseries",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 8,
+ "sizeY": 6.5,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "19f41c21-d9af-e666-8f50-e1748778f955",
+ "filterId": null,
+ "dataKeys": [
+ {
+ "name": "current_fw_title",
+ "type": "timeseries",
+ "label": "Current firmware title",
+ "color": "#2196f3",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.5978079905579401,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_fw_version",
+ "type": "timeseries",
+ "label": "Current firmware version",
+ "color": "#4caf50",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.027392025058568192,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_title",
+ "type": "timeseries",
+ "label": "Target firmware title",
+ "color": "#f44336",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.9496350796287059,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_version",
+ "type": "timeseries",
+ "label": "Target firmware version",
+ "color": "#ffc107",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.6734152252264187,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#607d8b",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.2983399718643074,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": true,
+ "postFuncBody": "function capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\nif (value !== '') {\n return capitalize(value);\n}\nreturn value;"
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
+ "realtime": {
+ "realtimeType": 0,
+ "timewindowMs": 2592000000,
+ "quickInterval": "CURRENT_DAY",
+ "interval": 1000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "showTimestamp": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "enableSearch": true,
+ "enableStickyHeader": true,
+ "enableStickyAction": true
+ },
+ "title": "Firmware history",
+ "dropShadow": false,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {},
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "displayTimewindow": true,
+ "titleTooltip": ""
+ },
+ "row": 0,
+ "col": 0,
+ "id": "100b756c-0082-6505-3ae1-3603e6deea48"
+ },
+ "17543c57-af4a-2c1e-bf12-53a7b46791e6": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "waitingDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n

\n
\n ${waitingDevicesNumber:0}\n
\n
\n Device Waiting\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_waiting",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "4d9a77a2-f0a5-690c-a83b-b0e940be788c"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6"
+ },
+ "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "updatingDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n

\n
\n ${updatingDevicesNumber:0}\n
\n
\n Device Updating\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_updating",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "57d39904-2350-b29b-78ed-56b8268814cb"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6"
+ },
+ "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "6044e198-df64-cd76-f339-696f220c4943",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "updatedDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n

\n
\n ${updatedDevicesNumber:0}\n
\n
\n Device Updated\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_updated",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "d787c212-8c56-34f0-349a-5aae2ffd1eae"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81"
+ },
+ "77b10144-b904-edd5-8c7c-8fb75616c6d8": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "updatingDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n
\n
\n ${updatingDevicesNumber:0}\n
\n \n
\n Device Failed\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .container-svg {\n height: 40px;\n width: 40px;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .container-svg {\n height: 28px;\n width: 28px;\n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_error",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "0b3d2887-9929-84d5-3795-0763dca15cba"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8"
+ },
+ "21be08bb-ec90-f760-ad6f-e7678f12c401": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e",
+ "dataKeys": [
+ {
+ "name": "current_fw_title",
+ "type": "timeseries",
+ "label": "Current FW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_fw_version",
+ "type": "timeseries",
+ "label": "Current FW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_title",
+ "type": "timeseries",
+ "label": "Target FW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_version",
+ "type": "timeseries",
+ "label": "Target FW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_ts",
+ "type": "timeseries",
+ "label": "Target FW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_checksum",
+ "type": "attribute",
+ "label": "fw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_url",
+ "type": "attribute",
+ "label": "fw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.4204673738685043,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History firmware update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_firmware_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit firmware",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download firmware",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "21be08bb-ec90-f760-ad6f-e7678f12c401"
+ },
+ "e8280043-d3dc-7acb-c2ff-a4522972ff91": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897",
+ "dataKeys": [
+ {
+ "name": "current_fw_title",
+ "type": "timeseries",
+ "label": "Current FW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_fw_version",
+ "type": "timeseries",
+ "label": "Current FW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_title",
+ "type": "timeseries",
+ "label": "Target FW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_version",
+ "type": "timeseries",
+ "label": "Target FW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_ts",
+ "type": "timeseries",
+ "label": "Target FW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_checksum",
+ "type": "attribute",
+ "label": "fw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_url",
+ "type": "attribute",
+ "label": "fw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.4204673738685043,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History firmware update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_firmware_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit firmware",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download firmware",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91"
+ },
+ "3624013b-378c-f110-5eba-ae95c25a4dcc": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f",
+ "dataKeys": [
+ {
+ "name": "current_fw_title",
+ "type": "timeseries",
+ "label": "Current FW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_fw_version",
+ "type": "timeseries",
+ "label": "Current FW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_title",
+ "type": "timeseries",
+ "label": "Target FW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_version",
+ "type": "timeseries",
+ "label": "Target FW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_ts",
+ "type": "timeseries",
+ "label": "Target FW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_checksum",
+ "type": "attribute",
+ "label": "fw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_url",
+ "type": "attribute",
+ "label": "fw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.4204673738685043,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History firmware update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_firmware_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit firmware",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download firmware",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "3624013b-378c-f110-5eba-ae95c25a4dcc"
+ },
+ "d2d13e0d-4e71-889f-9343-ad2f0af9f176": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "6044e198-df64-cd76-f339-696f220c4943",
+ "dataKeys": [
+ {
+ "name": "current_fw_title",
+ "type": "timeseries",
+ "label": "Current FW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_fw_version",
+ "type": "timeseries",
+ "label": "Current FW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_title",
+ "type": "timeseries",
+ "label": "Target FW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_version",
+ "type": "timeseries",
+ "label": "Target FW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_fw_ts",
+ "type": "timeseries",
+ "label": "Target FW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_checksum",
+ "type": "attribute",
+ "label": "fw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "fw_url",
+ "type": "attribute",
+ "label": "fw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.4204673738685043,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History firmware update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_firmware_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit firmware",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download firmware",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176"
+ }
+ },
+ "states": {
+ "default": {
+ "name": "Device list",
+ "root": true,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "cd03188e-cd9d-9601-fd57-da4cb95fc016": {
+ "sizeX": 19,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ },
+ "17543c57-af4a-2c1e-bf12-53a7b46791e6": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 0,
+ "col": 19
+ },
+ "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 3,
+ "col": 19
+ },
+ "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 9,
+ "col": 19
+ },
+ "77b10144-b904-edd5-8c7c-8fb75616c6d8": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 6,
+ "col": 19
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 12,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": true,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_firmware_history": {
+ "name": "Firmware history: ${entityName}",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "100b756c-0082-6505-3ae1-3603e6deea48": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_waiting": {
+ "name": "Device waiting",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "21be08bb-ec90-f760-ad6f-e7678f12c401": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_updating": {
+ "name": "Device updating",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "e8280043-d3dc-7acb-c2ff-a4522972ff91": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_updated": {
+ "name": "Device updated",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "d2d13e0d-4e71-889f-9343-ad2f0af9f176": {
+ "sizeX": 27,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_error": {
+ "name": "Device failed",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "3624013b-378c-f110-5eba-ae95c25a4dcc": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ }
+ },
+ "entityAliases": {
+ "639da5b4-31f0-0151-6282-c37a3897b7e8": {
+ "id": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "alias": "All devices",
+ "filter": {
+ "type": "entityType",
+ "resolveMultiple": true,
+ "entityType": "DEVICE"
+ }
+ },
+ "19f41c21-d9af-e666-8f50-e1748778f955": {
+ "id": "19f41c21-d9af-e666-8f50-e1748778f955",
+ "alias": "State entity",
+ "filter": {
+ "type": "stateEntity",
+ "resolveMultiple": false,
+ "stateEntityParamName": null,
+ "defaultStateEntity": null
+ }
+ }
+ },
+ "filters": {
+ "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e": {
+ "id": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e",
+ "filter": "WaitingDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "fw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "QUEUED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "579f0468-9ce9-7e3e-b34c-88dd3de59897": {
+ "id": "579f0468-9ce9-7e3e-b34c-88dd3de59897",
+ "filter": "UpdatingDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "fw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "OR",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "INITIATED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "fw_state equel",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "DOWNLOADING",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "fw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "DOWNLOADED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "fw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "VERIFIED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "fw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "UPDATING",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "fw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ],
+ "type": "COMPLEX"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "6044e198-df64-cd76-f339-696f220c4943": {
+ "id": "6044e198-df64-cd76-f339-696f220c4943",
+ "filter": "UpdetedDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "fw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "UPDATED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "bdbc6ea1-95a7-3912-341a-58dc7704a00f": {
+ "id": "bdbc6ea1-95a7-3912-341a-58dc7704a00f",
+ "filter": "FailedDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "fw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "FAILED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "8fdb88d0-50ac-2232-fdb7-69c30c16544e": {
+ "id": "8fdb88d0-50ac-2232-fdb7-69c30c16544e",
+ "filter": "DeviceSearch",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "ENTITY_FIELD",
+ "key": "name"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "CONTAINS",
+ "value": {
+ "defaultValue": ""
+ },
+ "ignoreCase": true,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "Device name",
+ "autogeneratedLabel": false,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": true
+ }
+ },
+ "timewindow": {
+ "displayValue": "",
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
+ "realtime": {
+ "realtimeType": 0,
+ "interval": 1000,
+ "timewindowMs": 60000,
+ "quickInterval": "CURRENT_DAY"
+ },
+ "history": {
+ "historyType": 0,
+ "interval": 1000,
+ "timewindowMs": 60000,
+ "fixedTimewindow": {
+ "startTimeMs": 1618998609030,
+ "endTimeMs": 1619085009030
+ },
+ "quickInterval": "CURRENT_DAY"
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ }
+ },
+ "settings": {
+ "stateControllerId": "entity",
+ "showTitle": false,
+ "showDashboardsSelect": false,
+ "showEntitiesSelect": false,
+ "showDashboardTimewindow": true,
+ "showDashboardExport": false,
+ "toolbarAlwaysOpen": true,
+ "titleColor": "rgba(0,0,0,0.870588)",
+ "showFilters": true,
+ "showDashboardLogo": false,
+ "dashboardLogoUrl": null,
+ "showUpdateDashboardImage": false
+ }
+ },
+ "name": "Firmware"
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/dashboards/gateways.json b/application/src/main/data/json/demo/dashboards/gateways.json
new file mode 100644
index 0000000000000000000000000000000000000000..0a18b10a52136ae98563340b8ae361f7dbf9bece
--- /dev/null
+++ b/application/src/main/data/json/demo/dashboards/gateways.json
@@ -0,0 +1,1267 @@
+{
+ "title": "Gateways",
+ "configuration": {
+ "widgets": {
+ "94715984-ae74-76e4-20b7-2f956b01ed80": {
+ "isSystemType": true,
+ "bundleAlias": "entity_admin_widgets",
+ "typeAlias": "device_admin_table",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 24,
+ "sizeY": 12,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "entitiesTitle": "List of gateways",
+ "enableSelectColumnDisplay": true,
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Gateway Name"
+ },
+ "title": "Devices gateway table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "active",
+ "type": "attribute",
+ "label": "Active",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "cellContentFunction": "value = '⬤';\nreturn value;",
+ "cellStyleFunction": "var color;\nif (value == 'false') {\n color = '#EB5757';\n} else {\n color = '#27AE60';\n}\nreturn {\n color: color,\n fontSize: '18px'\n};"
+ },
+ "_hash": 0.3646047595211721
+ },
+ {
+ "name": "eventsSent",
+ "type": "timeseries",
+ "label": "Sent",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.7235710720767985
+ },
+ {
+ "name": "eventsProduced",
+ "type": "timeseries",
+ "label": "Events",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.5085933386303254
+ },
+ {
+ "name": "LOGS",
+ "type": "timeseries",
+ "label": "Latest log",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.3504240371585048,
+ "postFuncBody": "if(value) {\n return value.substring(0, 31) + \"...\";\n} else {\n return '';\n}"
+ },
+ {
+ "name": "RemoteLoggingLevel",
+ "type": "attribute",
+ "label": "Log level",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.9785994222542516
+ }
+ ],
+ "entityAliasId": "3e0f533a-0db1-3292-184f-06e73535061a"
+ }
+ ],
+ "showTitleIcon": true,
+ "titleIcon": "list",
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "List device",
+ "widgetStyle": {},
+ "displayTimewindow": true,
+ "actions": {
+ "headerButton": [
+ {
+ "name": "Add device",
+ "icon": "add",
+ "type": "customPretty",
+ "customHtml": "\n",
+ "customCss": "",
+ "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\nopenAddDeviceDialog();\n\nfunction openAddDeviceDialog() {\n customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\n}\n\nfunction AddDeviceDialogController(instance) {\n let vm = instance;\n \n vm.addDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.addDeviceFormGroup.markAsPristine();\n let device = {\n additionalInfo: {gateway: true},\n name: vm.addDeviceFormGroup.get('deviceName').value,\n type: 'gateway',\n label: vm.addDeviceFormGroup.get('deviceLabel').value\n };\n deviceService.saveDevice(device).subscribe(\n function (device) {\n saveAttributes(device.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n function saveAttributes(entityId) {\n let attributes = vm.addDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n",
+ "customResources": [],
+ "id": "70837a9d-c3de-a9a7-03c5-dccd14998758"
+ }
+ ],
+ "actionCellButton": [
+ {
+ "id": "78845501-234e-a452-6819-82b5b776e99f",
+ "name": "Configuration",
+ "icon": "settings",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "__entityname__config",
+ "openRightLayout": false,
+ "setEntityId": true
+ },
+ {
+ "id": "f6ffdba8-e40f-2b8d-851b-f5ecaf18606b",
+ "name": "Graphs",
+ "icon": "show_chart",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "__entityname_grafic",
+ "setEntityId": true
+ },
+ {
+ "name": "Edit device",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "\n",
+ "customCss": "/*=======================================================================*/\n/*========== There are two examples: for edit and add entity ==========*/\n/*=======================================================================*/\n/*======================== Edit entity example ========================*/\n/*=======================================================================*/\n/*\n.edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n\n.relations-list.old-relations tb-entity-select tb-entity-autocomplete button {\n display: none;\n} \n*/\n/*========================================================================*/\n/*========================= Add entity example =========================*/\n/*========================================================================*/\n/*\n.add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n*/\n",
+ "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\nopenEditDeviceDialog();\n\nfunction openEditDeviceDialog() {\n customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\n}\n\nfunction EditDeviceDialogController(instance) {\n let vm = instance;\n \n vm.device = null;\n vm.attributes = {};\n \n vm.editDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.editDeviceFormGroup.markAsPristine();\n vm.device.name = vm.editDeviceFormGroup.get('deviceName').value;\n vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value;\n deviceService.saveDevice(vm.device).subscribe(\n function () {\n saveAttributes().subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n getEntityInfo();\n \n function getEntityInfo() {\n deviceService.getDevice(entityId.id).subscribe(\n function (device) {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\n ['latitude', 'longitude']).subscribe(\n function (attributes) {\n for (let i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n vm.device = device;\n vm.editDeviceFormGroup.patchValue(\n {\n deviceName: vm.device.name,\n deviceLabel: vm.device.label,\n attributes: {\n latitude: vm.attributes.latitude,\n longitude: vm.attributes.longitude\n }\n }, {emitEvent: false}\n );\n } \n );\n }\n ); \n }\n \n function saveAttributes() {\n let attributes = vm.editDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n",
+ "customResources": [],
+ "id": "242671f3-76c6-6982-7acc-6f12addf0ccc"
+ },
+ {
+ "name": "Delete device",
+ "icon": "delete",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenDeleteDeviceDialog();\n\nfunction openDeleteDeviceDialog() {\n let title = \"Are you sure you want to delete the device \" + entityName + \"?\";\n let content = \"Be careful, after the confirmation, the device and all related data will become unrecoverable!\";\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\n function (result) {\n if (result) {\n deleteDevice();\n }\n }\n );\n}\n\nfunction deleteDevice() {\n deviceService.deleteDevice(entityId.id).subscribe(\n function () {\n widgetContext.updateAliases();\n }\n );\n}\n",
+ "id": "862ec2b7-fbcf-376e-f85f-b77c07f36efa"
+ }
+ ],
+ "rowClick": [
+ {
+ "id": "ad5fc7e1-5e60-e056-6940-a75a383466a1",
+ "name": "to_entityname__config",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "__entityname__config",
+ "setEntityId": true,
+ "stateEntityParamName": ""
+ }
+ ]
+ }
+ },
+ "id": "94715984-ae74-76e4-20b7-2f956b01ed80"
+ },
+ "eadabbc7-519e-76fc-ba10-b3fe8c18da10": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "timeseries_table",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 14,
+ "sizeY": 13,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "LOGS",
+ "type": "timeseries",
+ "label": "LOGS",
+ "color": "#2196f3",
+ "settings": {
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.3496649158709739,
+ "postFuncBody": "return value.replace(/ - (.*) - \\[/gi, ' - $1 - [');"
+ }
+ ],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 2592000000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "showTimestamp": true,
+ "displayPagination": true,
+ "defaultPageSize": 10
+ },
+ "title": "Debug events (logs)",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {},
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true
+ },
+ "id": "eadabbc7-519e-76fc-ba10-b3fe8c18da10"
+ },
+ "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": {
+ "isSystemType": true,
+ "bundleAlias": "charts",
+ "typeAlias": "basic_timeseries",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 17,
+ "sizeY": 4,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "opcuaEventsProduced",
+ "type": "timeseries",
+ "label": "opcuaEventsProduced",
+ "color": "#2196f3",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "tooltipValueFormatter": "",
+ "showSeparateAxis": false,
+ "axisTitle": "",
+ "axisPosition": "left",
+ "axisTicksFormatter": "",
+ "comparisonSettings": {
+ "showValuesForComparison": true,
+ "comparisonValuesLabel": "",
+ "color": ""
+ }
+ },
+ "_hash": 0.1477920581839779
+ },
+ {
+ "name": "opcuaEventsSent",
+ "type": "timeseries",
+ "label": "opcuaEventsSent",
+ "color": "#4caf50",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "tooltipValueFormatter": "",
+ "showSeparateAxis": false,
+ "axisTitle": "",
+ "axisPosition": "left",
+ "axisTicksFormatter": "",
+ "comparisonSettings": {
+ "showValuesForComparison": true,
+ "comparisonValuesLabel": "",
+ "color": ""
+ }
+ },
+ "_hash": 0.6500957113784758
+ }
+ ],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 120000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 25000
+ },
+ "hideInterval": false,
+ "hideAggregation": false
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "shadowSize": 4,
+ "fontColor": "#545454",
+ "fontSize": 10,
+ "xaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "yaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "grid": {
+ "color": "#545454",
+ "tickColor": "#DDDDDD",
+ "verticalLines": true,
+ "horizontalLines": true,
+ "outlineWidth": 1
+ },
+ "stack": false,
+ "tooltipIndividual": false,
+ "timeForComparison": "months",
+ "xaxisSecond": {
+ "axisPosition": "top",
+ "showLabels": true
+ }
+ },
+ "title": "Real time information",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "mobileHeight": null,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "showLegend": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "right",
+ "showMin": true,
+ "showMax": true,
+ "showAvg": true,
+ "showTotal": true
+ },
+ "actions": {}
+ },
+ "id": "f928afc4-30d1-8d0c-e3cf-777f9f9d1155"
+ },
+ "2a95b473-042d-59d0-2da2-40d0cccb6c8a": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "timeseries_table",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 7,
+ "sizeY": 7,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "eventsSent",
+ "type": "timeseries",
+ "label": "Events",
+ "color": "#2196f3",
+ "settings": {
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.8156044798125357
+ },
+ {
+ "name": "eventsProduced",
+ "type": "timeseries",
+ "label": "Produced",
+ "color": "#4caf50",
+ "settings": {
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.6538259344015449
+ }
+ ],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 604800000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "showTimestamp": true,
+ "displayPagination": true,
+ "defaultPageSize": 6,
+ "hideEmptyLines": true
+ },
+ "title": "Total Messages",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {},
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "showMin": false,
+ "showMax": false,
+ "showAvg": true,
+ "showTotal": false
+ }
+ },
+ "id": "2a95b473-042d-59d0-2da2-40d0cccb6c8a"
+ },
+ "aaa69366-aacc-9028-65aa-645c0f8533ec": {
+ "isSystemType": true,
+ "bundleAlias": "charts",
+ "typeAlias": "basic_timeseries",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 17,
+ "sizeY": 4,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "eventsSent",
+ "type": "timeseries",
+ "label": "eventsSent",
+ "color": "#2196f3",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "tooltipValueFormatter": "",
+ "showSeparateAxis": false,
+ "axisTitle": "",
+ "axisPosition": "left",
+ "axisTicksFormatter": "",
+ "comparisonSettings": {
+ "showValuesForComparison": true,
+ "comparisonValuesLabel": "",
+ "color": ""
+ }
+ },
+ "_hash": 0.41414001784591314
+ },
+ {
+ "name": "eventsProduced",
+ "type": "timeseries",
+ "label": "eventsProduced",
+ "color": "#4caf50",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "tooltipValueFormatter": "",
+ "showSeparateAxis": false,
+ "axisTitle": "",
+ "axisPosition": "left",
+ "axisTicksFormatter": "",
+ "comparisonSettings": {
+ "showValuesForComparison": true,
+ "comparisonValuesLabel": "",
+ "color": ""
+ }
+ },
+ "_hash": 0.7819101846284422
+ }
+ ],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "shadowSize": 4,
+ "fontColor": "#545454",
+ "fontSize": 10,
+ "xaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "yaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "grid": {
+ "color": "#545454",
+ "tickColor": "#DDDDDD",
+ "verticalLines": true,
+ "horizontalLines": true,
+ "outlineWidth": 1
+ },
+ "stack": false,
+ "tooltipIndividual": false,
+ "timeForComparison": "months",
+ "xaxisSecond": {
+ "axisPosition": "top",
+ "showLabels": true
+ }
+ },
+ "title": "History information",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "mobileHeight": null,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "useDashboardTimewindow": true,
+ "displayTimewindow": true,
+ "showLegend": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "right",
+ "showMin": true,
+ "showMax": true,
+ "showAvg": true,
+ "showTotal": true
+ },
+ "actions": {}
+ },
+ "id": "aaa69366-aacc-9028-65aa-645c0f8533ec"
+ },
+ "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": {
+ "isSystemType": true,
+ "bundleAlias": "charts",
+ "typeAlias": "basic_timeseries",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 17,
+ "sizeY": 4,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "bleEventsProduced",
+ "type": "timeseries",
+ "label": "bleEventsProduced",
+ "color": "#2196f3",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "tooltipValueFormatter": "",
+ "showSeparateAxis": false,
+ "axisTitle": "",
+ "axisPosition": "left",
+ "axisTicksFormatter": "",
+ "comparisonSettings": {
+ "showValuesForComparison": true,
+ "comparisonValuesLabel": "",
+ "color": ""
+ }
+ },
+ "_hash": 0.5625165504526104
+ },
+ {
+ "name": "bleEventsSent",
+ "type": "timeseries",
+ "label": "bleEventsSent",
+ "color": "#4caf50",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "tooltipValueFormatter": "",
+ "showSeparateAxis": false,
+ "axisTitle": "",
+ "axisPosition": "left",
+ "axisTicksFormatter": "",
+ "comparisonSettings": {
+ "showValuesForComparison": true,
+ "comparisonValuesLabel": "",
+ "color": ""
+ }
+ },
+ "_hash": 0.6817950080745288
+ }
+ ],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 5000,
+ "timewindowMs": 120000
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "shadowSize": 4,
+ "fontColor": "#545454",
+ "fontSize": 10,
+ "xaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "yaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "grid": {
+ "color": "#545454",
+ "tickColor": "#DDDDDD",
+ "verticalLines": true,
+ "horizontalLines": true,
+ "outlineWidth": 1
+ },
+ "stack": false,
+ "tooltipIndividual": false,
+ "timeForComparison": "months",
+ "xaxisSecond": {
+ "axisPosition": "top",
+ "showLabels": true
+ }
+ },
+ "title": "Real time information",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "mobileHeight": null,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "showLegend": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "right",
+ "showMin": true,
+ "showMax": true,
+ "showAvg": true,
+ "showTotal": true
+ },
+ "actions": {}
+ },
+ "id": "ce5c7d01-a3ef-5cf0-4578-8505135c23a0"
+ },
+ "466f046d-6005-a168-b107-60fcb2469cd5": {
+ "isSystemType": true,
+ "bundleAlias": "gateway_widgets",
+ "typeAlias": "attributes_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 7,
+ "sizeY": 5,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "eventsTitle": "Gateway Events Form",
+ "eventsReg": [
+ "EventsProduced",
+ "EventsSent"
+ ]
+ },
+ "title": "Gateway events",
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "displayTimewindow": true,
+ "showLegend": false,
+ "actions": {}
+ },
+ "id": "466f046d-6005-a168-b107-60fcb2469cd5"
+ },
+ "8fc32225-164f-3258-73f7-e6b6d959cf0b": {
+ "isSystemType": true,
+ "bundleAlias": "gateway_widgets",
+ "typeAlias": "config_form_latest",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 10,
+ "sizeY": 9,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "gatewayTitle": "Gateway configuration (Single device)",
+ "readOnly": false
+ },
+ "title": "New Gateway configuration (Single device)",
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "displayTimewindow": true,
+ "showLegend": false,
+ "actions": {}
+ },
+ "id": "8fc32225-164f-3258-73f7-e6b6d959cf0b"
+ },
+ "063fc179-c9fd-f952-e714-f24e9c43c05c": {
+ "isSystemType": true,
+ "bundleAlias": "control_widgets",
+ "typeAlias": "rpcbutton",
+ "type": "rpc",
+ "title": "New widget",
+ "sizeX": 4,
+ "sizeY": 2,
+ "config": {
+ "targetDeviceAliases": [],
+ "showTitle": false,
+ "backgroundColor": "#e6e7e8",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "requestTimeout": 5000,
+ "oneWayElseTwoWay": true,
+ "styleButton": {
+ "isRaised": true,
+ "isPrimary": false
+ },
+ "methodParams": "{}",
+ "methodName": "gateway_reboot",
+ "buttonText": "GATEWAY REBOOT"
+ },
+ "title": "New RPC Button",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {},
+ "datasources": [],
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true,
+ "targetDeviceAliasIds": [
+ "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ ]
+ },
+ "id": "063fc179-c9fd-f952-e714-f24e9c43c05c"
+ },
+ "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": {
+ "isSystemType": true,
+ "bundleAlias": "control_widgets",
+ "typeAlias": "rpcbutton",
+ "type": "rpc",
+ "title": "New widget",
+ "sizeX": 4,
+ "sizeY": 2,
+ "config": {
+ "targetDeviceAliases": [],
+ "showTitle": false,
+ "backgroundColor": "#e6e7e8",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "requestTimeout": 5000,
+ "oneWayElseTwoWay": true,
+ "styleButton": {
+ "isRaised": true,
+ "isPrimary": false
+ },
+ "methodName": "gateway_restart",
+ "methodParams": "{}",
+ "buttonText": "GATEWAY RESTART"
+ },
+ "title": "New RPC Button",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {},
+ "datasources": [],
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true,
+ "targetDeviceAliasIds": [
+ "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ ]
+ },
+ "id": "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7"
+ },
+ "6770b6ba-eff8-df05-75f8-c1f9326d4842": {
+ "isSystemType": true,
+ "bundleAlias": "input_widgets",
+ "typeAlias": "markers_placement_openstreetmap",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 6,
+ "sizeY": 4,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "latitude",
+ "type": "attribute",
+ "label": "latitude",
+ "color": "#2196f3",
+ "settings": {},
+ "_hash": 0.9743324774725604
+ },
+ {
+ "name": "longitude",
+ "type": "attribute",
+ "label": "longitude",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.5530093635101525
+ }
+ ],
+ "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "fitMapBounds": true,
+ "latKeyName": "latitude",
+ "lngKeyName": "longitude",
+ "showLabel": true,
+ "label": "${entityName}",
+ "tooltipPattern": "${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Delete",
+ "markerImageSize": 34,
+ "useColorFunction": false,
+ "markerImages": [],
+ "useMarkerImageFunction": false,
+ "color": "#fe7569",
+ "mapProvider": "OpenStreetMap.Mapnik",
+ "showTooltip": true,
+ "autocloseTooltip": true,
+ "defaultCenterPosition": [
+ 0,
+ 0
+ ],
+ "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ "showTooltipAction": "click",
+ "polygonKeyName": "coordinates",
+ "polygonOpacity": 0.5,
+ "polygonStrokeOpacity": 1,
+ "polygonStrokeWeight": 1,
+ "zoomOnClick": true,
+ "showCoverageOnHover": true,
+ "animate": true,
+ "maxClusterRadius": 80,
+ "removeOutsideVisibleBounds": true,
+ "defaultZoomLevel": 5
+ },
+ "title": "Gateway Location",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {
+ "tooltipAction": [
+ {
+ "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66",
+ "name": "delete",
+ "icon": "more_horiz",
+ "type": "custom",
+ "customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true
+ },
+ "id": "6770b6ba-eff8-df05-75f8-c1f9326d4842"
+ }
+ },
+ "states": {
+ "main_gateway": {
+ "name": "Gateways",
+ "root": true,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "94715984-ae74-76e4-20b7-2f956b01ed80": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70,
+ "margin": 10
+ }
+ }
+ }
+ },
+ "__entityname__config": {
+ "name": "${entityName} Configuration",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "eadabbc7-519e-76fc-ba10-b3fe8c18da10": {
+ "sizeX": 14,
+ "sizeY": 13,
+ "row": 0,
+ "col": 10
+ },
+ "8fc32225-164f-3258-73f7-e6b6d959cf0b": {
+ "sizeX": 10,
+ "sizeY": 9,
+ "row": 0,
+ "col": 0
+ },
+ "063fc179-c9fd-f952-e714-f24e9c43c05c": {
+ "sizeX": 4,
+ "sizeY": 2,
+ "row": 9,
+ "col": 0
+ },
+ "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": {
+ "sizeX": 4,
+ "sizeY": 2,
+ "row": 11,
+ "col": 0
+ },
+ "6770b6ba-eff8-df05-75f8-c1f9326d4842": {
+ "sizeX": 6,
+ "sizeY": 4,
+ "row": 9,
+ "col": 4
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70,
+ "margin": 10
+ }
+ }
+ }
+ },
+ "__entityname_grafic": {
+ "name": "${entityName} Details",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": {
+ "sizeX": 17,
+ "sizeY": 4,
+ "mobileHeight": null,
+ "row": 4,
+ "col": 7
+ },
+ "2a95b473-042d-59d0-2da2-40d0cccb6c8a": {
+ "sizeX": 7,
+ "sizeY": 7,
+ "row": 5,
+ "col": 0
+ },
+ "aaa69366-aacc-9028-65aa-645c0f8533ec": {
+ "sizeX": 17,
+ "sizeY": 4,
+ "mobileHeight": null,
+ "row": 0,
+ "col": 7
+ },
+ "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": {
+ "sizeX": 17,
+ "sizeY": 4,
+ "mobileHeight": null,
+ "row": 8,
+ "col": 7
+ },
+ "466f046d-6005-a168-b107-60fcb2469cd5": {
+ "sizeX": 7,
+ "sizeY": 5,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "backgroundSizeMode": "auto 100%",
+ "autoFillHeight": true,
+ "mobileAutoFillHeight": true,
+ "mobileRowHeight": 70,
+ "margin": 10
+ }
+ }
+ }
+ }
+ },
+ "entityAliases": {
+ "3e0f533a-0db1-3292-184f-06e73535061a": {
+ "id": "3e0f533a-0db1-3292-184f-06e73535061a",
+ "alias": "Gateways",
+ "filter": {
+ "type": "deviceType",
+ "resolveMultiple": true,
+ "deviceType": "gateway",
+ "deviceNameFilter": ""
+ }
+ },
+ "b2487e75-2fa4-f211-142c-434dfd50c70c": {
+ "id": "b2487e75-2fa4-f211-142c-434dfd50c70c",
+ "alias": "Current Gateway",
+ "filter": {
+ "type": "stateEntity",
+ "resolveMultiple": false,
+ "stateEntityParamName": "",
+ "defaultStateEntity": null
+ }
+ }
+ },
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 25000
+ },
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false
+ },
+ "settings": {
+ "stateControllerId": "entity",
+ "showTitle": true,
+ "showDashboardsSelect": true,
+ "showEntitiesSelect": true,
+ "showDashboardTimewindow": true,
+ "showDashboardExport": true,
+ "toolbarAlwaysOpen": true,
+ "titleColor": "rgba(0,0,0,0.870588)"
+ }
+ },
+ "name": "Gateways"
+}
diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
new file mode 100644
index 0000000000000000000000000000000000000000..b8231ab8802b2ecc4a4d90b81ee3381327e8d8ef
--- /dev/null
+++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
@@ -0,0 +1,520 @@
+{
+ "title": "Rule Engine Statistics",
+ "configuration": {
+ "widgets": {
+ "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {
+ "isSystemType": true,
+ "bundleAlias": "charts",
+ "typeAlias": "basic_timeseries",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 12,
+ "sizeY": 7,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "successfulMsgs",
+ "type": "timeseries",
+ "label": "${entityName} Successful",
+ "color": "#4caf50",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "showSeparateAxis": false,
+ "axisPosition": "left",
+ "thresholds": [
+ {
+ "thresholdValueSource": "predefinedValue"
+ }
+ ],
+ "comparisonSettings": {
+ "showValuesForComparison": true
+ }
+ },
+ "_hash": 0.15490750967648736
+ },
+ {
+ "name": "failedMsgs",
+ "type": "timeseries",
+ "label": "${entityName} Permanent Failures",
+ "color": "#ef5350",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "showSeparateAxis": false,
+ "axisPosition": "left",
+ "thresholds": [
+ {
+ "thresholdValueSource": "predefinedValue"
+ }
+ ],
+ "comparisonSettings": {
+ "showValuesForComparison": true
+ }
+ },
+ "_hash": 0.4186621166514697
+ },
+ {
+ "name": "tmpFailed",
+ "type": "timeseries",
+ "label": "${entityName} Processing Failures",
+ "color": "#ffc107",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "showSeparateAxis": false,
+ "axisPosition": "left",
+ "thresholds": [
+ {
+ "thresholdValueSource": "predefinedValue"
+ }
+ ],
+ "comparisonSettings": {
+ "showValuesForComparison": true
+ }
+ },
+ "_hash": 0.49891007198715376
+ }
+ ],
+ "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 300000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 8640
+ },
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "shadowSize": 4,
+ "fontColor": "#545454",
+ "fontSize": 10,
+ "xaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "yaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "grid": {
+ "color": "#545454",
+ "tickColor": "#DDDDDD",
+ "verticalLines": true,
+ "horizontalLines": true,
+ "outlineWidth": 1
+ },
+ "stack": false,
+ "tooltipIndividual": false,
+ "timeForComparison": "months",
+ "xaxisSecond": {
+ "axisPosition": "top",
+ "showLabels": true
+ }
+ },
+ "title": "Queue Stats",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "mobileHeight": null,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "showLegend": true,
+ "actions": {},
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "showMin": true,
+ "showMax": true,
+ "showAvg": false,
+ "showTotal": true
+ }
+ },
+ "id": "81987f19-3eac-e4ce-b790-d96e9b54d9a0"
+ },
+ "5eb79712-5c24-3060-7e4f-6af36b8f842d": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "timeseries_table",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 24,
+ "sizeY": 5,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "ruleEngineException",
+ "type": "timeseries",
+ "label": "Rule Chain",
+ "color": "#2196f3",
+ "settings": {
+ "useCellStyleFunction": false,
+ "useCellContentFunction": true,
+ "cellContentFunction": "return JSON.parse(value).ruleChainName;"
+ },
+ "_hash": 0.9954481282345906
+ },
+ {
+ "name": "ruleEngineException",
+ "type": "timeseries",
+ "label": "Rule Node",
+ "color": "#4caf50",
+ "settings": {
+ "useCellStyleFunction": false,
+ "useCellContentFunction": true,
+ "cellContentFunction": "return JSON.parse(value).ruleNodeName;"
+ },
+ "_hash": 0.18580357036589978
+ },
+ {
+ "name": "ruleEngineException",
+ "type": "timeseries",
+ "label": "Latest Error",
+ "color": "#f44336",
+ "settings": {
+ "useCellStyleFunction": false,
+ "useCellContentFunction": true,
+ "cellContentFunction": "return JSON.parse(value).message;"
+ },
+ "_hash": 0.7255162989552142
+ }
+ ],
+ "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "showTimestamp": true,
+ "displayPagination": true,
+ "defaultPageSize": 10
+ },
+ "title": "Exceptions",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {},
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true
+ },
+ "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d"
+ },
+ "ad3f1417-87a8-750e-fc67-49a2de1466d4": {
+ "isSystemType": true,
+ "bundleAlias": "charts",
+ "typeAlias": "basic_timeseries",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 12,
+ "sizeY": 7,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "timeoutMsgs",
+ "type": "timeseries",
+ "label": "${entityName} Permanent Timeouts",
+ "color": "#4caf50",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "showSeparateAxis": false,
+ "axisPosition": "left",
+ "thresholds": [
+ {
+ "thresholdValueSource": "predefinedValue"
+ }
+ ],
+ "comparisonSettings": {
+ "showValuesForComparison": true
+ }
+ },
+ "_hash": 0.565222981550328
+ },
+ {
+ "name": "tmpTimeout",
+ "type": "timeseries",
+ "label": "${entityName} Processing Timeouts",
+ "color": "#9c27b0",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": false,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "showSeparateAxis": false,
+ "axisPosition": "left",
+ "thresholds": [
+ {
+ "thresholdValueSource": "predefinedValue"
+ }
+ ],
+ "comparisonSettings": {
+ "showValuesForComparison": true
+ }
+ },
+ "_hash": 0.2679547062508352
+ }
+ ],
+ "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 300000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 8640
+ },
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "shadowSize": 4,
+ "fontColor": "#545454",
+ "fontSize": 10,
+ "xaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "yaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "grid": {
+ "color": "#545454",
+ "tickColor": "#DDDDDD",
+ "verticalLines": true,
+ "horizontalLines": true,
+ "outlineWidth": 1
+ },
+ "stack": false,
+ "tooltipIndividual": false,
+ "timeForComparison": "months",
+ "xaxisSecond": {
+ "axisPosition": "top",
+ "showLabels": true
+ }
+ },
+ "title": "Processing Failures and Timeouts",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "mobileHeight": null,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "showLegend": true,
+ "actions": {},
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "showMin": true,
+ "showMax": true,
+ "showAvg": false,
+ "showTotal": true
+ }
+ },
+ "id": "ad3f1417-87a8-750e-fc67-49a2de1466d4"
+ }
+ },
+ "states": {
+ "default": {
+ "name": "Rule Engine Statistics",
+ "root": true,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {
+ "sizeX": 12,
+ "sizeY": 7,
+ "mobileHeight": null,
+ "row": 0,
+ "col": 0
+ },
+ "5eb79712-5c24-3060-7e4f-6af36b8f842d": {
+ "sizeX": 24,
+ "sizeY": 5,
+ "row": 7,
+ "col": 0
+ },
+ "ad3f1417-87a8-750e-fc67-49a2de1466d4": {
+ "sizeX": 12,
+ "sizeY": 7,
+ "mobileHeight": null,
+ "row": 0,
+ "col": 12
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margins": [
+ 10,
+ 10
+ ],
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ }
+ },
+ "entityAliases": {
+ "140f23dd-e3a0-ed98-6189-03c49d2d8018": {
+ "id": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
+ "alias": "TbServiceQueues",
+ "filter": {
+ "type": "assetType",
+ "resolveMultiple": true,
+ "assetType": "TbServiceQueue",
+ "assetNameFilter": ""
+ }
+ }
+ },
+ "timewindow": {
+ "displayValue": "",
+ "selectedTab": 0,
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 60000
+ },
+ "history": {
+ "historyType": 0,
+ "interval": 1000,
+ "timewindowMs": 60000,
+ "fixedTimewindow": {
+ "startTimeMs": 1586176634823,
+ "endTimeMs": 1586263034823
+ }
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ }
+ },
+ "settings": {
+ "stateControllerId": "entity",
+ "showTitle": false,
+ "showDashboardsSelect": true,
+ "showEntitiesSelect": true,
+ "showDashboardTimewindow": true,
+ "showDashboardExport": true,
+ "toolbarAlwaysOpen": true
+ }
+ },
+ "name": "Rule Engine Statistics"
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/dashboards/software.json b/application/src/main/data/json/demo/dashboards/software.json
new file mode 100644
index 0000000000000000000000000000000000000000..e9d7e9bbbdbf115aad6e2e637e934d13f6813886
--- /dev/null
+++ b/application/src/main/data/json/demo/dashboards/software.json
@@ -0,0 +1,2492 @@
+{
+ "title": "Software",
+ "image": null,
+ "configuration": {
+ "description": "",
+ "widgets": {
+ "cd03188e-cd9d-9601-fd57-da4cb95fc016": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": false,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "8fdb88d0-50ac-2232-fdb7-69c30c16544e",
+ "dataKeys": [
+ {
+ "name": "current_sw_title",
+ "type": "timeseries",
+ "label": "Current SW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_sw_version",
+ "type": "timeseries",
+ "label": "Current SW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_title",
+ "type": "timeseries",
+ "label": "Target SW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_version",
+ "type": "timeseries",
+ "label": "Target SW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_ts",
+ "type": "timeseries",
+ "label": "Target SW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_checksum",
+ "type": "attribute",
+ "label": "sw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_url",
+ "type": "attribute",
+ "label": "sw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.3355829384124256,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History software update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_software_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit software",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download software",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016"
+ },
+ "100b756c-0082-6505-3ae1-3603e6deea48": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "timeseries_table",
+ "type": "timeseries",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 8,
+ "sizeY": 6.5,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "19f41c21-d9af-e666-8f50-e1748778f955",
+ "filterId": null,
+ "dataKeys": [
+ {
+ "name": "current_sw_title",
+ "type": "timeseries",
+ "label": "Current software title",
+ "color": "#2196f3",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.5978079905579401,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_sw_version",
+ "type": "timeseries",
+ "label": "Current software version",
+ "color": "#4caf50",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.027392025058568192,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_title",
+ "type": "timeseries",
+ "label": "Target software title",
+ "color": "#f44336",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.9496350796287059,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_version",
+ "type": "timeseries",
+ "label": "Target software version",
+ "color": "#ffc107",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.6734152252264187,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#607d8b",
+ "settings": {
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.2983399718643074,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": true,
+ "postFuncBody": "function capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\nif (value !== '') {\n return capitalize(value);\n}\nreturn value;"
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
+ "realtime": {
+ "realtimeType": 0,
+ "timewindowMs": 2592000000,
+ "quickInterval": "CURRENT_DAY",
+ "interval": 1000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "showTimestamp": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "enableSearch": true,
+ "enableStickyHeader": true,
+ "enableStickyAction": true
+ },
+ "title": "Software history",
+ "dropShadow": false,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {},
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "displayTimewindow": true,
+ "titleTooltip": ""
+ },
+ "row": 0,
+ "col": 0,
+ "id": "100b756c-0082-6505-3ae1-3603e6deea48"
+ },
+ "17543c57-af4a-2c1e-bf12-53a7b46791e6": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "waitingDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n

\n
\n ${waitingDevicesNumber:0}\n
\n
\n Device Waiting\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_waiting",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "4d9a77a2-f0a5-690c-a83b-b0e940be788c"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6"
+ },
+ "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "updatingDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n

\n
\n ${updatingDevicesNumber:0}\n
\n
\n Device Updating\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_updating",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "57d39904-2350-b29b-78ed-56b8268814cb"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6"
+ },
+ "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "6044e198-df64-cd76-f339-696f220c4943",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "updatedDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n

\n
\n ${updatedDevicesNumber:0}\n
\n
\n Device Updated\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_updated",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "d787c212-8c56-34f0-349a-5aae2ffd1eae"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81"
+ },
+ "77b10144-b904-edd5-8c7c-8fb75616c6d8": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "html_value_card",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 8,
+ "sizeY": 3,
+ "config": {
+ "datasources": [
+ {
+ "type": "entityCount",
+ "name": "",
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f",
+ "dataKeys": [
+ {
+ "name": "count",
+ "type": "count",
+ "label": "updatingDevicesNumber",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.7404827038869322,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "cardHtml": "\n
\n
\n
\n ${updatingDevicesNumber:0}\n
\n \n
\n Device Failed\n
\n
\n
",
+ "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .container-svg {\n height: 40px;\n width: 40px;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .container-svg {\n height: 28px;\n width: 28px;\n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}"
+ },
+ "title": "New HTML Value Card",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {
+ "elementClick": [
+ {
+ "name": "activeDevices",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_error",
+ "setEntityId": false,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "0b3d2887-9929-84d5-3795-0763dca15cba"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "enableDataExport": false,
+ "displayTimewindow": true
+ },
+ "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8"
+ },
+ "21be08bb-ec90-f760-ad6f-e7678f12c401": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e",
+ "dataKeys": [
+ {
+ "name": "current_sw_title",
+ "type": "timeseries",
+ "label": "Current SW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_sw_version",
+ "type": "timeseries",
+ "label": "Current SW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_title",
+ "type": "timeseries",
+ "label": "Target SW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_version",
+ "type": "timeseries",
+ "label": "Target SW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_ts",
+ "type": "timeseries",
+ "label": "Target SW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_checksum",
+ "type": "attribute",
+ "label": "sw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_url",
+ "type": "attribute",
+ "label": "sw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.3355829384124256,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History software update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_software_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit software",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download software",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "21be08bb-ec90-f760-ad6f-e7678f12c401"
+ },
+ "e8280043-d3dc-7acb-c2ff-a4522972ff91": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897",
+ "dataKeys": [
+ {
+ "name": "current_sw_title",
+ "type": "timeseries",
+ "label": "Current SW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_sw_version",
+ "type": "timeseries",
+ "label": "Current SW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_title",
+ "type": "timeseries",
+ "label": "Target SW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_version",
+ "type": "timeseries",
+ "label": "Target SW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_ts",
+ "type": "timeseries",
+ "label": "Target SW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_checksum",
+ "type": "attribute",
+ "label": "sw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_url",
+ "type": "attribute",
+ "label": "sw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.3355829384124256,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History software update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_software_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit software",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download software",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91"
+ },
+ "3624013b-378c-f110-5eba-ae95c25a4dcc": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f",
+ "dataKeys": [
+ {
+ "name": "current_sw_title",
+ "type": "timeseries",
+ "label": "Current SW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_sw_version",
+ "type": "timeseries",
+ "label": "Current SW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_title",
+ "type": "timeseries",
+ "label": "Target SW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_version",
+ "type": "timeseries",
+ "label": "Target SW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_ts",
+ "type": "timeseries",
+ "label": "Target SW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_checksum",
+ "type": "attribute",
+ "label": "sw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_url",
+ "type": "attribute",
+ "label": "sw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.3355829384124256,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History software update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_software_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit software",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download software",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "3624013b-378c-f110-5eba-ae95c25a4dcc"
+ },
+ "d2d13e0d-4e71-889f-9343-ad2f0af9f176": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "enableStickyHeader": true,
+ "enableStickyAction": true,
+ "entitiesTitle": "Devices",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Device"
+ },
+ "title": "New Entities table",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "filterId": "6044e198-df64-cd76-f339-696f220c4943",
+ "dataKeys": [
+ {
+ "name": "current_sw_title",
+ "type": "timeseries",
+ "label": "Current SW title",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.09545533885166413,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "current_sw_version",
+ "type": "timeseries",
+ "label": "Current SW version",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.7206056602328659,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_title",
+ "type": "timeseries",
+ "label": "Target SW title",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.9934225682766313,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_version",
+ "type": "timeseries",
+ "label": "Target SW version",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.5251724416842531,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "target_sw_ts",
+ "type": "timeseries",
+ "label": "Target SW set time",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';"
+ },
+ "_hash": 0.31823244858578237,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Progress",
+ "color": "#9c27b0",
+ "settings": {
+ "columnWidth": "30%",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "return {\n 'padding-right': '30px'\n}",
+ "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return ``;\n}"
+ },
+ "_hash": 0.8174211757846257,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_state",
+ "type": "timeseries",
+ "label": "Status",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "130px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled",
+ "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};",
+ "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';"
+ },
+ "_hash": 0.7764426948615217,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_checksum",
+ "type": "attribute",
+ "label": "sw_checksum",
+ "color": "#3f51b5",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.5594087842471693,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "sw_url",
+ "type": "attribute",
+ "label": "sw_url",
+ "color": "#e91e63",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "hidden",
+ "columnSelectionToDisplay": "disabled"
+ },
+ "_hash": 0.3355829384124256,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
+ }
+ ],
+ "actions": {
+ "actionCellButton": [
+ {
+ "name": "History software update",
+ "icon": "history",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "device_software_history",
+ "setEntityId": true,
+ "stateEntityParamName": null,
+ "openInSeparateDialog": false,
+ "dialogTitle": "",
+ "dialogHideDashboardToolbar": true,
+ "dialogWidth": null,
+ "dialogHeight": null,
+ "openRightLayout": false,
+ "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b"
+ },
+ {
+ "name": "Edit software",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": "form {\n min-width: 300px !important;\n}",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
+ "customResources": [],
+ "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
+ },
+ {
+ "name": "Download software",
+ "icon": "file_download",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "12533058-42f6-e75f-620c-219c48d01ec0"
+ },
+ {
+ "name": "Copy checksum/URL",
+ "icon": "content_copy",
+ "type": "custom",
+ "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}",
+ "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {}
+ },
+ "row": 0,
+ "col": 0,
+ "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176"
+ }
+ },
+ "states": {
+ "default": {
+ "name": "Device list",
+ "root": true,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "cd03188e-cd9d-9601-fd57-da4cb95fc016": {
+ "sizeX": 19,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ },
+ "17543c57-af4a-2c1e-bf12-53a7b46791e6": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 0,
+ "col": 19
+ },
+ "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 3,
+ "col": 19
+ },
+ "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 9,
+ "col": 19
+ },
+ "77b10144-b904-edd5-8c7c-8fb75616c6d8": {
+ "sizeX": 5,
+ "sizeY": 3,
+ "row": 6,
+ "col": 19
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 12,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": true,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_software_history": {
+ "name": "Software history: ${entityName}",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "100b756c-0082-6505-3ae1-3603e6deea48": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_waiting": {
+ "name": "Device waiting",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "21be08bb-ec90-f760-ad6f-e7678f12c401": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_updating": {
+ "name": "Device updating",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "e8280043-d3dc-7acb-c2ff-a4522972ff91": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_updated": {
+ "name": "Device updated",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "d2d13e0d-4e71-889f-9343-ad2f0af9f176": {
+ "sizeX": 27,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ },
+ "device_error": {
+ "name": "Device failed",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "3624013b-378c-f110-5eba-ae95c25a4dcc": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "margin": 10,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "backgroundImageUrl": null,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70
+ }
+ }
+ }
+ }
+ },
+ "entityAliases": {
+ "639da5b4-31f0-0151-6282-c37a3897b7e8": {
+ "id": "639da5b4-31f0-0151-6282-c37a3897b7e8",
+ "alias": "All devices",
+ "filter": {
+ "type": "entityType",
+ "resolveMultiple": true,
+ "entityType": "DEVICE"
+ }
+ },
+ "19f41c21-d9af-e666-8f50-e1748778f955": {
+ "id": "19f41c21-d9af-e666-8f50-e1748778f955",
+ "alias": "State entity",
+ "filter": {
+ "type": "stateEntity",
+ "resolveMultiple": false,
+ "stateEntityParamName": null,
+ "defaultStateEntity": null
+ }
+ }
+ },
+ "filters": {
+ "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e": {
+ "id": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e",
+ "filter": "WaitingDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "sw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "QUEUED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "579f0468-9ce9-7e3e-b34c-88dd3de59897": {
+ "id": "579f0468-9ce9-7e3e-b34c-88dd3de59897",
+ "filter": "UpdatingDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "sw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "OR",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "INITIATED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "sw_state equel",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "DOWNLOADING",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "sw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "DOWNLOADED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "sw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "VERIFIED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "sw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ },
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "UPDATING",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": false,
+ "label": "sw_state equal",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ],
+ "type": "COMPLEX"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "6044e198-df64-cd76-f339-696f220c4943": {
+ "id": "6044e198-df64-cd76-f339-696f220c4943",
+ "filter": "UpdetedDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "sw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "UPDATED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "bdbc6ea1-95a7-3912-341a-58dc7704a00f": {
+ "id": "bdbc6ea1-95a7-3912-341a-58dc7704a00f",
+ "filter": "FailedDevicesFilter",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "TIME_SERIES",
+ "key": "sw_state"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "EQUAL",
+ "value": {
+ "defaultValue": "FAILED",
+ "dynamicValue": null
+ },
+ "ignoreCase": false,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "",
+ "autogeneratedLabel": true,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": false
+ },
+ "8fdb88d0-50ac-2232-fdb7-69c30c16544e": {
+ "id": "8fdb88d0-50ac-2232-fdb7-69c30c16544e",
+ "filter": "DeviceSearch",
+ "keyFilters": [
+ {
+ "key": {
+ "type": "ENTITY_FIELD",
+ "key": "name"
+ },
+ "valueType": "STRING",
+ "predicates": [
+ {
+ "keyFilterPredicate": {
+ "operation": "CONTAINS",
+ "value": {
+ "defaultValue": ""
+ },
+ "ignoreCase": true,
+ "type": "STRING"
+ },
+ "userInfo": {
+ "editable": true,
+ "label": "Device name",
+ "autogeneratedLabel": false,
+ "order": 0
+ }
+ }
+ ]
+ }
+ ],
+ "editable": true
+ }
+ },
+ "timewindow": {
+ "displayValue": "",
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
+ "realtime": {
+ "realtimeType": 0,
+ "interval": 1000,
+ "timewindowMs": 60000,
+ "quickInterval": "CURRENT_DAY"
+ },
+ "history": {
+ "historyType": 0,
+ "interval": 1000,
+ "timewindowMs": 60000,
+ "fixedTimewindow": {
+ "startTimeMs": 1618998609030,
+ "endTimeMs": 1619085009030
+ },
+ "quickInterval": "CURRENT_DAY"
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ }
+ },
+ "settings": {
+ "stateControllerId": "entity",
+ "showTitle": false,
+ "showDashboardsSelect": false,
+ "showEntitiesSelect": false,
+ "showDashboardTimewindow": true,
+ "showDashboardExport": false,
+ "toolbarAlwaysOpen": true,
+ "titleColor": "rgba(0,0,0,0.870588)",
+ "showFilters": true,
+ "showDashboardLogo": false,
+ "dashboardLogoUrl": null,
+ "showUpdateDashboardImage": false
+ }
+ },
+ "name": "Software"
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/dashboards/thermostats.json b/application/src/main/data/json/demo/dashboards/thermostats.json
new file mode 100644
index 0000000000000000000000000000000000000000..4f486effdf8981e44d8e7d87439156be5ad33db2
--- /dev/null
+++ b/application/src/main/data/json/demo/dashboards/thermostats.json
@@ -0,0 +1,1239 @@
+{
+ "title": "Thermostats",
+ "configuration": {
+ "widgets": {
+ "f33c746c-0dfc-c212-395b-b448c8a17209": {
+ "isSystemType": true,
+ "bundleAlias": "cards",
+ "typeAlias": "entities_table",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 11,
+ "sizeY": 11,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSearch": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "entityName",
+ "displayEntityName": true,
+ "displayEntityType": false,
+ "enableSelectColumnDisplay": false,
+ "entitiesTitle": "Thermostats",
+ "displayEntityLabel": false,
+ "entityNameColumnTitle": "Thermostat name"
+ },
+ "title": "Thermostats",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "datasources": [
+ {
+ "type": "entity",
+ "name": null,
+ "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e",
+ "dataKeys": [
+ {
+ "name": "active",
+ "type": "attribute",
+ "label": "Active",
+ "color": "#2196f3",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": true,
+ "useCellContentFunction": true,
+ "cellContentFunction": "value = '⬤';\nreturn value;",
+ "cellStyleFunction": "var color;\nif (value === \"true\") {\n color = 'rgb(39, 134, 34)';\n} else {\n color = 'rgb(255, 0, 0)';\n}\nreturn {\n color: color,\n fontSize: '18px'\n};"
+ },
+ "_hash": 0.9264526512320641
+ },
+ {
+ "name": "temperature",
+ "type": "timeseries",
+ "label": "Temperature",
+ "color": "#4caf50",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.9801965063904188,
+ "units": "°C",
+ "decimals": 1
+ },
+ {
+ "name": "humidity",
+ "type": "timeseries",
+ "label": "Humidity",
+ "color": "#f44336",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "useCellContentFunction": false
+ },
+ "_hash": 0.5726727868178358,
+ "units": "%",
+ "decimals": 0
+ },
+ {
+ "name": "latitude",
+ "type": "attribute",
+ "label": "latitude",
+ "color": "#ffc107",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.16055765877264894
+ },
+ {
+ "name": "longitude",
+ "type": "attribute",
+ "label": "longitude",
+ "color": "#607d8b",
+ "settings": {
+ "columnWidth": "0px",
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": ""
+ },
+ "_hash": 0.10969512220289346
+ }
+ ]
+ }
+ ],
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "displayTimewindow": true,
+ "actions": {
+ "headerButton": [
+ {
+ "id": "85b803db-90f2-5c63-1388-a378e0eb10d6",
+ "name": "Edit location",
+ "icon": "map",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "map",
+ "setEntityId": false
+ },
+ {
+ "name": "Add",
+ "icon": "add",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": ".add-entity-form{\n width: 300px;\n}\n",
+ "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}",
+ "customResources": [],
+ "id": "8ab5a518-67d2-b6a2-956d-81fd512294b2"
+ }
+ ],
+ "actionCellButton": [
+ {
+ "id": "ca241cd8-788d-5508-a9ce-74b03ef42a7f",
+ "name": "Chart",
+ "icon": "show_chart",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "chart",
+ "setEntityId": true
+ },
+ {
+ "name": "Edit",
+ "icon": "edit",
+ "type": "customPretty",
+ "customHtml": "",
+ "customCss": ".edit-entity-form{\n width: 300px;\n}",
+ "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}",
+ "customResources": [],
+ "id": "7506576f-87ba-d3a0-88fb-e304d451776d"
+ },
+ {
+ "name": "Delete",
+ "icon": "delete",
+ "type": "custom",
+ "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenDeleteEntityDialog();\n\nfunction openDeleteEntityDialog() {\n let title = 'Delete thermostat \"' + entityName + '\"';\n let content = 'Are you sure you want to delete the thermostat \"' +\n entityName + '\"?';\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\n function(result) {\n if (result) {\n deleteEntity();\n }\n }\n );\n}\n\nfunction deleteEntity() {\n deviceService.deleteDevice(entityId.id).subscribe(\n function success() {\n widgetContext.updateAliases();\n },\n function fail() {\n showErrorDialog();\n }\n );\n}\n\nfunction showErrorDialog() {\n let title = 'Error';\n let content = 'An error occurred while deleting the thermostat. Please try again.';\n dialogs.alert(title, content, 'CLOSE').subscribe(\n function(result) {}\n );\n}",
+ "id": "3488848b-e47d-6af6-659f-5d78369ece5e"
+ }
+ ],
+ "rowClick": []
+ }
+ },
+ "id": "f33c746c-0dfc-c212-395b-b448c8a17209"
+ },
+ "7943196b-eedb-d422-f9c3-b32d379ad172": {
+ "isSystemType": true,
+ "bundleAlias": "alarm_widgets",
+ "typeAlias": "alarms_table",
+ "type": "alarm",
+ "title": "New widget",
+ "sizeX": 13,
+ "sizeY": 5,
+ "config": {
+ "timewindow": {
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 86400000
+ },
+ "aggregation": {
+ "type": "NONE",
+ "limit": 200
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "4px",
+ "settings": {
+ "enableSelection": true,
+ "enableSearch": true,
+ "displayDetails": true,
+ "allowAcknowledgment": true,
+ "allowClear": true,
+ "displayPagination": true,
+ "defaultPageSize": 10,
+ "defaultSortOrder": "-createdTime",
+ "enableSelectColumnDisplay": false,
+ "alarmsTitle": "Alarms",
+ "enableFilter": true
+ },
+ "title": "New Alarms table",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400,
+ "padding": "5px 10px 5px 10px"
+ },
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "alarmSource": {
+ "type": "entity",
+ "name": "alarms",
+ "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e",
+ "filterId": null,
+ "dataKeys": [
+ {
+ "name": "createdTime",
+ "type": "alarm",
+ "label": "Created time",
+ "color": "#2196f3",
+ "settings": {},
+ "_hash": 0.7308410188824108
+ },
+ {
+ "name": "originator",
+ "type": "alarm",
+ "label": "Originator",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.056085530105439485
+ },
+ {
+ "name": "type",
+ "type": "alarm",
+ "label": "Type",
+ "color": "#f44336",
+ "settings": {},
+ "_hash": 0.10212012352561795
+ },
+ {
+ "name": "severity",
+ "type": "alarm",
+ "label": "Severity",
+ "color": "#ffc107",
+ "settings": {},
+ "_hash": 0.1777349980531262
+ },
+ {
+ "name": "status",
+ "type": "alarm",
+ "label": "Status",
+ "color": "#607d8b",
+ "settings": {},
+ "_hash": 0.7977920750136249
+ }
+ ]
+ },
+ "alarmSearchStatus": "ANY",
+ "alarmsPollingInterval": 5,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "displayTimewindow": true,
+ "actions": {},
+ "datasources": [],
+ "alarmsMaxCountLoad": 0,
+ "alarmsFetchSize": 100
+ },
+ "id": "7943196b-eedb-d422-f9c3-b32d379ad172"
+ },
+ "14a19183-f0b2-d6be-0f62-9863f0a51111": {
+ "isSystemType": true,
+ "bundleAlias": "charts",
+ "typeAlias": "basic_timeseries",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 18,
+ "sizeY": 6,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "temperature",
+ "type": "timeseries",
+ "label": "Temperature",
+ "color": "#ef5350",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": true,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "showSeparateAxis": false,
+ "axisPosition": "left",
+ "thresholds": [
+ {
+ "thresholdValueSource": "predefinedValue"
+ }
+ ],
+ "comparisonSettings": {
+ "showValuesForComparison": true
+ }
+ },
+ "_hash": 0.7852346160709658,
+ "units": "°C",
+ "decimals": 1
+ }
+ ],
+ "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 30000,
+ "timewindowMs": 3600000
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "shadowSize": 4,
+ "fontColor": "#545454",
+ "fontSize": 10,
+ "xaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "yaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "grid": {
+ "color": "#545454",
+ "tickColor": "#DDDDDD",
+ "verticalLines": true,
+ "horizontalLines": true,
+ "outlineWidth": 1
+ },
+ "stack": false,
+ "tooltipIndividual": false,
+ "timeForComparison": "months",
+ "xaxisSecond": {
+ "axisPosition": "top",
+ "showLabels": true
+ },
+ "smoothLines": true
+ },
+ "title": "Temperature",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "mobileHeight": null,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "showLegend": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "showMin": true,
+ "showMax": true,
+ "showAvg": true,
+ "showTotal": false
+ },
+ "actions": {}
+ },
+ "id": "14a19183-f0b2-d6be-0f62-9863f0a51111"
+ },
+ "07f49fd5-a73b-d74c-c220-362c20af81f4": {
+ "isSystemType": true,
+ "bundleAlias": "charts",
+ "typeAlias": "basic_timeseries",
+ "type": "timeseries",
+ "title": "New widget",
+ "sizeX": 18,
+ "sizeY": 6,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "humidity",
+ "type": "timeseries",
+ "label": "Humidity",
+ "color": "#2196f3",
+ "settings": {
+ "excludeFromStacking": false,
+ "hideDataByDefault": false,
+ "disableDataHiding": false,
+ "removeFromLegend": false,
+ "showLines": true,
+ "fillLines": true,
+ "showPoints": false,
+ "showPointShape": "circle",
+ "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
+ "showPointsLineWidth": 5,
+ "showPointsRadius": 3,
+ "showSeparateAxis": false,
+ "axisPosition": "left",
+ "thresholds": [
+ {
+ "thresholdValueSource": "predefinedValue"
+ }
+ ],
+ "comparisonSettings": {
+ "showValuesForComparison": true
+ }
+ },
+ "_hash": 0.28640715926957183,
+ "units": "%",
+ "decimals": 0
+ }
+ ],
+ "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "interval": 30000,
+ "timewindowMs": 3600000
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "shadowSize": 4,
+ "fontColor": "#545454",
+ "fontSize": 10,
+ "xaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "yaxis": {
+ "showLabels": true,
+ "color": "#545454"
+ },
+ "grid": {
+ "color": "#545454",
+ "tickColor": "#DDDDDD",
+ "verticalLines": true,
+ "horizontalLines": true,
+ "outlineWidth": 1
+ },
+ "stack": false,
+ "tooltipIndividual": false,
+ "timeForComparison": "months",
+ "xaxisSecond": {
+ "axisPosition": "top",
+ "showLabels": true
+ },
+ "smoothLines": true
+ },
+ "title": "Humidity",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "mobileHeight": null,
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "showLegend": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "showMin": true,
+ "showMax": true,
+ "showAvg": true,
+ "showTotal": false
+ },
+ "actions": {}
+ },
+ "id": "07f49fd5-a73b-d74c-c220-362c20af81f4"
+ },
+ "c4631f94-2db3-523b-4d09-2a1a0a75d93f": {
+ "isSystemType": true,
+ "bundleAlias": "input_widgets",
+ "typeAlias": "update_multiple_attributes",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 6,
+ "sizeY": 6,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "temperatureAlarmFlag",
+ "type": "attribute",
+ "label": "High temperature alarm",
+ "color": "#4caf50",
+ "settings": {
+ "dataKeyType": "server",
+ "dataKeyValueType": "booleanCheckbox",
+ "required": false,
+ "isEditable": "editable",
+ "dataKeyHidden": false,
+ "step": 1
+ },
+ "_hash": 0.8725278440159361
+ },
+ {
+ "name": "temperatureAlarmThreshold",
+ "type": "attribute",
+ "label": "High temperature threshold, °C",
+ "color": "#f44336",
+ "settings": {
+ "dataKeyType": "server",
+ "dataKeyValueType": "double",
+ "required": false,
+ "isEditable": "editable",
+ "dataKeyHidden": false,
+ "step": 1,
+ "disabledOnDataKey": "temperatureAlarmFlag"
+ },
+ "_hash": 0.7316078472857874
+ },
+ {
+ "name": "humidityAlarmFlag",
+ "type": "attribute",
+ "label": "Low humidity alarm",
+ "color": "#ffc107",
+ "settings": {
+ "dataKeyType": "server",
+ "dataKeyValueType": "booleanCheckbox",
+ "required": false,
+ "isEditable": "editable",
+ "dataKeyHidden": false,
+ "step": 1
+ },
+ "_hash": 0.5339673667431057
+ },
+ {
+ "name": "humidityAlarmThreshold",
+ "type": "attribute",
+ "label": "Low humidity threshold, %",
+ "color": "#607d8b",
+ "settings": {
+ "dataKeyType": "server",
+ "dataKeyValueType": "double",
+ "required": false,
+ "isEditable": "editable",
+ "dataKeyHidden": false,
+ "step": 1,
+ "disabledOnDataKey": "humidityAlarmFlag"
+ },
+ "_hash": 0.2687091190358901
+ }
+ ],
+ "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": true,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "showActionButtons": false,
+ "showResultMessage": true,
+ "fieldsAlignment": "column",
+ "fieldsInRow": 2,
+ "groupTitle": "${entityName}",
+ "widgetTitle": "Termostat settings"
+ },
+ "title": "New Update Multiple Attributes",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "enableDataExport": false,
+ "widgetStyle": {},
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "actions": {},
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true
+ },
+ "id": "c4631f94-2db3-523b-4d09-2a1a0a75d93f"
+ },
+ "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb": {
+ "isSystemType": true,
+ "bundleAlias": "maps_v2",
+ "typeAlias": "openstreetmap",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 13,
+ "sizeY": 6,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "temperature",
+ "type": "timeseries",
+ "label": "temperature",
+ "color": "#2196f3",
+ "settings": {},
+ "_hash": 0.1371919646686739,
+ "decimals": 1,
+ "postFuncBody": "return value || \"\";"
+ },
+ {
+ "name": "humidity",
+ "type": "timeseries",
+ "label": "humidity",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.043177186765847475,
+ "decimals": 0,
+ "postFuncBody": "return value || \"\";"
+ },
+ {
+ "name": "longitude",
+ "type": "attribute",
+ "label": "longitude",
+ "color": "#f44336",
+ "settings": {},
+ "_hash": 0.5548964320315584
+ },
+ {
+ "name": "latitude",
+ "type": "attribute",
+ "label": "latitude",
+ "color": "#ffc107",
+ "settings": {},
+ "_hash": 0.1803778014971602
+ },
+ {
+ "name": "active",
+ "type": "attribute",
+ "label": "active",
+ "color": "#607d8b",
+ "settings": {},
+ "_hash": 0.30926987994082844
+ }
+ ],
+ "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "fitMapBounds": true,
+ "latKeyName": "latitude",
+ "lngKeyName": "longitude",
+ "showLabel": true,
+ "label": "${entityName}",
+ "tooltipPattern": "${entityName}
Temperature: ${temperature:1} °C
Humidity: ${humidity:0} %
Thermostat details
",
+ "markerImageSize": 48,
+ "useColorFunction": false,
+ "markerImages": [
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
+ ],
+ "useMarkerImageFunction": true,
+ "colorFunction": "\n",
+ "color": "#fe7569",
+ "mapProvider": "OpenStreetMap.HOT",
+ "showTooltip": true,
+ "autocloseTooltip": true,
+ "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ "defaultCenterPosition": [
+ 0,
+ 0
+ ],
+ "showTooltipAction": "click",
+ "polygonKeyName": "coordinates",
+ "polygonOpacity": 0.5,
+ "polygonStrokeOpacity": 1,
+ "polygonStrokeWeight": 1,
+ "zoomOnClick": true,
+ "showCoverageOnHover": true,
+ "animate": true,
+ "maxClusterRadius": 80,
+ "removeOutsideVisibleBounds": true,
+ "useLabelFunction": true,
+ "labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '' + \n '${entityLabel}' + \n ''",
+ "defaultZoomLevel": 14,
+ "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;"
+ },
+ "title": "Thermostat maps",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {
+ "headerButton": [],
+ "tooltipAction": [
+ {
+ "id": "bef25673-b37a-8821-bc0f-5d6dd3680f24",
+ "name": "navigate_to_details",
+ "icon": "more_horiz",
+ "type": "openDashboardState",
+ "targetDashboardStateId": "chart",
+ "setEntityId": true
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true
+ },
+ "id": "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb"
+ },
+ "00fb2742-ba1f-7e43-673f-d6c08b72ed06": {
+ "isSystemType": true,
+ "bundleAlias": "input_widgets",
+ "typeAlias": "markers_placement_openstreetmap",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 24,
+ "sizeY": 12,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "longitude",
+ "type": "attribute",
+ "label": "longitude",
+ "color": "#2196f3",
+ "settings": {},
+ "_hash": 0.3640193654284214
+ },
+ {
+ "name": "latitude",
+ "type": "attribute",
+ "label": "latitude",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.49020393887695923
+ },
+ {
+ "name": "temperature",
+ "type": "timeseries",
+ "label": "temperature",
+ "color": "#f44336",
+ "settings": {},
+ "_hash": 0.5885892766009955,
+ "postFuncBody": "return value || \"\";"
+ },
+ {
+ "name": "humidity",
+ "type": "timeseries",
+ "label": "humidity",
+ "color": "#ffc107",
+ "settings": {},
+ "_hash": 0.21077893588180707,
+ "postFuncBody": "return value || \"\";"
+ },
+ {
+ "name": "active",
+ "type": "attribute",
+ "label": "active",
+ "color": "#607d8b",
+ "settings": {},
+ "_hash": 0.34722983638504346
+ }
+ ],
+ "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "fitMapBounds": true,
+ "latKeyName": "latitude",
+ "lngKeyName": "longitude",
+ "showLabel": true,
+ "label": "${entityName}",
+ "tooltipPattern": "${entityName}
Temperature: ${temperature:1} °C
Humidity: ${humidity:0} %
Delete",
+ "markerImageSize": 34,
+ "useColorFunction": false,
+ "markerImages": [
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
+ ],
+ "useMarkerImageFunction": true,
+ "color": "#fe7569",
+ "mapProvider": "OpenStreetMap.HOT",
+ "showTooltip": true,
+ "autocloseTooltip": true,
+ "defaultCenterPosition": "0,0",
+ "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ "showTooltipAction": "click",
+ "polygonKeyName": "coordinates",
+ "polygonOpacity": 0.5,
+ "polygonStrokeOpacity": 1,
+ "polygonStrokeWeight": 1,
+ "zoomOnClick": true,
+ "showCoverageOnHover": true,
+ "animate": true,
+ "maxClusterRadius": 80,
+ "removeOutsideVisibleBounds": true,
+ "defaultZoomLevel": 12,
+ "labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '' + \n '${entityLabel}' + \n ''",
+ "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;",
+ "useLabelFunction": true,
+ "provider": "openstreet-map",
+ "draggableMarker": true
+ },
+ "title": "New Markers Placement - OpenStreetMap",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {
+ "tooltipAction": [
+ {
+ "name": "delete",
+ "icon": "more_horiz",
+ "type": "custom",
+ "customFunction": "var entityDatasource = widgetContext.mapInstance.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.mapInstance.saveMarkerLocation(entityDatasource[0], null, null).subscribe(function success() {\n widgetContext.updateAliases();\n});",
+ "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true
+ },
+ "id": "00fb2742-ba1f-7e43-673f-d6c08b72ed06"
+ },
+ "0a430429-9078-9ae6-2b67-e4a15a2bf8bf": {
+ "isSystemType": true,
+ "bundleAlias": "input_widgets",
+ "typeAlias": "markers_placement_openstreetmap",
+ "type": "latest",
+ "title": "New widget",
+ "sizeX": 6,
+ "sizeY": 6,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "dataKeys": [
+ {
+ "name": "longitude",
+ "type": "attribute",
+ "label": "longitude",
+ "color": "#2196f3",
+ "settings": {},
+ "_hash": 0.3640193654284214
+ },
+ {
+ "name": "latitude",
+ "type": "attribute",
+ "label": "latitude",
+ "color": "#4caf50",
+ "settings": {},
+ "_hash": 0.49020393887695923
+ },
+ {
+ "name": "temperature",
+ "type": "timeseries",
+ "label": "temperature",
+ "color": "#f44336",
+ "settings": {},
+ "_hash": 0.5885892766009955,
+ "postFuncBody": "return value || \"\";"
+ },
+ {
+ "name": "humidity",
+ "type": "timeseries",
+ "label": "humidity",
+ "color": "#ffc107",
+ "settings": {},
+ "_hash": 0.21077893588180707,
+ "postFuncBody": "return value || \"\";"
+ },
+ {
+ "name": "active",
+ "type": "attribute",
+ "label": "active",
+ "color": "#607d8b",
+ "settings": {},
+ "_hash": 0.34722983638504346
+ }
+ ],
+ "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
+ }
+ ],
+ "timewindow": {
+ "realtime": {
+ "timewindowMs": 60000
+ }
+ },
+ "showTitle": false,
+ "backgroundColor": "#fff",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "8px",
+ "settings": {
+ "fitMapBounds": true,
+ "latKeyName": "latitude",
+ "lngKeyName": "longitude",
+ "showLabel": true,
+ "label": "${entityName}",
+ "tooltipPattern": "${entityName}
Temperature: ${temperature:1} °C
Humidity: ${humidity:0} %
Delete",
+ "markerImageSize": 34,
+ "useColorFunction": false,
+ "markerImages": [
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
+ ],
+ "useMarkerImageFunction": true,
+ "color": "#fe7569",
+ "mapProvider": "OpenStreetMap.HOT",
+ "showTooltip": true,
+ "autocloseTooltip": true,
+ "defaultCenterPosition": "0,0",
+ "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ "showTooltipAction": "click",
+ "polygonKeyName": "coordinates",
+ "polygonOpacity": 0.5,
+ "polygonStrokeOpacity": 1,
+ "polygonStrokeWeight": 1,
+ "zoomOnClick": true,
+ "showCoverageOnHover": true,
+ "animate": true,
+ "maxClusterRadius": 80,
+ "removeOutsideVisibleBounds": true,
+ "defaultZoomLevel": 5,
+ "labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '' + \n '${entityLabel}' + \n ''",
+ "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;",
+ "useLabelFunction": true,
+ "provider": "openstreet-map",
+ "draggableMarker": true,
+ "editablePolygon": true
+ },
+ "title": "New Markers Placement - OpenStreetMap",
+ "dropShadow": true,
+ "enableFullscreen": false,
+ "titleStyle": {
+ "fontSize": "16px",
+ "fontWeight": 400
+ },
+ "useDashboardTimewindow": true,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {
+ "tooltipAction": [
+ {
+ "name": "delete",
+ "icon": "more_horiz",
+ "type": "custom",
+ "customFunction": "var entityDatasource = widgetContext.mapInstance.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.mapInstance.saveMarkerLocation(entityDatasource[0], null, null).subscribe(function success() {\n widgetContext.updateAliases();\n});",
+ "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66"
+ }
+ ]
+ },
+ "showTitleIcon": false,
+ "titleIcon": null,
+ "iconColor": "rgba(0, 0, 0, 0.87)",
+ "iconSize": "24px",
+ "titleTooltip": "",
+ "displayTimewindow": true
+ },
+ "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf"
+ }
+ },
+ "states": {
+ "default": {
+ "name": "Thermostats",
+ "root": true,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "f33c746c-0dfc-c212-395b-b448c8a17209": {
+ "sizeX": 11,
+ "sizeY": 11,
+ "row": 0,
+ "col": 0
+ },
+ "7943196b-eedb-d422-f9c3-b32d379ad172": {
+ "sizeX": 13,
+ "sizeY": 5,
+ "row": 0,
+ "col": 11
+ },
+ "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb": {
+ "sizeX": 13,
+ "sizeY": 6,
+ "row": 5,
+ "col": 11
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70,
+ "margin": 10
+ }
+ }
+ }
+ },
+ "map": {
+ "name": "Edit location",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "00fb2742-ba1f-7e43-673f-d6c08b72ed06": {
+ "sizeX": 24,
+ "sizeY": 12,
+ "row": 0,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70,
+ "margin": 10
+ }
+ }
+ }
+ },
+ "chart": {
+ "name": "${entityName}",
+ "root": false,
+ "layouts": {
+ "main": {
+ "widgets": {
+ "14a19183-f0b2-d6be-0f62-9863f0a51111": {
+ "sizeX": 18,
+ "sizeY": 6,
+ "mobileHeight": null,
+ "row": 0,
+ "col": 6
+ },
+ "07f49fd5-a73b-d74c-c220-362c20af81f4": {
+ "sizeX": 18,
+ "sizeY": 6,
+ "mobileHeight": null,
+ "row": 6,
+ "col": 6
+ },
+ "c4631f94-2db3-523b-4d09-2a1a0a75d93f": {
+ "sizeX": 6,
+ "sizeY": 6,
+ "row": 0,
+ "col": 0
+ },
+ "0a430429-9078-9ae6-2b67-e4a15a2bf8bf": {
+ "sizeX": 6,
+ "sizeY": 6,
+ "row": 6,
+ "col": 0
+ }
+ },
+ "gridSettings": {
+ "backgroundColor": "#eeeeee",
+ "color": "rgba(0,0,0,0.870588)",
+ "columns": 24,
+ "backgroundSizeMode": "100%",
+ "autoFillHeight": true,
+ "mobileAutoFillHeight": false,
+ "mobileRowHeight": 70,
+ "margin": 10
+ }
+ }
+ }
+ }
+ },
+ "entityAliases": {
+ "68a058e1-fdda-8482-715b-3ae4a488568e": {
+ "id": "68a058e1-fdda-8482-715b-3ae4a488568e",
+ "alias": "Thermostats",
+ "filter": {
+ "type": "deviceType",
+ "resolveMultiple": true,
+ "deviceType": "thermostat",
+ "deviceNameFilter": ""
+ }
+ },
+ "12ae98c7-1ea2-52cf-64d5-763e9d993547": {
+ "id": "12ae98c7-1ea2-52cf-64d5-763e9d993547",
+ "alias": "Thermostat",
+ "filter": {
+ "type": "stateEntity",
+ "resolveMultiple": false,
+ "stateEntityParamName": null,
+ "defaultStateEntity": null
+ }
+ }
+ },
+ "timewindow": {
+ "displayValue": "",
+ "selectedTab": 0,
+ "hideInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "realtime": {
+ "interval": 1000,
+ "timewindowMs": 60000
+ },
+ "history": {
+ "historyType": 0,
+ "interval": 1000,
+ "timewindowMs": 60000,
+ "fixedTimewindow": {
+ "startTimeMs": 1587473857304,
+ "endTimeMs": 1587560257304
+ }
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ }
+ },
+ "settings": {
+ "stateControllerId": "entity",
+ "showTitle": false,
+ "showDashboardsSelect": true,
+ "showEntitiesSelect": true,
+ "showDashboardTimewindow": true,
+ "showDashboardExport": true,
+ "toolbarAlwaysOpen": true
+ },
+ "filters": {}
+ },
+ "name": "Thermostats"
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/oauth2_config_templates/apple_config.json b/application/src/main/data/json/system/oauth2_config_templates/apple_config.json
new file mode 100644
index 0000000000000000000000000000000000000000..a956920b6bb8fc0187b38108fdf93b67b6e7c1fb
--- /dev/null
+++ b/application/src/main/data/json/system/oauth2_config_templates/apple_config.json
@@ -0,0 +1,24 @@
+{
+ "providerId": "Apple",
+ "additionalInfo": null,
+ "accessTokenUri": "https://appleid.apple.com/auth/token",
+ "authorizationUri": "https://appleid.apple.com/auth/authorize?response_mode=form_post",
+ "scope": ["email","openid","name"],
+ "jwkSetUri": "https://appleid.apple.com/auth/keys",
+ "userInfoUri": null,
+ "clientAuthenticationMethod": "POST",
+ "userNameAttributeName": "email",
+ "mapperConfig": {
+ "type": "APPLE",
+ "basic": {
+ "emailAttributeKey": "email",
+ "firstNameAttributeKey": "firstName",
+ "lastNameAttributeKey": "lastName",
+ "tenantNameStrategy": "DOMAIN"
+ }
+ },
+ "comment": null,
+ "loginButtonIcon": "apple-logo",
+ "loginButtonLabel": "Apple",
+ "helpLink": "https://developer.apple.com/sign-in-with-apple/get-started/"
+}
diff --git a/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json
new file mode 100644
index 0000000000000000000000000000000000000000..04847a9da6f9a2c8128ca4dc77bc9e02942e9acf
--- /dev/null
+++ b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json
@@ -0,0 +1,23 @@
+{
+ "providerId": "Facebook",
+ "accessTokenUri": "https://graph.facebook.com/v2.8/oauth/access_token",
+ "authorizationUri": "https://www.facebook.com/v2.8/dialog/oauth",
+ "scope": ["email","public_profile"],
+ "jwkSetUri": null,
+ "userInfoUri": "https://graph.facebook.com/me?fields=id,name,first_name,last_name,email",
+ "clientAuthenticationMethod": "BASIC",
+ "userNameAttributeName": "email",
+ "mapperConfig": {
+ "type": "BASIC",
+ "basic": {
+ "emailAttributeKey": "email",
+ "firstNameAttributeKey": "first_name",
+ "lastNameAttributeKey": "last_name",
+ "tenantNameStrategy": "DOMAIN"
+ }
+ },
+ "comment": null,
+ "loginButtonIcon": "facebook-logo",
+ "loginButtonLabel": "Facebook",
+ "helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog"
+}
diff --git a/application/src/main/data/json/system/oauth2_config_templates/github_config.json b/application/src/main/data/json/system/oauth2_config_templates/github_config.json
new file mode 100644
index 0000000000000000000000000000000000000000..439043d96122df81d2afd7b31d514137456f9c6a
--- /dev/null
+++ b/application/src/main/data/json/system/oauth2_config_templates/github_config.json
@@ -0,0 +1,21 @@
+{
+ "providerId": "Github",
+ "accessTokenUri": "https://github.com/login/oauth/access_token",
+ "authorizationUri": "https://github.com/login/oauth/authorize",
+ "scope": ["read:user","user:email"],
+ "jwkSetUri": null,
+ "userInfoUri": "https://api.github.com/user",
+ "clientAuthenticationMethod": "BASIC",
+ "userNameAttributeName": "login",
+ "mapperConfig": {
+ "type": "GITHUB",
+ "basic": {
+ "firstNameAttributeKey": "name",
+ "tenantNameStrategy": "DOMAIN"
+ }
+ },
+ "comment": "In order to log into ThingsBoard you need to have user's email. You may configure and use Custom OAuth2 Mapper to get email information. Please refer to Github Documentation",
+ "loginButtonIcon": "github-logo",
+ "loginButtonLabel": "Github",
+ "helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app"
+}
diff --git a/application/src/main/data/json/system/oauth2_config_templates/google_config.json b/application/src/main/data/json/system/oauth2_config_templates/google_config.json
new file mode 100644
index 0000000000000000000000000000000000000000..f8626439bad807d49fd10dfde778adc9c7a514ce
--- /dev/null
+++ b/application/src/main/data/json/system/oauth2_config_templates/google_config.json
@@ -0,0 +1,24 @@
+{
+ "providerId": "Google",
+ "additionalInfo": null,
+ "accessTokenUri": "https://oauth2.googleapis.com/token",
+ "authorizationUri": "https://accounts.google.com/o/oauth2/v2/auth",
+ "scope": ["email","openid","profile"],
+ "jwkSetUri": "https://www.googleapis.com/oauth2/v3/certs",
+ "userInfoUri": "https://openidconnect.googleapis.com/v1/userinfo",
+ "clientAuthenticationMethod": "BASIC",
+ "userNameAttributeName": "email",
+ "mapperConfig": {
+ "type": "BASIC",
+ "basic": {
+ "emailAttributeKey": "email",
+ "firstNameAttributeKey": "given_name",
+ "lastNameAttributeKey": "family_name",
+ "tenantNameStrategy": "DOMAIN"
+ }
+ },
+ "comment": null,
+ "loginButtonIcon": "google-logo",
+ "loginButtonLabel": "Google",
+ "helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication"
+}
diff --git a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..448fcaf52c554b0cbb31cc6d2a04b622c19a35ec
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json
@@ -0,0 +1,30 @@
+{
+ "widgetsBundle": {
+ "alias": "alarm_widgets",
+ "title": "Alarm widgets",
+ "image": "data:image/png;base64,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",
+ "description": "Visualization of alarms for devices, assets and other entities."
+ },
+ "widgetTypes": [
+ {
+ "alias": "alarms_table",
+ "name": "Alarms table",
+ "image": "data:image/png;base64,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",
+ "description": "Displays alarms based on defined time window and other filters.",
+ "descriptor": {
+ "type": "alarm",
+ "sizeX": 10.5,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-alarms-table-widget-settings",
+ "dataKeySettingsDirective": "tb-alarms-table-key-settings",
+ "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/analogue_gauges.json b/application/src/main/data/json/system/widget_bundles/analogue_gauges.json
new file mode 100644
index 0000000000000000000000000000000000000000..b2ae3143245af4799bb5c58372c62d40d37ce318
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/analogue_gauges.json
@@ -0,0 +1,105 @@
+{
+ "widgetsBundle": {
+ "alias": "analogue_gauges",
+ "title": "Analogue gauges",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAyyUlEQVR42u2deVRVV/bn6V69ulf/0/1Hd//Tq9dvdafmqtSQGpNKjTFVlUqqMhiTmOSXVGUymmgqg2gSjQMyCiIyzyCziIAToCiCgiLOKE6gqCiCyOSEoGB/3tvmcH3D5XHfe0jIPWuvuy6Xe88959zv23ufffbZ2+fOmJdbt27duHHjypUrndZy+fLlzi+Lw3Pu5H6euvO1L9qha2tra21tPX/+fEtLy7lz586cOcORc65wvb29/f4Onc/YvGZgYIBOXraWnp6e69evc2VoaGjEB7mHO7mfp+Rx6uHK1wpMV69evXTpkqDHWOFZoNbb29vf3z8RgEU3urq6QIMHu0Q91Ead1DxmwzT2RYYODmSPkhMnTmzdujUtLW358uVBQUHz5s375JNPPvjgA46cc4Xr6enp3MOd9o9T5xgMnVeANTg4yLfv6OjgePv2bS81nZrVW3jjxMCTDB2yTAuFU6dOlZSUBAQEvPTSS4888sg3vvGNB1wr3PnrX/+ap3iWGqhHWy1v8d7Q+Xj8Y3d3dyPdR/WDsMjEocHB27cGb/UP3hrg3BUpqf1x80be6z0Qj0GRodPKu4aGhtjY2JdffvkHP/jBA54o1PPqK6/ExcUdPXpUveXs2bOMnseHzseD49JlLSOoikNDt/v7etsvtBw7uH/rhspVKaWpkTW58Vsyog8UpiRHhOzLiShPCtmRHlaSEFyVFb1/U+G5w3W9bS23+2/c0UUb72WAaMBXDl40mJbzgRV/WrVq1VtvvfW9733PIT64/s1vfvPRRx/l/PHHH//rX/8KZ/Lz8+PIOVe4zn+5x76G+KlTlz3/PNepn7coHuZxeHkAWHAXOKp+s4ZuDXSdbz5cVVaSFtWwPv1AUUpfbfHxDRl9u9ee2Dh8zE2IuL4l5djqKHU8mh91bUvK/pyIw6siNySE1G9d23WuEcam/5Foz1cFVcxFFJfiMycmJv7+97+3QQMQ+e53vztp0iTOp0yZgnT77W9/+8ILL8ifTz/9NEBBteLIOVe4zn+5hzvlT56lhm9/61uNc+ZMf+wxVTP4W7Zs2cmTJxW84JqjEhfeAtbNmzeZsPT19TnB3OCVtpbqdbl71ySDp96awtNlWT01hU1lWVd3FVnAVFusA6xr5ZZj7+bkxsLo7rLEpsLonk1JIGx3VnjlquSusyed8TDao9OqcVIYugsXLsgX5dOGh4f/6le/0uLp29/+tjAhgAVQ/va3vz344IOc8Ofzzz8viOH4l7/85cc//jHA4si5us498iBP8azlyiOPdCxY8CMQ9u1va1/Ee9H3GxsbpTG0yv2h83GHUSF34A0OAY6qdHxPdVVuwtENK69YkXS9tvhkSebVnZbzrh0FLeW557fm9lSvadmSC6QuVqxSwGpdH8fx3NrYzrLElrWxzcWxl0sTwVbP5qQTBVFXy1MarQhryI+sTAs/tnPL4EC/Mz5KCz3y+/NsoUmwVaXlFBUV/eEPf7DhUo899tiPfvQjADF58uTvfOc7KOAwpGeeeUYYFUdhVMCFZ3/5y18CLI6cc0VYl7qTp7hCDbFTp1Z+/DF1UvNjGr4l5Te/+U1+fr5qlZtDZxBYKDRMx/jNORBGAzdrNm/YU5B0eG0aWAFYHI9pjq1WDIEwjjYkwLIhMMTxghVtDflRmmMkx0O5ETCwmtKi233XHer1tHNcGVdpzMWLF+Xj7d27d/r06Tb69aPW8uKLL37rW98SPD377LPARdjPH//4RzjQ7373O478i+M3rQVgyYm6LvdwP1VxnRoaP//8kyefpE5qpiqQxItsZgavvvpqXV2dmjYaHjojwMJciRnJwTR1aOj4vtqDZauzEqORd0g35B38CQkIc7JAp7bYHkwjAktLCEeOsDHQ1rgmGil5fHUUUjI7Knhvit/hrWvv2LWKdtJa2jweUEUzlEaVkpKi/ag//OEPv//977/yyiuIP2aCnMOrYDbcAyZ+8pOfwGbQk5zN+ACWs3/xFM+++Ic/dC5c+MhDD1Hnc889B/jkXRx5Fzeo+4EjZjBlWTU2dKMGVq+12F/vaW/dsnrl6e3r2/eUn6spKctKRPY1lmbdqC2+4og52dDNPetv7itZn5t2oyrzekWaPryEjSETRTJuTA5vzA68kBd4MtO/It7fons5ajZq8n3X05VGNXv2bO23R7mGqfCN+cB8ePSqn/70p1OnToW1/OIXv3DFlPD555/r3xA2eXLZtGnUJoyQ+nkL7+KNvJe3y+RAFVjp8ePHpcEGJkOjAxbrKiwv2PKpwcG6LRvP1mw8XlHcf7S6obyQY25ybHf1Gscwqlvbf6Ty1pn62+3Ng70dQxY7gj3zGxzqu3a768KtlqP9J3b17S6+vjXVIcLQw3Kjgq8WhRxK9eN4JH3JqazAnQUpQ7dtefi1a9fQG+4XqphtyUeqrq4Wi4AUPvMTTzzB9O1Pf/oTX5oP7/Azu1/2f/jhnCeesIEyrJE38l7eThtoCe1R93Bx586d0myUQm8Bi6pZ0bSd2ly7sq0wu35Twc2G6padpR17yzv3b+05UMF5SUbCPTxp78aB0wcGuy5ienBYP3PdiooKeK9jhXfg5u1LZ/obtt+ozNACa13islM5gZfyg9vyg1pXBZ3ODgReB1L8ticH3ei6ZFMJ7R/tAHmkIIvl82zevBlWob4cChAyDkmHHARPfFQxh6Ktjwo0Dz30EEdmhc5ueOyhh7oWLvzVgw9qL/IW3sUbeS9vpw0ic7UziZ/97Gfl5eXSeHrheWDxPexV9fONR3etzbm0t/z64e3HthbBqC7UlnEUgml1bl8NpPqP1dy+3HJncATjW21tLa/gOJI9Ee23CbkJqjpKYFcWJAmdybGcH05f0lMY0poXtD1+0bmDNfaT/DHGlkJVcXHxg5pPi6KDivPPf/4TzvGPf/wDTQhjgTFuVFBQABr8/f2d3RD4zDPbZsxw9l/ey9tpg7SHI9MF7XyC+qULzIQ8CSzYuD2vOrFv54Gy1QDo6Naia4erWmvLbhzZrlAlIKvbWjp41VXp09TUVFlZydHVZbUrl/eUrz+TE6SABQGpc7mB3WtC6tOX8Cca/ZEtRTbmLow09GhsUIXwlU+SmZmprEd8ub///e+ADC2KIxoPlgIdfjNiCbYWHf0987XX5j/1lE4NvB2ZSEtUq7BKqEVJeFtWVpZ0xMWhGxlYqJz2etW+qs0o6YAJkYcQbK7eoIUUNNB8CCXJwOLGqFdtu9tuVGZqsQU1ZQVwbM4JPJsbiHzcvS4LWWqjb42BdV5p6/AqJd34hCwkY0b685//jEUAboFy46b+xBoztcFX3KyHltAeWoV2hZimnQru/CoU33Jl6HxGnB7b14KqDqqOWJX05h0brtRX3oOqE7V8bG0NcDuZ83vqgyHLsN0B97sGYny2mg9e2xipxVZnQUijFV4HU/2YMO5cnWw/T/SqDYLK5TNs2rRJmRVQYtCxsCGhu8C0+JDwKvcVc6nk4Ycf9khV4Al2RQtpJ60VU77IRKVvjTh0PvqmPHs07KssO1Caj+zrOVhxwjoNvIdRnTk8dO9CHjZAHIP27Nmzf//+5uZm9z8Y7kRHjhyhNjqJsj+s3d+83lezyoZ1NaT7d6wORjLuSfbbD9+6VybSOy/ZTqlW7FXMAUWzFl7Fd4qOjuYoyvLPf/7zB8ZfoVVig6Dl0lrFt0CbzBPpnf7Q+egsO6Cp2VhBj+2pgUUh++o3F1ytr+xruEepwnyglTioF6yqcgQEjC+mCgDhkc+GaRhJv2vXLqq1aXT/seprxaEKWL1rgrsKLJNEkY/15YU2tlP66PE1HyoU2zr2KiSU0qvgBMID5GuNT1QpbClU0WatExi2EvEfxC6vM3Q+OlqnjU/VhcYG9PGmqnWoVjcbdmBZGEbV8Z2DVxxPtRAEBw4cAGHbt28HW+5/Nny6t2zZAt/ipwNkHXCLCyeurQ0DSVdy5l/614Otef5XC0PO5gQez/Bvzg5suXeeSB89rsirdcBZs2apT4XgQ68SXqWwNW6BpVDFCg9H9C3ar/774YcfqvXE0QGLObmNatV3pbumOKt5u0VJx7COHNSgatfgtTGaZLk6Ceg4dyV3Qds7/9ab/gkIw8p1yqpvncwMqE5Y1NfVZmP1dbjoadhnQQYd12GtZYF5FhoxuotIQMGWkpLjqmDKElQxT6S1QAq+S/u1Nojk5GTppjM/CMfAshEQeL9kJsVhrML4iXEBUahR1XcZmP15faH3TP2l6f+vJ+Vfw/pWYcixlf7MELFHrFweqDXS0lN8bDwlEMWrGJ1SKexYmJAjzOGZbSFTlAQEVR5Rtz1eaKTiprSWGQYtp/30QtlOwRnas/jYOBw6H4fLajYw3F+97ez+6vSEmLM7S4DXMLCQgNfHnUtd/+Ftbf/4nzf3l97uOCsyUZm4MHqlhvk1xM2tzU+0sWx5ZCWRoZPfMWZGtWIDA+BPvgQzefs5oDh5fs9asJQyq8cwwcmYwQjTKK/jpTT1R9Zi41NPm2k57acX9EWt+eCDKp11OHQ+9pYkG8N07+VLe7ds6G06dKP5yNq8TBZw7qLqWLUzveo+lr7aoosv/dcbFel3Wdf540qXr04OzAlf1JHm25bqW7NidvfZEzaKkZuOuTwu7sVaexKrb+Lxgu3R3l7F9bKyMpwLMJovWbKEJeFPP/10wYIF2gU7bxdWr9+zFrxroqKipk2bZv922ZEhvjdPaBYcxX+LGaL90PnY6+zam+By20uK+DzNe3dAnOyvLM1JSUDHgh+MN1SxUN065T9dWxd+DwM7ur2zIDgnwr8m9ovelb7H4izEydbIeUOaVSbx2XdfZ8cPU/kWs9DLJ0FNYbXEoZMCn4qtDYAJYM21FrweANZYCj6AxdvZNwawcIx+++23ndm36AV8ix6pBXK8n8Wt2X6VzMfmN2czuE0Nh47vqji1d/tAy7G+Mw0AC+o4urds/do748wz82phcOtzPswE7Wf/pfkrzyTNAUxQZ7pvT7pvQ+zsQzG+R7es8RTTUuwKD2PlXwVuUNiZBiJuHK7YcAMfdcaMGXgG433Fs3AOX1/fMQYWrsnr1q0DWJyj9jkUxLSfXtAXesQN9E6uR0REiLO8zdD52LArrdWL/Vg1ZcUWadJcf7J2m6DKQq2NWqcUFFXssEz+T58+fX8whRdy6kegqnvF6wJ3dJ3Dhw/zY8LWhbFq8HrPlbyFAiyoPnY2ApGTivDZgzeva62ahk0PsiaIVQXNV0ZcvPbwSEGyOFtdRpvBBsF34gbuR0dGo7fxfPd2gb+i2HFET3/VWmw84rVr1fSFHolvoPKXZ3nX3vTgo7UW2jC0o/vruk4cgGNdPFw7jKrzx4eu9ygzIAWXbfg/VnVbc+XYgOpWf3fYS6CqK/DpO1/CHQtqtbUcO3ZMFg9YaFLAgs4k+cKxWpJ9D5fl2zAtAxs4eUTs7MzStahCEUYC4pfywEQp4r8ldnk17Y2PjxempR06H+2MRmsRHbp9u3J9QU/jAZDUeXwfolBQhVOU1r4MY1i7du2hQ4fEvD7WqOq72rn4L6Dq8vzfDw3cncmyhgijYt23qqoKJ5y7K+hDQ9dKogVVPSstkOKkNcV3U9jswf4+rb3UwOK0TAZhV+gcalsV3Ahl3IPbTcdDEf8tdmrQO9nYKJqW7E/UDp2P1nalHazGIwf7zja0HKiBYyEKh9nVQJ/9sAJVTOH2rjVeLYNXOzvmPgyqLn34Q3vnHMyeuDBgZdFaTXsz7mpaiML6WN/GBIvKdayi2GYB0ZjtKi8vT7tfjw8gO7dG67U3nouYQugXvVPAoqxevVoWeWyBZf9LrVib37i7EhODVgjevnx+vNjW25svzfwOqGqf/sBgV6vr00atQOy0wGt2aehs7fSQcRhVNBuGTsw5b775ptq5hSKMNMTM6HEP4/te6BH9onf0Ue0hw0ghg6CGzsehlaG389LlY3v7W46e3ru988S+YXbVf2M8oOrWuSPtb/8fUNX2j//FyuColnoUqpCGDbG+3emWk+7mo9r53ahUeFHbiYYgpk40X1QQlFxmeVirH5iIhX7ROzGoiqYPzlBntSq8j0P+X1uxmWkgVgak4TC76jjnQXCwiiK6PwJrVA8OnNjV9vr/AFUXX/1vA6f2jdrWtSl+mGNZ7A4WmbgjI8KwNJRIQ0ptR/lgZQ2PADYiO5tefdUL0vDJJ5+UTbPKmpqQkMA44CIwDCzYl1YOEvblYGUZSLp5rqG1fng+OHTDk6s3hFLJzs6uqakhIBgrKhgsLGaB69fF5CF+EPwpPn3DhvW6dRjWLah64T/fPLjZwHvBogLWqUTfrnTLSW3k7MGBmwakIbeJCFBTP/QPfs1wL/RcF3dufeUKxlJ6Rx/pKf2Vi6+99ppWGlqAxVqPdj7Y09HWUF0Ox2raUzXMsVpPOtik5V7Bo0bCsjGDYxmE+SqO4Rs3bmSayZ9ogiyhM9PET1Duv7FtJYZ1UNU6+T/21eQbNk9gRNVwrNlwrP1Rn3SdPqJVm1xcOhTnY+ZEsktC9iKj4eIIAMd6YOIW1nboo+jyYlDFZCpzQxk6H3vO37B3lxgX0LEgARbbtjyuKgmw8NZas2YN5whp8XzFCnXw4EG8yZhrwK6ItsPN19ZHtE7+DxZUPeeDF7I7771RnSfA6k6fA1kMEOm+9RuybAxaLgp0GkxMM7Wgy4izrCb7qCYwsGTXmvRU7c9m3ZPRYEwcAmto2/o1TXVVTAkv1O8aloN9V70ELArQ4ZxorTbASkpK4pvV7tplsTlZIQVdzV/i5nsHzhxS0rAp0fdw7Owjsb5lyz8zoGaJXVTtu2LGhMeV+CloN61PvELvJP4W/VVzQ7YJSShKC7CUQnN3TjTQf2BbqdbEIDTirkC3V2UcrDzCsYgk0xP9pkJVT8IMD7zr5rXejLlau4OoWdqwIozJiO7w3CBaBT9cNeLIRNz6mIo7C5s2MQpcCuUdaajdKYmiKQPCyPhg1dTuuLjadfnM/mrYFXT11F276O32M/fFrNDVdqFzyZMKVd3LpnpKz7u2brngqT3NwrGgE/G+veeHtzQyJiPGiGLoZByVv96ULwu+vA9M9EIfX7AWtY0Hk6lyK/WxiW59suFwfXV5e0OdWsOxKFg97Q4XyLxsWO+6/NmjClWXFzw2NOCuA7FqM25bilexwtOc6Lsnes7xnVu0070R9XfR3BHZor1iy8GlRM4fvHcz+4Qsar5Cr2VNmnPxomFkfGzm84f27jm0u6a8ZH1+TtaqrJV5meklhfmXzp/T+jLwJBo3W69ETfOKYb3zAgs1ClUds38+dMO4hyfWS6wb6EPbtm3jnCsXD+1cu3xezrKF2aELspfOL1n+Wd2ymXvy47WieUT9XRywmLQqZ1EiTgnHUouGE7jQR+ksvVbWLEZY3LN8bIavsrysvfnkjY4LxIERutrecuPqFRlHXBjWr19PZAusAPzJPM4rhvWWo+3v/JtC1aX3vqndAWug0GCmBaxMI+Bkpbyn+WhzxNvd0W/2WKk96i3+3LT801FNDJltaDdNIAhkoFl7Vu5KE7jQR3oqXVbrhitXrmRMsE3aAquqvLSntbnlREPrqeMKW9poZnwYdl/BAOBYHtzcjOmI2mhQR0tzm//fFara3/rf1u2KbpV9+/bRZqwstFliQ+BU3fMlqpqWv3Mi/J3zK97e5D99VMCStWd89OwV27HU3Jk6MM/fvXs3RyYNY/Ze+mgfCG7FihWyGm0DrKGKsg3nTzZ0nG26fun8MLDueNdZFEs3azus0HHCsfXC+dbcAOuizX9n77x3ZO2AAlZbpIVdga2N/jO0k4MRgcXyBYNI3ARla+C7yi9Y+cF5u8TExNAMNXScq8Ulbxf6KJ2l12qtXSwOOJXcC6yhob01VchBQmf3tp69C6wrl72KKjbigareewvYurjg8f6jO7z33p64aQKs1hVvtUS8jTSsDn536PaA68CSVcL58+crB0tEAwuF4hRqM4HCo5f9q5iq1QYej/AqkGQzdFzxON9ih+pHH32k3UYhqrqENqHXykUWh30xZd0DLGLzbS8vO3u8Huq+0PwlsLy7FQcJKD84beHKpdPHvcsmk2cJsFpWvH102TSoPHCGduI5IrDEyZ0MNlrpgOZh7zaOnZrbCOzJlqnXX3/dU98b2edw6EpLSz0LrLCwMAkqaQMsekp/tXKfbsq+nXuAxfLz0f27BU+3ey7dBdZV7wJLslLZF657F1jpswVY3dFvycnepdMGb95wHVhis5k5c6aMKbkhgu4tiplRSF7CMi1roB7cpIpe5XDouO5ZYC1dupSQDUq+0y+bntJ3+RejIcNyL8casnCs0w0Hm+r3jx3H6uhw+LPz4MxgRI5VHzbtcNg75YHTvcSxKMhBPAI8uwMHzmQ/dCyRqbVLb4tCVzkWxpt9NduVzj42Olb3+kg0KpvRQevydt4zpWMpQse6o9l95EEdy0sFXcqhjqVM4d72ynJVx5JZIezq1JEDWLPGYFaIf5XFZyEvAGzJj48fnOjyXnZtHp4VMiWEXdWHyaxw6Ks1K2QOSDMkRQhHztnNPP5mhXfu7NiyyZZj3WvH8qQhtLURg4LYqzpzFl6+hFrVjgQcg/SWWjuWos2BM76Kdiy+q7JjjQ2vMmDHulOxqRR2BRENSwHLWQBtt1wMBvo6PvmpxVj14n/B12qMl7dvnWtQeMKCBceCNkd+YcDyzlzPxvKOw+7XxPIuyca0lveMjIxhy7t2rfBo/QF7jjXU7/k0Wqwxd3z8k/Z3/+9A056x95u4Wb/VnmPVF6dp1c0RQznYrxWq5TNzrdDWu+H82WbYVdPhA2hayuLgpQhYTAu8bctw6vFSlaVsDfVWBetw2LSztaZ3g+e8G2z8sa719rBWKHas/s62u0zr3oB9pFghUgPKEFuN1a6McVv2WwtxJYhVqeKKE8pBgNUZ9Vb3l0aH3jPH3PfHEhcl0x/LzoP01q3aqq2iZl1taxnW363iErVagn8SnISvBTDZZjPOgVVuLbQTJx9+EnesG/N7Yu4aRVsj35Ip4bbA6bc1QeTc8SDFr5Lh1snUNTE8SJk06HmQ3rnXvxvdQiaGA93tF0+f0OjvlpmaJI5HjsKrcEvHKwvHgXEOrM3WQhAHQhsKfx1orNO6NnRaLe+bg2faLDS5Urnp8+7U591+EA8f2IeC1dxwSLyyOltObdtcWre7Vqt/iJ3JG7GsPa9OWYs2g8H2tXnrgj8+HfGOeGIdsXCsaYcKk0c1JZRi7tKx36UjQUAc7Cvs7ersaT2D5wx4ys7MKCoqxJPpzkQpLNp0hb14wm9KccCs9MBP14bMhmnh4NDdVK91DnNnXyEDPeH3FdI7+ijxS53uK7TZCc2KYUpSEnqJt5eB70sh9G1n4NOKQNi6wFnxfnO0wYwM7IQmXpSMuLkTengntL00RB2RyERgcPwLu1GVK1mfK1Qd9Hupwe+Fy4HP1CT6GVCwtCuGOC/I+H5tYzcQv9Q2dsMd+2gzvb319fUSVt/bu3HG1OB+4bhCVUfgsxwvBjy7a9HL3aeG5aDhaDNiy1HRZthdqJ0tTqRCv+idS9Fm7KThEGtPiEI8xCeSQLyyarEC1km/5w8sfunkkinbFv9D65E82vhYShrixKc18CApJnB8LHqnNdc5jY91xy6iH5AS9GHs8kgOnHHArk4oVJ3xn9wa8BwnzUsmH9mYbVgOalejCRSgRp9VDolsO1Ej+tE7+qiuO43od8cuBikSEE0LHQt3tomgZg0N4jKqVduP+03Zu+iligWv3tYsWHk8Bqk2COwEKBKDlH5pY5AS7FkvBql91GQC1w5aC4+NNjzaeCvkkFaQOh/wHGp7R+Az0KEiW/OVO1GT2TOjPoCLUZPR9J966ilxxRQPQW2u7/FWtFGTlc+ZRE1mBBxHTb5jzf18T5z3wUE2eSIT0Wc519E8+BfmCQwY3M/ePfa1ctQyRq8Wmo1tHbaKowGqIesB7CLUJvTCzNsV/oqgqj3wmctWSKFgbVnwmjYQyNjHecf6RQB+9u2ww4Kb8YhntvXGG2+MJVZgP5999hlvJykG/sc0wNmd+nHebYZuhMwUQAQdC/WLSO7OsjZQKXoJs0g4IUtyCFBQhbGbkzFjSLyXZWMgVWMtdBVnqS9hdbs381PNZPCZA34vNS6Z0hr43LGSbBt25X5mCuX350pmCgqowgeV4P2kpSDvCB6h7qeIHlXBn51pB8wyNDRUshM6vM1ZZgrx7BshM4W93YHCr1+UdzyGdRb8BVikoSfSFfukd+zYMZaJKgATzebVLGKyRk6blcsGcQAVqlDYsYiK8r7B711tsGTYlUdy6YBp13PpIFMkAT3iDxc50lK4nzB8tOVP1sIyH81AIjvzfaX9sFX7XDpwHIcZMV3I/mX1z8frSODl7DfNdYQRElOE6Ri4F9s4GsiRBtAMJbXZ8toV/KziVQIpdPajflO6Gw96il2pERBNyyb7F5ZSZ9m/xD1QxIrKLzfGwEL2sXcIXsU+IkQhCp/9PSr7F33R7tWRyaBL2b/uOMpXiFDjU4EVjGAjeimNn0Lkvq6Q54fZVeBzdYumtgU+2x74bHVamPZOj+crVDHfR8xX+JVYwPFAvkJl07o3w+oQMg4mzwljp2VpuM55ewOgsdLR1tqy9FWFqtP+z5/1n8wJOntpwHvaJFOSYdVT7xWbFj9F5aikzbDKT19lWOUjKU1/XBWbDKv8aZ9hFfmuMqw6HAdXc0LDrvCSwwov3jZifQBVBPHR/60jGvcxVSwp8Szts6b10nkvbUsO9RMwnbfaQlkWxM6OHOy71KK900s5odUmiwe+zAmNZQHvJfQqbY748ekSI22jnQCINqOE2eSETklJkW46G7rRZLG/cAFNTaaK/FdQNaI5MSshYduGDft37PAsUWd2YqL+q2lhangA2MK8fmjxC2ALVJ2tKbHBgcez2IvpgfLBBx+oL0HKbhZAfvaznzGxGs+okqJaSGsliz3wUv/9+OOPDWaxFwGBQLThCthpmP1dtRZXUEVJW7Gi9dQpbxA1j/h2xbdaAp5jsbm+MNHGsOkNX0XJi8a4s7MALqW8HpAprK+pbzaenWpom2onbablKlE0QRyYydE7+qgzdD76Uy17/QlTAgLRRVTpAOtIXV3eypXQod275cqGggL+5OhBYCm+hRDck7nMJissvRvRsd3wLFVmiNg+4FLKuKC+FrMwpIzSt9xfwsNu6Skve1pFU2mhaq0ywvEvbOAyE9QfOh/9AcIaZA8ggvq5PodyBqzQgIDU2NiU2FhO5MqO8vLk2NglX3yxd8cOueF8Y+PZ48dXhIa6A6w7Vj/PqqKsoXsjLtMvr+bBY+hEXmAuVv7vOPIKDwBtCBc4gUfmiYsXL6ZmUgB7ZA5Iq2gbLaSdtFZtwkHNwkAonRpx6Hxc+Spq15SB4gxYs6ZPbzpyBD+mmdOmaa8vnjevrqqKk+lvvnkOWXLo0EczZ7oJLPvC5MMj9oURh04+A0lclI8Dv36+HJZuNGJmW8zk3Te1U9uSJUvAgZv10BLaQ6toG3oV7VS8Cr8r7HPSHVeElY8rA4R6q9176BFgpcfHnz95Ep4E05Ir20pLEYVhgYEHdu7kz+jw8Lz09Jy0tHgnNRgGFr82jyvsOkMnHyMrK0thC30FlgADYA7PEdsjfMLZmo8rBcMmWjamc8M18Hb0KlqiWkULlV5Fy8moJR1xceh8XBwgbFfG5uTOgJWZlNTS2HihqSn1XmCFBwUd3LWLP+FnFiUsPd2zwKIXLu7A8VRBk1N8SxvTgdk7Xw6rIxZt1nzQkJytVbviKwybMez4xXt5Oys20h6OWssCnhqKV9k47XkAWIItA0qJM2Ah4E4cPIiwQ+Q5FIVCpxoaEJqeAhbtH2NU2WALfUurVGFvlH1UAA6Wg1FbckiPjW8gbxH/Kt7L22mD7FrT+u2grSu9alSWcJ9RDRC2xNHqW86AhchDCMKughcvlis1W7eivPvNny/KuxDIWx4c7BFgoVe5uczsEZnIPFHZIGTNh9U3lBvUGmZ24r8FB/K2TzP18xbxr+K9vJ020BK1YiOcTOaAEudjVP31Ge0AoZCOysfSqblhzx4bc8PGNWu8YW5Qc8Ax0NZd1OWxM+P8ZP+ZxTcQBym+NNMxdB0+s4vmrs8//9xFAxV1UjP18xYJWct77aE8ffp0sVe5qK27CyyZSMMVXfS0TPGagTQlwqWoWrIH2quWhVENndi3ZM1HG/gASSS+gag4MBLO2bXHh0da4VOAhAIBOpYq/F6c/YuneFZkLrVRJzVTP2/hXeK1p1X+OKdt0khaa2zofIwNEMYx9DhX1HnW9dIiI9esXOlZos5tJSUjvp1VKdrpJSuo4aETuzyFyBcwBhun8ketBdsBrIVpGo40qNJgAqYiG2OAI6o6R/7FUfYiAyw5UdflHu4negfXJScq/6JOaqYqeZGNSz52UUmXJJsjDA+dj+EBwpyP3oDWNeKSCF/3KsYwj9KI/l60irbRwnG4E0Siup35spC52D7sEaYp2AxzfrgLWrYkQITZiLFKNotyBbigazMhAFgcOecK1/mvupOnBE/UQ22yEK4ieaiC1574V6l1QHeGzsfNMRKWME4EjSriTu1BnwVvFIZOfGxE68L53SYEPDZJRJXsIQMuYvoCKOJwJwZxjqjYWKEAFkfO1XVxMOR+MUoJz6M26rTZoo0hFA9jcV0XRuX+0Pl4ZIxQS933wPSUoKEl911PH9XQKa0L6wob1e136UigEVGuwQfTN7iLMCQJeQpDAljCqARY/Jd7uFP+hD9JAA+bmhGFy5YtE/di0ag8NXQ+nhog2YjBR71fCo1Ayt5nf/wXcQeXvRgCL0QSe4udxfSW+FuyrQ9fA2FC5IYQ9sYVQYzEr3JYAzVTP2+R/YCyG8KzrMHH42OEWkMTx9LnnXfxRt77lYOU/dAp7kXBETwhIYEoLp6KwUw91AZTlDgLXoKUV4ClZvhYPpjkc/Tex5bwgvKWCRO5RIZO6V6KhxHTDGGHgUBYketLPdzPUzxLDYo/KV3Ke0Pn49VhYguGZMWx2b/vJn8SPFHzqKJ3fLUKXZNME2fsCioRS0PkMkXjBjHsRiSDDd6qHDnnCtf5L/co5UlbJJOFt4fOZ8yGSRR8AIEVADshV1yZzcpeLu7nKZ4VxXwC48mh7sgyGpNcCcRlrPAsG0auuGCm+YoBy2akZLtVp7UIXHTOuZP7x5WR8z6CTA0dfrzIMjYiABrRzDhyzhWu89/7O3Q+5tcyiwkss5jAMosJLLOYxQSWWUxgmcUEllnMYgLLLCawzGICyyxm8SKwcBnATQenbDwriPwhmSMNFEJ5E5jryJEjVMJGeQM1kA8Rp5FD1pKammo4Axnb4njcsP8aS2w0o7S0lNU66iG2qrF6eHz9+vXURiXsADNWyYYNG1hX5utQCRkYDdTAUg/Rswmjx1I0ldTV1Y22BjziGQ1OSIbKngvWi9jOmpmZabPyawusNWvWcB/LTET/5U9C+RpoPS8jEjCZGtyphIHDoyMqKsqdSig8zjpaYWGhsceBNY7X+A0LOqUxBgropBKOLKgbq4RI42CCpUASQrGWbKwSwE2P8BoljAyfyUAl+G/xUXiWIzGFCSjMyJCx0YYHDQOL+NXEFwgLCyNQEVEAIiMjucjDo3ory585OTnJycn4bOBQZqwSfkx5eXmc8IOgQmOVqMLjeInQNcNcnWTSjCDJAQA638NADQTB51dO3A4cWvhlG6sErkCcHzxFZSiMVQK/CQkJAaDuVMKz/DySkpKQS3zu/Px8GkYqZz2OBbPlVvoPDHEN42jgxcRbh08uXbqUd1MJvkEGKkEi89OkKkaBSgwjgx8JvaAeY4/zM8PJCWwhUoG7McaJMkAN+Ett3LgRgRDh2o5Ie90AcCMK+EZFRUXyexttQSLxLD5bHPlGhoHFkdhJMDwYGCcMsk3CJR+HYlhia+uHbNMv1IAXkVRiWCmh0TwOnzBcyR2rK5g7sWsRf2ethb4wMu64oMh+YiSD4UpwJRJfNCox7JrLYEoDDFciHUFDkJAFfGj7NF7mrNAsprnBLCawzGICy0FhNls/UkGrbdQt3MAM2c1KsIRha3G/EmZDOjUwW37//fdnzpw5f/58Z5WIZc7NlnDPiJWQBVi/EuwF7lfCVMD9SijOnOgdA4v4Na+97/tReLYOTV8UOT99qw7NCkn797ff84/L0aFPg6KiV1fo0PywxHdmzIpNz9Oh4IjY/JJKHeKGf737bl5stDNatmjB49bywuTJlfl5Dilyid/Hb7yRs2SRDsV8OqcicrkOhX30oe8rU7M+nKlDMTOmbZk3R4cC3/zn55OfyXp9qg7FvvLClrdf0yG/FycveGJS5lOP61DMk49v/pserXj2bwT9Hx2wPghOjajr16H5uXX6N3yRv+/D+cElTbd0KH7DHv0bYtfVLgpadrJrUIc2bN+vf0N+aVVUgP/giWPOaE9xkQDr9ZdfdnZPeWZG0mdzbm0o0qG9cdH6N6wNClg5Y9rN8EAdqpvnq39D3gfvrXp5yo0Zr+tQ7T+n6t+Q8eqLxZN+c23SL3Vo56Rf699Q8vSfTWCZwDKBZQLLBJYJLBNYJrBMYJnAMoE1IYHFKtKHc+bNWRCgQ3MXBgZHJevQguAV7//ro/mLA3ToiyWBEXHJOuQXEv6vjz5Z7B+oQ0sCg2MTU3QoMCSUBO2Bixc7I/4rwHru2WdTrBl+7GlpQIDvB7MC5s3ToeAFXyQvD9eh4C/m+74/I2DOHB0K+ezT5OAgHfL3/WTu9HcDPvpQh0Jmf5y8eKEO+c2a+fm0d/xnvadDIf+alTTvUx2K+myus5QCpuXdUjAV3uVYr79ujoa5pGMCywSWCayvObAkyx461jiMYm0C6ysJLLxvw1fELI1Jj8xYHxKb5R8aFRMb58H07iawvnbAwp8wMjomelVFXGVHXOWleGhbOxRb1hQUHsegT/hRIH/TU089BbBCQ0NNTHgMWAmJyVHF++KqOmyAFb+tLaGibVlcRlx8QnxC4rijeIeUYIDo3htvvAGwAgMDk8zicomLi3OWa84HN6PQpNVxVZedASuh4qKFtrYmWuhC4pYLSVvOW6i8BUouP5e8GToLpWw6Y6XmlLLm1LLTqaXQKSitpMlKjWkbG9M3nrTQhhPQyg3HV66HjmVA645aqSFzLXTEQsWHsyxUn1UEHcqGCg9aaM2BHAvtzymA9uVCq/daaU9ePlRnoVW7V1modlVebX7eLgvl7oRW59aszoGqoYLsHVbaXpC1fU1WVXhQCumKbDTLoZHKoAvl9kjl1khlwIXSP1K56ULpG6ncuLewycWpgTQ6Nj5mW5sJrDWZlevWrrMZHRNYxoEVGpUYa0GVCayqm339JrA8BqygCBNYJrC8AKyQSBNYJrC8AKyI6LgY8GQCK7OqbvceE1geAxaBR5bnbDGBFR+R996M90xgeQxYjE5IaHjsptPOgBWTsyk8Mi48Kt4hLY+KG6ZIfYrVoxX2FBPhKkXfpYgRaIVTinr77XewYxET5StqUkq8H4X4IkRtcWogZeP94oDg+E0nbIAVV3ExMr24sKhowpuJ4QrkIwVYDJZpNPfkWiFsMCs7O3hFQszqyoSNh6NWVy9Lyl/sH2wTm2aiFnZvylrhm2++aWLC894NwIshJpQWMXfI+GN6N5jFY24zX89iAssElgksE1gmsExg2RRCti0NXxG2PFKHImMTsvMLdSg5Pds/KCh8RaQOxSQk5hUU6lBqRlZQUPCKyCgdSkhMKlhTpEPpGZkhQUFRGBWc0IL58wRYzz8/uahgtUPKSE0NCQiIDA/XoeS4uMK8PB1KT0gI9fePDA3VoeTo6MKsLB1KjY4K9fOLDAnWodSoyMKV6TqUHBEetnhRZGCADqVGLC9MSdajNKfhqJ3uK3zPLyZka5sOzU3Zqn/Dp2mV73+6ZPW+dh2KWFWhf0N4bvnni4P2nu7QoVWllfo3pBdsDFu4oGPPbme0NSdbgPXqSy85u6coKSFy9sftq7J1aNvyMP0b8hYvjJv25sWgRTq0dc5H+jekvf9u+tTJF6b9uw5tef1F/RsSpk7JnvSblkkP69CmSY/q37D66b+YG1bNDavmTmgTWCawTGCZwDKBZQLLBJYJrIkKrBmLo4PLL+jQnORy/Rvmpla8P9dv1Z5WHVqet0X/hmU5mz5bFLC7sU2HcjdU6N+QunpD6IIv2mp3OaPyrMy7s8IXX3R2z5r4uIhPPmrNy9ShLctD9W/IWbQgdtob5wMX6lC574f6NyTPeCf1pcnn3nlVhza/9oL+DfFTp2RN+s2ZSQ/rUOmkR/VvWPX3UQIL40REVMzySD2KS0zJL1ynQ+nZq4JCQldEx+hQQkpqQfE6HcrIyVsaFhYdE6tDKWlpxevW61BWTu6y0KWxUVHOaPHChU/+9a9sLXzl5ZfXFxU5pOyVK8NCQmJWrNCh9KSkdQUFOpSVlrYsODBmebgOrUxMWJefp0MZCfHLAwNjwsJ0aGVc7LqcbB1Kj41ZHuAfszREhzJiotdlZuhRdhZpKUzLu1nMJR2zmMAyi1lcAhb500gQRcJF0sFxNFYv6bLI0YpTF5UQu8ZADQS9weOWejjJyMgwHJ6EnBS5ubnOEiiMWNAhSBJLVk7iO2RnZ5OvwVg9PE5SP5RXKiFThrFKaAbJIFjJJckew2u4O+S26O7uJgEis7TR1kA2SRKOcFJVVUWKPFzbyca4du3aEYBVai2SwBM/f2Pp+XiZZFjlcSoxlguUZrATgdR+VILrsLEcf3esqVkZPnL8GXt8165duG6TkhRwExaRE2P1kDORZzlKbQZqAI48DkDxnwYfxiohTSFJkHHtJ+wCmyMMVMKA8C3k05DulFzGtIdqT58+7RhYkmGVN7H1gqArxjJwSoZVXkPiSYBlrBKVYZV6wIQ7uUDvWLM28i35dRrm6pJshwbwgyEVpYEa9u3bB7MhdaX0wlglfB04xKJFi9yphFAdc+fOJcOoO5UwpOAblJM7iFaRXJMs1/RRj2MBCzAIonkxzNbYi2E2NJ3EpAwllXBuoBJ+DUgNhDK/DLIFkU3ZGCwIIAMsDCcJRxazF4VjcXExsoy0scZEGB/grbfeYng5Z3gNVMLb+YT+/v5UggOZMY4F30U3QJ5wghu6sUrkp05LkKp8GvK1ghMbXcXHXorxYvCINGQIJJ+ngcLjsECc6KnEWEJReBWu93A+OLbhSigkTmcEDbMrePB2a4H5MzKSXNRYQeOU4XXmw+QK76Q7VEJGNGOVoJkwmFRCd1CV+NDGOiIjI3oevzpyE9vc8/8BNEpQ+r0H9XcAAAAASUVORK5CYII=",
+ "description": "Display temperature, humidity, speed, and other latest values on various analog gauge widgets."
+ },
+ "widgetTypes": [
+ {
+ "alias": "analogue_compass",
+ "name": "Compass",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAh70lEQVR42u2d+b9V0//H799yJTKTIUPGMiXKGJlSxjJkqkwJCSGRhPDVTESKjImKkCZRKZkyD6mIhL7Pz3k9PuvztvY++5zb2WcP9+73D/ex7xn22eu9Xus9r/dq2FLQli3//PPPn3/+uXHjxvXr1//8888//PDDd9999/XXX69Zs+aLEn3++eeflYgLvcJbfICP8WG+whf5OjfhVgU/oYaWOey///4bHPzyyy/ff//9V1999VmsxA25LTfnJ/ihAljNH0y//vrrjz/+GDuSKuKMH+WnWxTImj+wUE8Ij2+++eazDBCPwcPwSAWw8o2nKoXT6tWr33rrraeffvr++++/4YYbLrjggtNOO+2YY45p3779brvttuuuu+64446NJeKCf3mRt/gAH+PDfGXkyJFTpkx5++23uVWVYqx5I6y5AQvb+bfffvv222+j5/WTTz557bXXhg8ffskllxx99NEON7UTt+rUqRO3vffee2fOnMkPRT8JHsCGDRuan8nffIC1efNmvDP8tXJT+Omnn7766qtDhgw5+eSTY0RSNO20006nnHIKPwqOeYByz8Zj8/AMoQBWhmjTpk34/BHC6cknn+zdu/dee+3VmCrtvffeffr0mTx5cjkxRiyDgTCcAljpG1I49uXk09SpU5lI7KGtw0GrVq34u8022+y+++5cHFMiLviXF90Hqqc2rVvfe9JJXfbZh0dCVz733HPlZBiqPO/wamh+kPrggw9GjBhx2GGHNWnW9913X9QWF+ecc45e6devH39bt2595ZVX6l+9wr/bbrut+wDUs2dPaT1uEvETvQ4++Jcbb3yzd2/3Ch4AWnLRokWhA2GA+bXu8wcsokGYIy4UbmnOnDnMeps2bapB0n777Sc0nHvuuVwce+yxEkhXXHHFdtttZ3GjCwes/v37e8i7/PLLJdK4CRfcUBjVTziacOaZAOvGzp29J+EOuJavv/56qHIkBpbHAFjOgEXmJNQ8nzZtGjayNFQ0nXDCCVJhAwYM0CsCyp577nneeedxcfbZZwsQ5YDlvc6HzzrrLC7OP//8tm3bWuTpJ/g5frR1q1ZfDBgAsA7cddfQB+Phu3XrNn369FDTHs+xAFa9nL7QIMKMGTO6d+9eEU/ghtnl4qSTTjriiCO4uPTSSyVXPAnkBE+VwDruuOOILwhGQrZuyM0xpLg46qijTjzxxDPatwdV75dkWzTxhC+++GJocDVHmjEfwCKWGNR9hDTPPPPMivOk2YWuueaa/1jQbdqg7Lg48sgjBTUUmbSnANGuXTuZWWhVlFQ5YGFm6T4YWPvss4/7Orfq27cvF4BJCOY+O+yww/917w6whnTpYh+pHAFQBCfh1qBmhBUFsOIRVMFszLJlywYNGrT99ttHr3tptIsuukiBhquvvlpfEcJAxlVXXcXF8ccfD8gcMnhd1jqacY899igHLN6S6uQmsuX1deQTN9THhEt+bttWrT7t3x9gddh9d2QnFpV0KA8ZMQT5DR999FFQdGU/4pVpYGFYBC2qSZMmSUKUW+vy0ZAcwg0ZGEkI5AehUQkMxSCEMD4A+LhA0kh0SRAeeOCBO++8Mxf777+/BZb+5S0+4D7MFy+77DIuCJh5N7/44otP3m8/UPVR6euoYH2Au+nneOAI65BVMXbs2KDVRVa7ANbWZGbwhjxuLly4UGs9lDB0nDcn4wmjRzM3cOBAYU6TfdBBB51xxhlSUvpA55KnJgFTjhywIgSMuxWiUYoS2PFzo7p1A1h3n3gir1x33XX6gO7Go0pA8vCy1UIJO3L+/PkeQ4imZjYX1JAX9Tdu3DgFKoMkhYVmEezQNQgJiRxJr1NPPfXwww+XttL0S8xIhVVJFYHlkW7ODyGLVlx1FcDqtNdePAYPIykF4LjgUaWpkZoHHHCAG06QGP6ECROCqcZsqsXMAev333/31N+KFSu0psvRTTfdpFm89tprJYGuv/56SS/CRpIlwgQzJ+22FdRUYDki1A6qPu7ff5uSfhSyeUI9mIQoAkwXxCYYTsTdWDyYmJY/X3755R9//FEAK4ooTPC8vzfeeOPggw8OZTGrX0Y3kqlXr15cYHvJWuIVeXaUtUhWRcfE6wos0jgAa0TJvNNjBOUWqSe9hUOg8TI0PXmQDjnkkNmzZ3veYtZMrgwBC0fak/Pjx48PFTBy97CZnKxiucuu4kKVC8gqVj/v7lomIJkYLb3iCoB1fLt27hUeiQfj+SW3kLIKpfLkuuBdhiaLXr5CsDjn8ccf99i1bt26Alg+kaXxUsgwvZyvxEKniEqSSSIK+0N5FewVGVhYKk1NEteDjtpzT1C1ql+/VoGx8Hgyp/AT25Vgh72vVxiUXE482R49epTzf3FKvDQ2bCyA9T/66aefLHdWrlypEFGQlVJ5kItjYX7J+GV6FIaQcskI3XnCCQDroZLiK0d6YLsktEiwwJzcIv8YuszQ+B9//LHlHswsgPUf8sIKS5Ys6VIKT4cWsWAzkULhgqycLHr0CJXBulAWOXZi4u+44w7pXH5UsqRKWtC3L8DqFqbOPOLh5XmgxHVBRFdrhrcUHwmVwV27dl26dKnlISxt6cDyZBUFJB07dgwNKDjDGfkvfcFiVeCH+HWTJrupBJSJIUmKPProo9XD95DddgNVnw8YsG0V2XERA1E4HuOdrA4XRFMV+pIDERqMOPTQQ99///1MYashO3bVggULYJDHMtgq/xwxJkaTd3MKomJiJy5gEUCigALHrUnAurVLF4A15vTTm/qLxEo0Rogxyi9BaCm5CUOC1Yv4ku+991527K3UgOX5gIgEhQeDVQmoBsGInInK9zp06ECNityr6HB5XMB65JFHmFT+PvbYY9UD6+IOHeZdcsnZTZemDErOLFa8gg7gRokp+ZKhSS0Y6EXnU/QT0wEWQRc7/sWLFxObCVo2MjWAkaxaeArIxHGkV5Pi5rUDiwv2eM2dO7dOllxo7F6VXsRckFsysFhdKpqAOUE3BU1K4svyNq34VgrAIkxso6BY60AnyFak/eDBgyWQCCfKeiV+c/PNNyccR3DAwmMgYJsYsJzXglqUNsR3Ob2kWMEcfAi1t5Bw1paH1anE5ZMGFoktm7FZtWqVikwsYatq8vCJEPuCEbFpKlJkyG9TtS3cDIjBCkAg5sILL3TZdIW+YJQz7f+XROrShZCNzfkkn09MFFik4kma2iioTCWPCH6yC09lnEh7JdHsLoYWSK5KTL6hLAfLqGAM2cZOYXvCdRCJAssLWSn+5NlVWp2WZaxUlToVpOi8JLdlEUwL2luYZV6NTfMEFlV7Xh4wqNEwSLGrlAq0jKPIKbG9y1kmmKB6L8scPMRbb72VKExQh44ZMyYtQ74hFdOK5LysUWuqs1FYMp/qF2X4I0R9SybLFtxABL8sBBjoxbcAIt6GNeQT246RELBs4R7lRMFKGLw/7HTFbFhqVBUHBX5BQSOBagg5N8SWicWoCs0Lyi9fvjx5YysJYHmxUPy70BJQVSOd/t84NRXoQbFfoMqaB9oOBLEhETNUpkUwBsFblv/J7POpO7CQvTZqhWkVjNPg5ihZoZCV45ezqwpsBZngmINnrSBfY2k7LswMxvlge8IKsSFJJUg20Ktbd/Y7rEH9iSMowWAtMmylzUGLimB5jGL4waUFo2Qz8AFUgdKplrEi2G47RLD1N9/AYke8FcLBmjXKjNymUwwvoskqGQ2NV7VYVEUMX4zCEyK44ApDYKkrXHPEK3Yu6r1nv47AopWF9QQnTpwYyi9WoUtZgCquy21TKSiUYBdM04JEPxJPDiYzggqRqalrr5E6AsvWWrGdV9EERwSr3EZNnORbbrlFOyPQhhGSCR/ntttuk8bEmCCff/vttxP9QtQTAGsslTGpWDl3xKDuu+8+JWrICWJrMpb7/kvSd+UkmRhCypWAlpalNu567W64ud3hU9e6moZkbHaXlrFElp6lJsDBCBJhzkQoR6xFKkOoQuaa6B/sHj16NOYXvuS7774LvHC/VQqROyLgwtAefvjhxlLtKBYngxo1alS7EgXjnx7BOnaGOccQzRiaLCfolYwVXy9g2a5oNLfw4isEYMQCXidHgfrXvxWLqwAWO1epuYPXAhbWK0hCjNFMlu3C4KzcrqnsA4s2JyAJPDlgMVgGdWpkybz7unOrSUur4giuetygLnLevHm2t1uegEWbQ2snBnvCILSJ5rn0FvhAdFWzTwtgPfTQQ2gKVraARTjnnnvuocIT0xVt+MILL+Q0US1gIb9ZNjBHwHrqqacIvlSZKoWBsNEpTfaNcZ+gDqWk285OnXpS1gVYnDDjnvull14q584gulGREvKspGpqQQUsLh588EFq7uAaX6cXMlYIdgZdPUFbTm1wAauxtOmeoQlYw4YNa9IdVKjNX7CIKghlKdNh+2/VSWg11FtcqcGLdWFYRi6lo8rj6G4+ocBiZdMYUsvxiSee0HYx2v+HGnP5AhYrhDbPAtasWbOeLJHHxgiCmbAUxrogDv964UNEfr2FVvzAsp2xaXwY6v5gaWJFurBCXffYtDRyzEQzgk5colDbAJuhrhU1MQOLKga7FOgLagfDbi3nAKuhFCGDiv5OQU0lVCH6FAfZlZDAdq9HkhVauIexl5jGDCy7o4s2wJ51hbQn1I6B6eCFRku4hLwlECx1UUMuMA/Y4ePtk2NqbEVN7DGtOIFFPYYNtatAO5iRAF7E7iiSqb0DTEERxI4BVCGiy/Un99Y5b9lAfLzlNHECy27qIuXpxa4Ak9sz3ljad8rAMtVnoTkRjIW9zsyA7TBfbeisxmTjXZ3qS+MEli1kIGIZHC2ZLIUYCjwlRlgdxB3oDxDaEGro0KF1KnmIDVgkB+z2G8/Rwwd2q0eq0LVgKKhOBHthMqx20SymwIvssMLtZp4YTfjYgLV27Vr3fJyOFLpukFUugtWqRMX0148sh4nCw39SPcHIDoH+ehSXxgYse5Zpb3MOkecV4uXiFSa5Qb6FYwsRhZ9EALncEUM4jLYiPlvAsnqQw/i8vSI8Oh6ifZHMaOodHFsCwWSbhGYKWPOqDXFEUN6enxhXvUM8wLLbJUiwBEdIhoEyKXILyKoqT+cqKC5CUSC3iJeiDduZVqiOSHXHrg0bYvcHPT3onWvKAqLZehHBSoxgNZDyime8SbHakKnMCrCocLX+oFcpyr4RNqASRKGsr4UXradLMJ8pYCKYDrcnSsReapuIi6VkOQZg2bgoFSyhoyJYijTGK1Q9cTHNSRIMh+0wnykI7mgVUUbhJpFu+5kAlm31Qc21J644Xc0aVTiDW302REFbRzDc+uBMB5PiCS22DsTbd7kh3kCDF3/D3aVYFBXOiuEv14U2TNHYogJA52vQSs4L97CX2k0iE5o+sKyBhdfqtfrwXF92pVZzdGVBsRNsh/kRIR6kmg061G5m1QqsjRs3ljOwiLarYINtTEU4NCPERFB2SyiLqfE2h82cOdNNJdOaMrBsBGv48OHBkSDD0Og4I4yEOEqBsLTwpJQaE8F0hCoW9g3EGM2qFVh2m5e3oS/49An0ZC+oHAWZ702QjWbVXqzcEKPlroOTHJGfGlAiajbwQcodY1lQYsQUMBFMh+bFO7CIutMY7feagEXNoXuU1atXl+vmiH+LXiciX1Qhp0gwn6waE1Eupcb02RKaGgtKawKWzT1r65J1bun2QWP0YkazSWRvmSAvt2Y3SdeYjW6IyyV8+umnPf3NEkHYEjVB6vKXHb0JHE9SUDmC+UyBmw6mhgnyzKxnn302LsewJmDZ9lcjRoyIHhgSuIiOpkgwv2JdCZ0j3IQyuakBy2728pq20yAA/5YiGSx6cpxFlCE7cQemg0lhapggnZztiLqmuDaE1QQsu+mZXRLeGMh3EoJDkfP09IRxfTILSouYAiaC6WBSmJpgQhrrPq6IQ03Ass0/qmm1k0fiIDtaaHDmjzv8h391ul3zI7pFuAllclMDlj0Yxwax1AhZRFkfq4TOvnmslqFBkmrfSPivKxEXqmq8++67czccpoCJYDqYFDdBdkuLDWXVWPFXE7DWrFnjniMCN/gjbEWKyE9nFlU0NcDUWBcgXmTrcO6wxRQwERH2Lnt43IQyuakBi/PK3HPYTYJN3drFeNR/myYiOvqRLGm66oZflwG7rgzJcQme35lWUMp1K21S/Yg3U9zHHkaXGrBspwZbvkfqgJAJKU96ByhwggPSvn37csPbZZdd6M/GxQMPPMDZuFzQQs11eEqFMKTQBesiiQ/MmDEjC8BiZcIxdSuFmRGqUHpQ9gk1ykyTTbVRV2O7OaQGLNu+tsa9N8888wxIYofP2LFjGerzzz+f7lRhpDu7qhzxAT6WEWCxErqXqJZj0tgqbXsbpQYsW4FvNTc2YL9K5AVL6fXAi/xldyUXqZsvOqm7IsH9jACLYri+JbISCyZXnAibwGUS7Zw2B2ARr6OfFvuku3btykXoyatJEoipiCqKlrIDrClTpoRG23MJrBhVIcYBjdpZbdyH9t3Bc+cKVRgNLIoA1K20ljb3WVGFcRnvGaQqjXc6eeZoUEwBE6EkNFPDBGXUeI8r3JDNgHvFcANrOnXJWiNlNNxgA6QRAimnAVIciOgA6Z133pnHAGlE8RLtsjIRILUpHWsDNpuUDtgKTekAuNyhqpqUDsnpTKR0bBIah66xORKxdaKgK1eu1DC5wK7KuwYsR5Q/ZCIJXZTN5A431ZfN0DmhKPQrqFrKR6GfLU3mHJvoIRWlyelSnkqTi80UOaJqNlPQlTgTmyns9i/C5fYRi+1fGSemJrj965133snE9q9iw2qOCOYzBfnYsOptsfdw47bYI4E5Z9BrpVxQ8hS9xd4GsVLeYu81BaGrhGdmeQMrmoKkSBWbgnDKX4aagtg2Rhz3HRxP0cYoI1GGim2M2HKcoTZG1jF87bXX7IMWjdeyiTCmg0kJNl6z/W3Tb7xmW0Viv0c0ri1aRaZIFVtF8hbTl6FWkZ797p2JXTS3zQ5FN7e1J/nGcqJOzO24hwwZYh+3aMedBaqmHfcdd9yRuXbc9gABz8xyRL4TYYZep2CjOEAgYarmAAF7PnRWDhCIPvKExVEceZIFyt+RJ1v+fUhTnz597EMXhzSlblrB8OoPaYrrAN/4j5WbPHlycHjFsXIpUsVj5dg6ltFj5byDML0GycVBmGlR8CBMlreXIMn0QZhe0MF7dLt0iqN7k6Rqju4lk5vdo3s9bUiDCu/pddg4ZmNx2HiSqAoeNs4+HO9j06dPz/Rh41YbBvv7sG7YZevkVs+ePbG37FbEgmIn2Dtw4EBY7eormQLveDbWuS2V2bx5c+aA5fmGnH8XHCoVP+y5IKASXDcF1YmkKyiVCY1LDx06NHZ/MH5g2UjpokWLvFgcS4dt3c7RZfVgihUIqxPBWJo4OC0B24lgMQX2MxTSLF682E0Z05dRYFFzaLs5eKeOu+goViRyC4uyiGbVlQATKxl4ObPdC1Db8BUTV2PJaB2B5W0Ie/PNN72RoOzJg+IVuoINwvRFvXLsBEtd/oMLFS95hX5MjU3j1LjZq+7A+uuvv2xvIw6EtYPp1KmTgxTLiJJFclhOXFdDBClg0+jRo/mLqh00aBBhGG511113wSlMUYyJAlhgCEFFRNQV9MF2mG8/Q+8/27EoRrO9LsDytkeHdnzEByaOxQZX5xXS3qlKlmElsIGRuB+nmoPLYcOGkdvu0qULvbXoSkI2CcDlFxCwhTWDQU2iArfa9atlx0D1N3HMhEuoQkLToSFDGgXEWIicBLA2bdpk4w40QvZ8YAINLpoFB3GJPR84gqh+JO7So0cPeQbyMQmPURVNuwt6eDRpDjJFJFtoEoFhREUe8GK1jBkzRv1qm9RWFGbCYdcaGFbzr5cLsQcFQExZDoDlNQt5+eWXQ4saWEaChVQh0rvK7aysSKq+WHCHlog5YAIQV2CLw6vyW+wFHxDw9FYQCADWU089pX61VWYpYKAMKf4izokyhLKU6aCtnJsgtsPUAwN1AZYXLEXCe2OjlJRl5GINbHbDoq8me0iqS2erwDWUKVqVVf7II4/wyqRJk5iJXNtG5PIAFmumV69eAGvatGnqV1uuiMojGAgbYaazq2Cy+9eaE/UWV/UClrctjE3SnktCZlRiDJYBEfiofysKLZg1btw4RBRM79ixI68gsXT4AB2XvfrVfBHGNea21D0VIgCLoVX/dcc6srGkbhRigKtewQyv2+3O9bCu6gsshJZ1DxlqkBfUnbHCFDKFBZiZQdkWSs2y8Ia1hzbXmiFnDLBmz56tfrXVsIXPYFpofWLI4i+HxnF43TqDcdUyJAcsL6a1bNkyyhTtCEmLIpPFCFTALbfcwkqVz9iSC00R4U3NzcMufQUDA2dZvjYvwl5vgxf/Ll++vH6xq4SARYWr7X7LqROhfGFnN3JLERdMb66L5HSTCHbBNHkt+I/4Q17lsaOJEyfa3rWxlCCnACxow4YN1k70clUQgXi30xDH2DEo1A9q4SXz5ZzrxtJ2c3ScjM7G0i5CzFbvk6hXOxdMTV2nvr7A8koeFi5c6AVUHLMI3hDNk0jHkZE9bokmdNjmLRZbDBzvBOnuvQ6j5PfxAeLDzhrzGIVUs/nmeAsZ0gGWZ8VPmDAhGIgnseOkN04NDrauXWAQVLGxLMjWFkUeExxziAw7qQ8bvUbIAhlsT8ZmTw5YXnFpaOGyM6oQ18QDdQ28OnfuXKCqHLZgjluBpDeIiDqX0PsW+VPL/xjLRFMGlqcQ8RCD50cShqGQRkEXGIRalIQvUBWBLdhF6EvyiSQE4dBgKBVXccWKFXF1b88csEie21KtOXPmeLkXIg6q9MAaJeilZGKBqorYIsFFBkImPAz0utuhLileskVXsVcxpAwsr74UGj9+fNASJ/I5ePBgFQB6Yr+W8x2bDcGEoHlAgJDwVbD6CPaSpbA8j7dGNCvA2vLv9iEQdQ0eL8geykTwBD5HRBeocgYTzPFYBNOCRd7Ebiy3Y2n1kVFgUfxqj99hf0jwPAuPZfDLlVjhWqe1G5EQ0f0lSqvelYG7EAx+n4zUCFOBOJbdfoNpFW/lcbaAJWPLhuPZg0vXEI8p2FiaP9KIWPQyTgnSyJxndSYfzaKghUciDfXKK69UWWsQYwRLUhzJTTpVr7DYtFmepwrmYTml1h3+oyA7lb0JT3TSwIL++OMPG9launSpl4F35jwJRCXtKZXRUTwYGUj45He6IquoIkxlTxGDdSkvgnzU6EmA3XzzzaG5L1KuH374oY1awfDkZzkFYEF0YLLqn6BwMADBLKqKAYdZx9GyUvGoVbZ11llnJakWwTdBbdKdSZ4mxwAZplKoZGy0nDA3qQqRoxMEOk4i6Y20DPb0gQVx9p8d//z580MbslFiC5ik+4gHcvCpoCbjDJAlcIwKDtfIkSO1Y51SFntMfP1wrPVDYaPEOfEXBZZhBeaBVyoioowWNlquwuS05jc1YHl1NdCCBQtATFAhCjrUJykRxkpFNQhqXCTTO55qRCpUOS8IhZiMgNQYIWSV9CBKUIkv3g2exoDI91BV16qYTAMLwge2vFiyZIl86WDOB1dI1wQpZFtQK6EPs32g+n0+tdg6CZjtDERbS9B37BnR0nLmOUwItav4sM0xJx9cyBywgsEtbHmcmtB5lfWK6JKKRIpIgOmoRC5yvfeVh5dNierXBXktZSN4S75LqNeCGLPWeu1nWDYTYAXl1qpVq0LjW+gF6rdc9E9KkOiOipvJwsryyF0/CD0wo5AVhUzStlunEBWXCg2ywCjYlSlZlSFgQWvXrrXcIbiHbVEuXuWUIIElQZDJUKrfTk/G+2/xeNJrOHoKSiGD9QpBO2VLsdy9Q1DtMgN2Ngqaul2VRWAF/USIUtpQFww8Oc4q6MCeVVm4RA6VVUShMHO8m8GelDwSD8bzOyWu/Tm4n0ozKEyldeXVrYsY7NixYz12pegDZhpYim/Z2KnqICgICZ0eVrP2X7C4VYmLKlSLG0xgWb5Ud8ldr72zjc50qPEmegweCWNRolcBPESU3qIiTSEVhhYaN1a0BbZYLsG0tOJV+QCW4vI25wNRToQhFZHGsXJLZi8XClIgt2SsCBOEyrZ6q3QtwOJHFaVDIOnBJKvU46SxtPdLF4qzR9wK1U9Bm+UP7Eoltp4zYG0ptayxhYGuy7fXndwGI6QyZG/hMKrXPHJLBgoZIa1+NI7mVbW8TYrdNxVYurn7IX2Xx9BObmSVbHaSCnL9iIUKfOU2KbFdwFYYu3a0iZVY5R5YqoPwXEVFucr1Y24s7SRWnIkpdPaWBJjqc5S7lRcm7x1BqA+oyCk6iF8RWPq6boUEUjQEYAlA/LSELnFzfUB341EJKzSWdhR6nYYssUJokugxhLBCwjULuQeWCLvB1p2K2BkcYTBpo6YMYZojylIWHLFaFHvkX0WuhTMkgaoG8CuFM4kZBJ70pgSJA5ZcB95SVFYfVrsvCR6JHN2cX1eik3ZLyvHhA+rXuZv8DLdxN5SQZxRFekyALVkzqvIELJXZBNUi23kJRkRvtAdD8qeYaelQJlIizXleEhUEGDXlHA1iK5/Y/SKICE8OWPrLWzpTmZtI6+nrwFetlPiYZJh+jmv9HJpaKpvHi+7fxAAxFj2LSsVV2VR/eQKWiI0lnrcIzZs3DwVRsTbLaU9NMLMlJQUClHrjX2FUyCCkJJnn6gpDgeXwx4cVmNXXuZUiatSZCa/cX3XDoERPG6HQndxlaHST84YME5LZY9NSgCXRxTbLzwI0c+bMauCFnBCM3HyjkmSKCRAOMeRPVJPpLKpQYLkLDpWUbYRnoMfQDbm5U8EqZuQBXD+0aFlLU7HgSBFUCewHbHHAErEx3AtGiOgpRWSomspSZlcRecUkHQ6Ycqk2aiikQ6sEFh9W1RSqs23bthap+gl+rlwzBS8QT9SNdl/B0THkeu+Ib+nAksNI/ieoGaG5c+c6R68iCT3IFeUfkVLKYaO5nHcZCixPwmE8KbXnRB03lCwMDZqHupNYXbaHsdV9eMd17d5RAOtfhFKwvd28+ogRI0YES7sqxsSFBte5xOFGtpQDlmd7ua/w9abG94lE0JAiGEdwXdFypPuaCbAqwguigxmGlNeGpElJYtnRusMxJVJ4Qjp3q5PceJTEJuzpSB5hTdapg2MBrCYQc8DiDlWO2ghEb1Ls6NBy3iQJf5PHoAzVng/oKT4GkndINR9gObeRopFgQNXS66+/zulRHGuQWHNlqjPI4XBG/KxZsyIejMfm4ZPfpFUAqwmmPSUSoYEJS6tXryZOgSmGCEHBxbh/H0uLrA635ebgmB+KfhKSfXh8mc3MFMAKEWDEEu3G6wiiXI5wK23iR40aRWKRDA87F8CHtvyTlnHI44J/taWdD/AxPkwNBV+cOnUqDYm9yrsIPPF42Q+gF8CKMvCZwmBeKBVClPIw+fX1CmCFENEgtCRhIXt8dQKEcOJH+ek8hqMKYDUZZBs3bkR4EK2IHWfcEOeOm/MTLQpMBbBCTH7UEzhYv3493hmw4DggtOeaNWu+KJGLZXChV3iLD/AxPsxX+CJf5ybNzwzfOvp/rjTPJr3CXzwAAAAASUVORK5CYII=",
+ "description": "Displays latest value of the attribute or timeseries key on the compass. Expects value to be in range of 0 to 360.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 6,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueCompass(self.ctx, 'compass');\n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-analogue-compass-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"minorTicks\":22,\"needleCircleSize\":15,\"showBorder\":true,\"borderOuterWidth\":10,\"colorPlate\":\"#222\",\"colorMajorTicks\":\"#f5f5f5\",\"colorMinorTicks\":\"#ddd\",\"colorNeedle\":\"#f08080\",\"colorNeedleCircle\":\"#e8e8e8\",\"colorBorder\":\"#ccc\",\"majorTickFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ccc\"},\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"animationTarget\":\"needle\",\"majorTicks\":[\"N\",\"NE\",\"E\",\"SE\",\"S\",\"SW\",\"W\",\"NW\"]},\"title\":\"Compass\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "temperature_gauge_canvas_gauges",
+ "name": "Thermometer scale",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured widget to display temperature. Allows to configure temperature range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueLinearGauge(self.ctx, 'linearGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-analogue-linear-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 30 - 15;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"defaultColor\":\"#e64a19\",\"barStrokeWidth\":2.5,\"colorBar\":\"rgba(255, 255, 255, 0.4)\",\"colorBarEnd\":\"rgba(221, 221, 221, 0.38)\",\"showUnitTitle\":true,\"minorTicks\":2,\"valueBox\":true,\"valueInt\":3,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"colorNeedleShadowUp\":\"rgba(2,255,255,0.2)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"highlightsWidth\":10,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"showBorder\":false,\"majorTicksCount\":8,\"numbersFont\":{\"family\":\"Arial\",\"size\":18,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#78909c\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":26,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#37474f\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":40,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#444\",\"shadowColor\":\"rgba(0,0,0,0.3)\"},\"minValue\":-60,\"highlights\":[{\"from\":-60,\"to\":-40,\"color\":\"#90caf9\"},{\"from\":-40,\"to\":-20,\"color\":\"rgba(144, 202, 249, 0.66)\"},{\"from\":-20,\"to\":0,\"color\":\"rgba(144, 202, 249, 0.33)\"},{\"from\":0,\"to\":20,\"color\":\"rgba(244, 67, 54, 0.2)\"},{\"from\":20,\"to\":40,\"color\":\"rgba(244, 67, 54, 0.4)\"},{\"from\":40,\"to\":60,\"color\":\"rgba(244, 67, 54, 0.6)\"},{\"from\":60,\"to\":80,\"color\":\"rgba(244, 67, 54, 0.8)\"},{\"from\":80,\"to\":100,\"color\":\"#f44336\"}],\"unitTitle\":\"Temperature\",\"units\":\"°C\",\"colorBarProgress\":\"#90caf9\",\"colorBarProgressEnd\":\"#f44336\",\"colorBarStroke\":\"#b0bec5\",\"valueDec\":1},\"title\":\"Thermometer scale\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "temperature_radial_gauge_canvas_gauges",
+ "name": "Temperature radial gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display temperature. Allows to configure temperature range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 6,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-analogue-radial-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":60,\"startAngle\":67.5,\"ticksAngle\":225,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":-60,\"to\":-50,\"color\":\"#42a5f5\"},{\"from\":-50,\"to\":-40,\"color\":\"rgba(66, 165, 245, 0.83)\"},{\"from\":-40,\"to\":-30,\"color\":\"rgba(66, 165, 245, 0.66)\"},{\"from\":-30,\"to\":-20,\"color\":\"rgba(66, 165, 245, 0.5)\"},{\"from\":-20,\"to\":-10,\"color\":\"rgba(66, 165, 245, 0.33)\"},{\"from\":-10,\"to\":0,\"color\":\"rgba(66, 165, 245, 0.16)\"},{\"from\":0,\"to\":10,\"color\":\"rgba(229, 115, 115, 0.16)\"},{\"from\":10,\"to\":20,\"color\":\"rgba(229, 115, 115, 0.33)\"},{\"from\":20,\"to\":30,\"color\":\"rgba(229, 115, 115, 0.5)\"},{\"from\":30,\"to\":40,\"color\":\"rgba(229, 115, 115, 0.66)\"},{\"from\":40,\"to\":50,\"color\":\"rgba(229, 115, 115, 0.83)\"},{\"from\":50,\"to\":60,\"color\":\"#e57373\"}],\"showUnitTitle\":true,\"colorPlate\":\"#cfd8dc\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"valueDec\":1,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1000,\"animationRule\":\"bounce\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"°C\",\"majorTicksCount\":12,\"numbersFont\":{\"family\":\"Roboto\",\"size\":20,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":30,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"unitTitle\":\"Temperature\",\"minValue\":-60},\"title\":\"Temperature radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "speed_gauge_canvas_gauges",
+ "name": "Speed gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display speed. Allows to configure speed range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-analogue-radial-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 220) {\\n\\tvalue = 220;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":180,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":false,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":80,\"to\":120,\"color\":\"#fdd835\"},{\"color\":\"#e57373\",\"from\":120,\"to\":180}],\"showUnitTitle\":false,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"minValue\":0,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"MPH\",\"majorTicksCount\":9,\"numbersFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"size\":32,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\",\"family\":\"Segment7Standard\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Speed gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "radial_gauge_canvas_gauges",
+ "name": "Radial gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 6,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-analogue-radial-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":10,\"highlights\":[],\"showUnitTitle\":true,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":10,\"valueInt\":3,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":36,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"minValue\":-100,\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json
new file mode 100644
index 0000000000000000000000000000000000000000..4930219741dec77de3d790884fb8168691a4f20f
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/cards.json
@@ -0,0 +1,222 @@
+{
+ "widgetsBundle": {
+ "alias": "cards",
+ "title": "Cards",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAT5UlEQVR42u3d95MUVffHcf86/UEtqwxVapmzaH0VE2YR82PAgBkTKCYUAyrGR0mSxERwBXRV0EdFRcAc5vvaPnptZ5bdZWbDwJ5TW1s93T23b5/7vufe7t3PuXv8+OOP36YNZrzUqCzdNUR37fHdd9810gaz4qV01xDdlWAlWAlWgpVgJVgJVnoqwUqwEqwEK8FKsNISrAQrwdqlwfrtt9/+V7Off/55gIJ++OGH8m46bOvWrb/88sv4Aeubb76pu8vb5z/++MPvEaqPwocL8RUrVqhwfc8rr7zSYdsNBFZvb+/hhx/+n79t/fr1ree8+eabn376qY03KrPx2muvff311zaeffbZd955Z/yANWvWLF76v8psPPLII19++eVdd901QvX57LPP7rvvvqGfv3nz5ldffbXfQzfffPPy5cvre9zC999/P4JgnXfeefU9n3/+uX6pEkHMxo0bL7vsMh5UCU786quvfGXSpElz584VwByNkvUtX3nrrbdsRDmrVq1auHDhtm3bdr+h8LHHHpszZ05s//TTT9EbN2zYwG+LFy/mH+OACMGBf/75Z5zW09Pj0Pbt2wsxuuimTZvi46+//qr3vvvuu3H+hx9++MUXX/g6D3/88cdxzkcffbRo0aItW7bER+FHCa5VamW0ef75588555z4iootW7Zs5cqV0SLA0hyuUmJHAUutHOo3pnQElqpsq8xt2DNjxowzzjjjySefPP/8819++eUPPvjAxh133AGpJ5544plnnnHDp512mtNUa/r06QsWLOCOKVOmPPTQQ7rXDTfcoJCHH374+uuvd/KECROaRs/dDCwOvPjii21Mnjz58ssvF8JPPvnkK664wsYll1zCAw5xpvD21FNPcazRR5fj8xdeeIEbuff333/nYWXedttt/On8s88++8ILL5w3b56jV155ZQxbPKxMNJh+QEdRgVGJQ1pw5syZTnj77beRfdZZZ6nk7bffrtgAK/b4CowKWO5UUUoWPlxlOME69NBDL67MtQOsl156yYYa33rrrTbUbMmSJTYCLBvuVhe0EWDpYVwT0w5u1VduvPFG96AP6cfjByxBJToVkmysXr0aTwLGUUcdJbRzztSpU5cuXWq0uvba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",
+ "description": "Tables and cards to display latest and historical values for multiple entities simultaneously."
+ },
+ "widgetTypes": [
+ {
+ "alias": "attributes_card",
+ "name": "Attributes card",
+ "image": "data:image/png;base64,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",
+ "description": "Displays one or more latest values of the entity. Supports multiple entities.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
+ "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"| \" + dataKey.label +\n \" | |
\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasourceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasourceTitleFontSize = self.ctx.width/12;\n }\n datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}"
+ }
+ },
+ {
+ "alias": "html_card",
+ "name": "HTML Card",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAATFSURBVHja7d3tUxNXFMdx/u7vEkAJsKQK6bilKaBV1Eh5GGNaSyq0WodhRuqMtQJqW5UHCwKGMRJiQpL99cWGEGaaTjtjgaTnvNpz7mYnn8neu/duXtwWFbdWlxs8Vrf21VJcy5TU4FHKrBVbtjJqgshstayWmgFSWm1ZVlPEskEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAG+T9ACrlcLjjyc7lcWflcbfhBfnByOZfL5cpSKZfL+f/2G+WAJ/8dJAHh4GgFeK4BaiOjMYDtyskzAIvSQ+BtI0JmKif3NRBk3PM8Lwwhz/M8LxtAzgf30RoNBJEkjUOk0j4GwJok6XaDQ8LwtSSVXVrP1of4v1w978bu5YNh4fHlfnfwfpDo15Hevm/eHkCyP8Tc/rGV44d8FaLbl/QCRrrqQopXg251bkdSdjBIIpuSNAtAbwXyqgsA595Hh3Sm0+l0Or1QDzJxLWiYhCftdSFJoKMHiPnyvwTOhIHeD9KGA3SfIYBkOsGJtIPz/GNDauIvIfEFGJdKnZwtOPUgOw4kfT0E1vQMmPb12IEZaRJCz1RKBpBJCG+ocAVixw25XArTUdACTBbqdvY56MhLisK8xisDXRyiUi+MStoDnqjUCT9Keg3Oh48MCcXj8Xg8PlwPMqQE/Kzr8HK3LiQBFyozhaI8uClJ8+AUi8CD6qi1DYymUqkksH7Mnf0zLcNIvo2In64LGYXhauLCHUlaAnb3gIUq5FXN7//qmCH98s8RmoXv9KYuZBI+ryZ9cFsKzsvngUdVyAowcDGIjWOGRKRpaINNrdWF3IUeX9LS/PymLsMlSZqCLikM01XIe+CnE3ogRqRtAE9arQtZCVqynbCoWXBWpfddMCaNgLsnLQSj1gVwM5Je3/RPAKIBYPYIJOp5nud5dw8uFIPQxO1eCOf1oRs6Jm71QOtG0FMiydFQAHkKnJlIjTgkTwIyB87uEUglbh5c6G13UGhdkvSyPUicB5J0HYBQe/Bkv+tUPnr/JCDZVq7obyF6d6MNnC+COdTmSCs4A78Hs5dkG3zyYqAy13oec8CJ/XZ6l7qFzfW9apJ/80dNsr5V2yNyGxt7tmY3iEEMYhCDGOTEIJnD6YtBDGKQpocsDrvR5K4kyX96PeoOzu5Lysbj8b2Zvv7SkepphgwBcD4vqRAPVlXR91IauASUVAhWgny6e7ohtEdCwcpdCaDTBYYDCED5sHrxdEOuFZRx4Ya06cC3ZS05sKI04EzM3demA1NlLTqweuo7ewI86U7lHdYQzCgNzEnSHXB9SYPw/amHpKBPGoFzqVQqFYUxpYEX0pHqeINAav4wvXoI+eyweq1BIDHorry+nT6E1FRnGgQyCtFqUxVyo7baGJBHlVG4mFivgRxU9xMbjQIpR4HBqYRLz84hpBwFhqYSLu5Og0C07VZ6dXTvEHKk2iAQZZOdQHcqX3NrSdlb1WrjLHXL26/T/j+s2prdIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMY5PRAmmaD4ObYsvndVst+c2yiXWppjm3NS/oTe0OjFEeU1MMAAAAASUVORK5CYII=",
+ "description": "Useful to inject custom HTML code. Designed to display static information only.",
+ "descriptor": {
+ "type": "static",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n var $injector = self.ctx.$scope.$injector;\n var utils = $injector.get(self.ctx.servicesMap.get('utils'));\n\n var cssParser = new cssjs();\n cssParser.testMode = false;\n var namespace = 'html-card-' + hashCode(self.ctx.settings.cardCss);\n cssParser.cssPreviewNamespace = namespace;\n cssParser.createStyleElement(namespace, self.ctx.settings.cardCss);\n self.ctx.$container.addClass(namespace);\n var evtFnPrefix = 'htmlCard_' + Math.abs(hashCode(self.ctx.settings.cardCss + self.ctx.settings.cardHtml + self.ctx.widget.id));\n cardHtml = '' + \n self.ctx.settings.cardHtml + \n '
';\n cardHtml = replaceCustomTranslations(cardHtml);\n self.ctx.$container.html(cardHtml);\n\n window[evtFnPrefix + '_onClickFn'] = function (event) {\n self.ctx.actionsApi.elementClick(event);\n }\n\n function hashCode(str) {\n var hash = 0;\n var i, char;\n if (str.length === 0) return hash;\n for (i = 0; i < str.length; i++) {\n char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash;\n }\n \n function replaceCustomTranslations (pattern) {\n var customTranslationRegex = new RegExp('{i18n:[^{}]+}', 'g');\n pattern = pattern.replace(customTranslationRegex, getTranslationText);\n return pattern;\n }\n \n function getTranslationText (variable) {\n return utils.customTranslation(variable, variable);\n \n }\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-html-card-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"cardHtml\":\"HTML code here
\",\"cardCss\":\".card {\\n font-weight: bold;\\n font-size: 32px;\\n color: #999;\\n width: 100%;\\n height: 100%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\"},\"title\":\"HTML Card\",\"dropShadow\":true}"
+ }
+ },
+ {
+ "alias": "timeseries_table",
+ "name": "Timeseries table",
+ "image": "data:image/png;base64,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",
+ "description": "Displays time series data for one or more entities. Data for each entity is displayed in a separate tab.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "latestDataKeySettingsSchema": "",
+ "settingsDirective": "tb-timeseries-table-widget-settings",
+ "dataKeySettingsDirective": "tb-timeseries-table-key-settings",
+ "latestDataKeySettingsDirective": "tb-timeseries-table-latest-key-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":null}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"displayTimewindow\":true}"
+ }
+ },
+ {
+ "alias": "html_value_card",
+ "name": "HTML Value Card",
+ "image": "data:image/png;base64,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",
+ "description": "Displays configurable HTML with ability to inject values from the selected datasource. For example, display single or multiple attribute values.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.varsRegex = /\\$\\{([^\\}]*)\\}/g;\n self.ctx.htmlSet = false;\n \n var cssParser = new cssjs();\n cssParser.testMode = false;\n var namespace = 'html-value-card-' + hashCode(self.ctx.settings.cardCss);\n cssParser.cssPreviewNamespace = namespace;\n cssParser.createStyleElement(namespace, self.ctx.settings.cardCss);\n self.ctx.$container.addClass(namespace);\n var evtFnPrefix = 'htmlValueCard_' + Math.abs(hashCode(self.ctx.settings.cardCss + self.ctx.settings.cardHtml + self.ctx.widget.id));\n self.ctx.html = '' + \n self.ctx.settings.cardHtml + \n '
';\n\n self.ctx.replaceInfo = processHtmlPattern(self.ctx.html, self.ctx.data);\n \n updateHtml();\n \n window[evtFnPrefix + '_onClickFn'] = function (event) {\n self.ctx.actionsApi.elementClick(event);\n }\n\n function hashCode(str) {\n var hash = 0;\n var i, char;\n if (str.length === 0) return hash;\n for (i = 0; i < str.length; i++) {\n char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash;\n }\n \n function processHtmlPattern(pattern, data) {\n var match = self.ctx.varsRegex.exec(pattern);\n var replaceInfo = {};\n replaceInfo.variables = [];\n while (match !== null) {\n var variableInfo = {};\n variableInfo.dataKeyIndex = -1;\n var variable = match[0];\n var label = match[1];\n var valDec = 2;\n var splitVals = label.split(':');\n if (splitVals.length > 1) {\n label = splitVals[0];\n valDec = parseFloat(splitVals[1]);\n }\n variableInfo.variable = variable;\n variableInfo.valDec = valDec;\n if (label == 'entityName') {\n variableInfo.isEntityName = true;\n } else if (label == 'entityLabel') {\n variableInfo.isEntityLabel = true;\n } else if (label.startsWith('#')) {\n var keyIndexStr = label.substring(1);\n var n = Math.floor(Number(keyIndexStr));\n if (String(n) === keyIndexStr && n >= 0) {\n variableInfo.dataKeyIndex = n;\n }\n }\n if (!variableInfo.isEntityName && !variableInfo.isEntityLabel && variableInfo.dataKeyIndex === -1) {\n for (var i = 0; i < data.length; i++) {\n var datasourceData = data[i];\n var dataKey = datasourceData.dataKey;\n if (dataKey.label === label) {\n variableInfo.dataKeyIndex = i;\n break;\n }\n }\n }\n replaceInfo.variables.push(variableInfo);\n match = self.ctx.varsRegex.exec(pattern);\n }\n return replaceInfo;\n } \n}\n\nself.onDataUpdated = function() {\n updateHtml();\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n singleEntity: true,\n dataKeysOptional: true\n };\n}\n\n\nself.onDestroy = function() {\n}\n\nfunction isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\nfunction padValue(val, dec, int) {\n var i = 0;\n var s, strVal, n;\n\n val = parseFloat(val);\n n = (val < 0);\n val = Math.abs(val);\n\n if (dec > 0) {\n strVal = val.toFixed(dec).toString().split('.');\n s = int - strVal[0].length;\n\n for (; i < s; ++i) {\n strVal[0] = '0' + strVal[0];\n }\n\n strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1];\n }\n\n else {\n strVal = Math.round(val).toString();\n s = int - strVal.length;\n\n for (; i < s; ++i) {\n strVal = '0' + strVal;\n }\n\n strVal = (n ? '-' : '') + strVal;\n }\n\n return strVal;\n}\n\nfunction updateHtml() {\n var $injector = self.ctx.$scope.$injector;\n var utils = $injector.get(self.ctx.servicesMap.get('utils'));\n var text = self.ctx.html;\n var updated = false;\n for (var v in self.ctx.replaceInfo.variables) {\n var variableInfo = self.ctx.replaceInfo.variables[v];\n var txtVal = '';\n if (variableInfo.dataKeyIndex > -1) {\n var varData = self.ctx.data[variableInfo.dataKeyIndex].data;\n if (varData.length > 0) {\n var val = varData[varData.length-1][1];\n if (isNumber(val)) {\n txtVal = padValue(val, variableInfo.valDec, 0);\n } else {\n txtVal = val;\n }\n }\n } else if (variableInfo.isEntityName) {\n if (self.ctx.defaultSubscription.datasources.length) {\n txtVal = self.ctx.defaultSubscription.datasources[0].entityName;\n } else {\n txtVal = 'Unknown';\n }\n } else if (variableInfo.isEntityLabel) {\n if (self.ctx.defaultSubscription.datasources.length) {\n txtVal = self.ctx.defaultSubscription.datasources[0].entityLabel || self.ctx.defaultSubscription.datasources[0].entityName;\n } else {\n txtVal = 'Unknown';\n }\n }\n if (typeof variableInfo.lastVal === undefined ||\n variableInfo.lastVal !== txtVal) {\n updated = true;\n variableInfo.lastVal = txtVal;\n }\n text = text.split(variableInfo.variable).join(txtVal);\n }\n if (updated || !self.ctx.htmlSet) {\n text = replaceCustomTranslations(text);\n self.ctx.$container.html(text);\n if (!self.ctx.htmlSet) {\n self.ctx.htmlSet = true;\n }\n }\n \n function replaceCustomTranslations (pattern) {\n var customTranslationRegex = new RegExp('{i18n:[^{}]+}', 'g');\n pattern = pattern.replace(customTranslationRegex, getTranslationText);\n return pattern;\n }\n \n function getTranslationText (variable) {\n return utils.customTranslation(variable, variable);\n \n }\n}\n\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-html-card-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"My value\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.random() * 5.45;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"cardCss\":\".card {\\n width: 100%;\\n height: 100%;\\n border: 2px solid #ccc;\\n box-sizing: border-box;\\n}\\n\\n.card .content {\\n padding: 20px;\\n display: flex;\\n flex-direction: row;\\n align-items: center;\\n justify-content: space-around;\\n height: 100%;\\n box-sizing: border-box;\\n}\\n\\n.card .content .column {\\n display: flex;\\n flex-direction: column; \\n justify-content: space-around;\\n height: 100%;\\n}\\n\\n.card h1 {\\n text-transform: uppercase;\\n color: #999;\\n font-size: 20px;\\n font-weight: bold;\\n margin: 0;\\n padding-bottom: 10px;\\n line-height: 32px;\\n}\\n\\n.card .value {\\n font-size: 38px;\\n font-weight: 200;\\n}\\n\\n.card .description {\\n font-size: 20px;\\n color: #999;\\n}\\n\",\"cardHtml\":\"\\n
\\n
\\n
Value title
\\n
\\n ${My value:2} units.\\n
\\n
\\n Value description text\\n
\\n
\\n

\\n
\\n
\"},\"title\":\"HTML Value Card\",\"dropShadow\":false,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "simple_card",
+ "name": "Simple card",
+ "image": "data:image/png;base64,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",
+ "description": "Designed to display single value of the selected attribute or timeseries data. Widget styles are customizable.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.tbDatasource-table {\n width: 100%;\n height: 100%;\n border-collapse: collapse;\n white-space: nowrap;\n font-weight: 100;\n text-align: right;\n}\n\n.tbDatasource-table td {\n padding: 12px;\n position: relative;\n box-sizing: border-box;\n}\n\n.tbDatasource-data-key {\n opacity: 0.7;\n font-weight: 400;\n font-size: 3.500rem;\n}\n\n.tbDatasource-value {\n font-size: 5.000rem;\n}",
+ "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"| \" +\n dataKey.label +\n \" | |
\");\n } else {\n table.append(\n \"| \" +\n dataKey.label +\n \" |
|
\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '' + html_org + '';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (self.ctx.datasources.length > 0 && self.ctx.datasources[0].dataKeys.length > 0) {\n dataKey = self.ctx.datasources[0].dataKeys[0];\n if (dataKey.decimals || dataKey.decimals === 0) {\n decimals = dataKey.decimals;\n }\n if (dataKey.units) {\n units = dataKey.units;\n }\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n};\n\n\nself.onDestroy = function() {\n};\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-simple-card-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "label_widget",
+ "name": "Label widget",
+ "image": "data:image/png;base64,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",
+ "description": "Displays static image and multiple values of selected attributes or timeseries keys on top of it. Position of the values on the image is configurable using advanced settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 4.5,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.varsRegex = /\\$\\{([^\\}]*)\\}/g;\n \n var imageUrl = self.ctx.settings.backgroundImageUrl ? self.ctx.settings.backgroundImageUrl :\n 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==';\n\n self.ctx.$container.css('background', 'url(\"'+imageUrl+'\") no-repeat');\n self.ctx.$container.css('backgroundSize', 'contain');\n self.ctx.$container.css('backgroundPosition', '50% 50%');\n \n function processLabelPattern(pattern, data) {\n var match = self.ctx.varsRegex.exec(pattern);\n var replaceInfo = {};\n replaceInfo.variables = [];\n while (match !== null) {\n var variableInfo = {};\n variableInfo.dataKeyIndex = -1;\n var variable = match[0];\n var label = match[1];\n var valDec = 2;\n var splitVals = label.split(':');\n if (splitVals.length > 1) {\n label = splitVals[0];\n valDec = parseFloat(splitVals[1]);\n }\n variableInfo.variable = variable;\n variableInfo.valDec = valDec;\n \n if (label.startsWith('#')) {\n var keyIndexStr = label.substring(1);\n var n = Math.floor(Number(keyIndexStr));\n if (String(n) === keyIndexStr && n >= 0) {\n variableInfo.dataKeyIndex = n;\n }\n }\n if (variableInfo.dataKeyIndex === -1) {\n for (var i = 0; i < data.length; i++) {\n var datasourceData = data[i];\n var dataKey = datasourceData.dataKey;\n if (dataKey.label === label) {\n variableInfo.dataKeyIndex = i;\n break;\n }\n }\n }\n replaceInfo.variables.push(variableInfo);\n match = self.ctx.varsRegex.exec(pattern);\n }\n return replaceInfo;\n }\n\n var configuredLabels = self.ctx.settings.labels;\n if (!configuredLabels) {\n configuredLabels = [];\n }\n \n self.ctx.labels = [];\n\n for (var l = 0; l < configuredLabels.length; l++) {\n var labelConfig = configuredLabels[l];\n var localConfig = {};\n localConfig.font = {};\n \n localConfig.pattern = labelConfig.pattern ? labelConfig.pattern : '${#0}';\n localConfig.x = labelConfig.x ? labelConfig.x : 0;\n localConfig.y = labelConfig.y ? labelConfig.y : 0;\n localConfig.backgroundColor = labelConfig.backgroundColor ? labelConfig.backgroundColor : 'rgba(0,0,0,0)';\n \n var settingsFont = labelConfig.font;\n if (!settingsFont) {\n settingsFont = {};\n }\n \n localConfig.font.family = settingsFont.family || 'Roboto';\n localConfig.font.size = settingsFont.size ? settingsFont.size : 6;\n localConfig.font.style = settingsFont.style ? settingsFont.style : 'normal';\n localConfig.font.weight = settingsFont.weight ? settingsFont.weight : '500';\n localConfig.font.color = settingsFont.color ? settingsFont.color : '#fff';\n \n localConfig.replaceInfo = processLabelPattern(localConfig.pattern, self.ctx.data);\n \n var label = {};\n var labelElement = $('');\n labelElement.css('position', 'absolute');\n labelElement.css('display', 'none');\n labelElement.css('top', '0');\n labelElement.css('left', '0');\n labelElement.css('backgroundColor', localConfig.backgroundColor);\n labelElement.css('color', localConfig.font.color);\n labelElement.css('fontFamily', localConfig.font.family);\n labelElement.css('fontStyle', localConfig.font.style);\n labelElement.css('fontWeight', localConfig.font.weight);\n \n labelElement.html(localConfig.pattern);\n self.ctx.$container.append(labelElement);\n label.element = labelElement;\n label.config = localConfig;\n label.htmlSet = false;\n label.visible = false;\n self.ctx.labels.push(label);\n }\n\n var bgImg = $('
');\n bgImg.hide();\n bgImg.bind('load', function()\n {\n self.ctx.bImageHeight = $(this).height();\n self.ctx.bImageWidth = $(this).width();\n self.onResize();\n });\n self.ctx.$container.append(bgImg);\n bgImg.attr('src', imageUrl);\n \n self.onDataUpdated();\n}\n\nself.onDataUpdated = function() {\n updateLabels();\n}\n\nself.onResize = function() {\n if (self.ctx.bImageHeight && self.ctx.bImageWidth) {\n var backgroundRect = {};\n var imageRatio = self.ctx.bImageWidth / self.ctx.bImageHeight;\n var componentRatio = self.ctx.width / self.ctx.height;\n if (componentRatio >= imageRatio) {\n backgroundRect.top = 0;\n backgroundRect.bottom = 1.0;\n backgroundRect.xRatio = imageRatio / componentRatio;\n backgroundRect.yRatio = 1;\n var offset = (1 - backgroundRect.xRatio) / 2;\n backgroundRect.left = offset;\n backgroundRect.right = 1 - offset;\n } else {\n backgroundRect.left = 0;\n backgroundRect.right = 1.0;\n backgroundRect.xRatio = 1;\n backgroundRect.yRatio = componentRatio / imageRatio;\n var offset = (1 - backgroundRect.yRatio) / 2;\n backgroundRect.top = offset;\n backgroundRect.bottom = 1 - offset;\n }\n for (var l = 0; l < self.ctx.labels.length; l++) {\n var label = self.ctx.labels[l];\n var labelLeft = backgroundRect.left*100 + (label.config.x*backgroundRect.xRatio);\n var labelTop = backgroundRect.top*100 + (label.config.y*backgroundRect.yRatio);\n var fontSize = self.ctx.height * backgroundRect.yRatio * label.config.font.size / 100;\n label.element.css('top', labelTop + '%');\n label.element.css('left', labelLeft + '%');\n label.element.css('fontSize', fontSize + 'px');\n if (!label.visible) {\n label.element.css('display', 'block');\n label.visible = true;\n }\n }\n } \n}\n\n\nfunction isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\nfunction padValue(val, dec, int) {\n var i = 0;\n var s, strVal, n;\n\n val = parseFloat(val);\n n = (val < 0);\n val = Math.abs(val);\n\n if (dec > 0) {\n strVal = val.toFixed(dec).toString().split('.');\n s = int - strVal[0].length;\n\n for (; i < s; ++i) {\n strVal[0] = '0' + strVal[0];\n }\n\n strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1];\n }\n\n else {\n strVal = Math.round(val).toString();\n s = int - strVal.length;\n\n for (; i < s; ++i) {\n strVal = '0' + strVal;\n }\n\n strVal = (n ? '-' : '') + strVal;\n }\n\n return strVal;\n}\n\nfunction updateLabels() {\n for (var l = 0; l < self.ctx.labels.length; l++) {\n var label = self.ctx.labels[l];\n var text = label.config.pattern;\n var replaceInfo = label.config.replaceInfo;\n var updated = false;\n for (var v = 0; v < replaceInfo.variables.length; v++) {\n var variableInfo = replaceInfo.variables[v];\n var txtVal = '';\n if (variableInfo.dataKeyIndex > -1) {\n var varData = self.ctx.data[variableInfo.dataKeyIndex].data;\n if (varData.length > 0) {\n var val = varData[varData.length-1][1];\n if (isNumber(val)) {\n txtVal = padValue(val, variableInfo.valDec, 0);\n updated = true;\n } else {\n txtVal = val;\n updated = true;\n }\n }\n }\n text = text.split(variableInfo.variable).join(txtVal);\n }\n if (updated || !label.htmlSet) {\n label.element.html(text);\n if (!label.htmlSet) {\n label.htmlSet = true;\n }\n }\n }\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n singleEntity: true\n };\n};\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-label-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"var\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"backgroundImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"labels\":[{\"pattern\":\"Value: ${#0:2} units.\",\"x\":20,\"y\":47,\"font\":{\"color\":\"#515151\",\"family\":\"Roboto\",\"size\":6,\"style\":\"normal\",\"weight\":\"500\"}}]},\"title\":\"Label widget\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "entities_table",
+ "name": "Entities table",
+ "image": "data:image/png;base64,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",
+ "description": "Displays list of entities that match selected alias and filter with ability of additional full text search and pagination. Highly customizable using widget styles, data source keys and widget actions.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-entities-table-widget-settings",
+ "dataKeySettingsDirective": "tb-entities-table-key-settings",
+ "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"entitiesTitle\":\"\",\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":true,\"entityNameColumnTitle\":\"\",\"displayEntityLabel\":false,\"displayEntityType\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"useRowStyleFunction\":false},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"useCellContentFunction\":false,\"defaultColumnVisibility\":\"visible\",\"columnSelectionToDisplay\":\"enabled\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}]}"
+ }
+ },
+ {
+ "alias": "entities_hierarchy",
+ "name": "Entities hierarchy",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAr9SURBVHja7d17VFTHGQDwbx+siDxUFMEgKCm2JsZgFmNUVDAoRzERQfRgfTTa2Go18XlQo1WDmlVaS6I2XWuJgHIEHynVoMYiGuN7j5zjM0qQtw9wBURQll2+zty5CwtiT633biuZ+eNymbv38WNm7t35mNkFrC8rKnjJU1GZCaG+uMqML3kyVxXXQ1kVtoFUVQZF5rYAMRdBAbaJVMAhHMIhHMIhHMIhHMIhdoXc4BAO4RAOsU33/rI6UaJ73s4zEkNuNKUWfeKcuy13/cqxW7CPw7bWjro16/mu4ir0kxryjGJ5FAialOZ7nlHGWtA8R3WllaN6L3y+q1jsBQb7QLYBwCtoWffe/sas8b4msnzoEIffJZCVXN0DxOJ18zY9QIPObaiuAvHU0rk76xFv60qT568pbUibt/I63fH6yiXpJHtf2sl559ihTN02aufYE7IbwKHEmuXKTn3qMq7oTH5mQC4aOrwzw9ezKDO6fe/ou7hKETbRedhjPAe9Qie6+ET3ifF1JEdN07wb4xTRgBGejv32sENlqEoSOj22T9UaAJqduJVwrlpzYL11YyNkal8LGt0+YVXrnOILxBzNGgLZiJgNg+uxwmUFlrsuQzwOhzHC5Zr1AONC8Z46VWrIEpoam31TY7+HWB3musSaYYQ/PQWZ437Y3NhG5ruT+oOTBhDIScTHVINvTsNERTlZ6bEGI4ZZ97/rkIQYHmqXEsGiM1drm2U8ht8/BTFGa7r+5qwIGf823bbajUHqBfdbU3AFCGmmDSRefdxgWKUskB+SP6cHQEhZQ86XuuQia2aPSeyGk9IEQaxIDVfsZZBfC3fUBV1bQDZozhtIyreBvMZsq2WHXHElp3E2XtIK55tUyXLnOd4RngCJGK8hFSqdQOZmkJx+EwhkAeLnmmLEhn5jW0C+ge9peWIT5AxpMCR90NMiN2Qtvf4gHMP+cEJlp891r8Dvbx99w6+aNOS4kmO9CSTc51RVlmscYkBQ0ZMH3YMuF3+s/K4FxPTGq8cqD3fPaoLM8hIaVjb8U+4n+yp6+cEYLEJWidm5I5WgmUSr2gIldP2QQMrHq8BhZh1ikhou49WBAL7kudMcgiVhSnBebmmE1LgtEH5afH8p93utvKMkGdBwlKW8xg2VhfVspbZUzKnOf8K23BEKraShtXM+yK/j7345hEM4hEN+epAbHMIhHMIhTelbvX5HVk1rW2oT7rW6R5X+Jn2bqadbc/UVVXpr732P/j7bUX+avnXV03SenmLDH85KCHkSqgCbFCxmR2r8fJRum1qLwin3t3rceuflZLkbtpLlSqe6PFAIUSMsVEGOsLIE5pOlXtGJpLVoGt1+1GBFrHSQUxATa5P0Vgjp4T7a4rChlQM8ekYhjhpOFr9SjCPLd0dgHihZB2WlkkEM7RwpZJW/2MN3vIQYp/hBMkg2ZLf2qkihq77SkYTrMGvRquOkl0ULolRXeF9HejINBxYuFfYr/Wx+olgD45xM2OAZ5mJCs/NqAhnhTmNc9a+EChDTmx97U8iHIezV4aNoFYT9ckDm0rrlessGkgvJiIs00eGKeFzuQbqy8S411+EbxCntIserSOTrdEfttK79meQEnMWLYFCfIMtjBJKo3kly/65IESBrfasFSPjkQ9tPWrv1WXBdDkiwT2xsjPgrg9SrV2OW4iDiunb3r5Grw0HTkUK+hiOIOxR5lp9FWTDfKYFFlhw34fpf4JDluEVTSyD/GEd7wWNGZlPIdcdMFCD9FW6dYDjrW9YFjnmRNtK8Q28LCW76lUHQZQnO6CMcPwsDZ2OJ4lsBMqUvvXDdzdNCLGVkDNt/aBQOXYRxgTh5MLk7QXomXMMC5b7DBGIJIj15AZL8NzMmilHB37kXoMQlki2EJZ6CVJJYSwi7nX2FCZ7mzd3NAiQknO28k20bwn77xLNCnYUXlGW9llJIiqXXfFzhZcogkC1dykQII48S2nu74yg1ZKuqY8+apyHHSGzn/UAaiTOU41318RASsaOQqBFi2Boy6Daxoh+BTa4mtHgkQCaFJOH6ztXdV5B6mFPj4q7VajUeWjEuOC2ALFLU+1ByiPfXdYHbn4I8CfI347JOFSwUh2Mmqi4yyCfupH0boy/mwXZxG00P1X1IVA+nvq6qYpC7mgmqQgqpS6fJfUx69WWg/4PQRhK25q8oC0TbHOJvyN7SvzN5At9yGXnlwVYPUjVS4TVkkLwOkwsKIj0fYoT7PuP5178UjzWAxCsRd4EWGQQnwlikELaZVq2GwIFXyj6FQ3jBeTItzEsSPhBn6XSFtGrVBvvpdLOsEAB1z9n5wiv6AnTfRWNwLutFCGb7Awy4TAphmgNoPrA+IhcpaRysXHgSCpAsONACgoVhAB3jET9izctHMkjdKCVAttDYbZ8jzdLt4qfjuSXiPyOr82qf833hoxL+7pdDOIRDOIRDOIRDOOSnGqAjoTcacrEkpWKBvkzMSxPCccLqnRPWF2bp5YBIFqDDNGHI0UcOR0jH8YKY19WJhuPIWfaOVU8U83LbgxwQ6QJ0AkSnSMYmSJ0ija2Md57eU4Q0hHSQBSJdgI5CkhSfoQ2kAE6xlbOP8G0Rsq1LrNyQFwzQEcghh7loCzkNx3fsucPWRUipW0q83JAXDNClwQ7ndyzNIHvBwUvV/qAtZFwYSgWRK0CXBu06O5xuBvlxTT7eecv7cRMkrUO+VBCUK0CXBsOMAT3KbSFiuOt8I8TYjdwI5YRIEaBLg0uY6zba0hJyAw42QhZBgFbrDdoNckGkCNAJt999irU2kIj3yGI/XGuEnKOBuqmQflFGyAsH6NgDcYHqGIGk0IIqxP1K/f1zvYMabBo7ylW1pAvQMYhpkOftDNZ0fou4uRMoh+WjPSAyB+gaip8vfscH1XAIh3AIh3AIh3AIh/x/QiSa4vqEROISD9+VFvK/mOJqBHc/vw6Of5QW8oxikXOKqxE+J52Cucof7QERp7jGjW4aziLZFFcBgjmwz36QXaT31zh9T7IprgzyZ8Ulu1QtYYrrF7JMcTVCaGxslFO81G3k30xxrQp1WdRUs6Wa4mqEgOjo4c6LLfYoETmnuLKqdUqZKD9E3imuDII9pssOkXmKK4NUOS2THSLzFFcjLDZc2B/kdF32J7u8U1wJhCSPsLPSv9dqmV7aKa4vZeIQDuEQDmmzkBscwiEcwiFN6b8aQafXJ5+gHdCbev22AzaTK2p3rdlmjaf9oC+RByLlCDpvPx+NJxnxlwQ9/NqrYqwfyV/k5/X+z93Y5+yZ+sFReSASj6C7P/BVCslD8wnfoeJn8kf1qUZzyCBhPa6DXBBpR9DRYQ1VAoRMzlWI86eX7yaLdU509ZpjvB0gLz6Cju7oZREh2DfS5jxRdMaeZcj0HDtAXngE3czYhUHdjqEVEhFgPXLm5kh32gfe7HFfQoh8I+jCoiMDPFIbIVP9MYXcyGhZ93MKIRdY6JKK0kGwtRKRZgQdrVpx6jwrJHgITgoNncA6+MNIHHZ0OMoNkWYEHYXcgBQRYuoy1+Y86XArE/y02j7gHyUjRLIRdJhJ7AyyTsXGyTV0op+hkKioLKDD5zbCykPyQiQYQbfHcGa7d9/HBJJxNnmywhqYn9XxYMUZf7E2yla1pBxBB6DqOaOUNnaAbuGNF1wzWwPqmHKZIXaZ4lpf9J/G8nifnUM4hEM4hEM4hEM4hEM4hEM4hEM4hENkhrSZLwhuG1/ZXFkGprbxJdpmaBtfa27GfwEB0j8MtCzTjwAAAABJRU5ErkJggg==",
+ "description": "Displays hierarchy of entities based on their relations. The root of the hierarchy is defined using entity alias. By default, displays entities related using \"Contains\" relation. You may change the behaviour using advanced settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesHierarchyWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'nodeSelected': {\n name: 'widget-action.node-selected',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-entities-hierarchy-widget-settings",
+ "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"nodeRelationQueryFunction\":\"var entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeOpenedFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be opened (expanded) when it first loaded.\\n\\n// The following example code will open by default nodes up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n**/\\n \",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" \\\"+ data['temperature'] +\\\" °C\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\"},\"title\":\"Entities hierarchy\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "qr_code",
+ "name": "QR Code",
+ "image": "data:image/png;base64,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",
+ "description": "Displays QR code of calculated text from configured pattern or function with applied attributes or timeseries values.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 4,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.qrCodeWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-qrcode-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7036904308224163,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"qrCodeTextPattern\":\"${entityName}\",\"useQrCodeTextFunction\":false,\"qrCodeTextFunction\":\"return data[0] ? data[0]['entityName'] : '';\"},\"title\":\"QR Code\"}"
+ }
+ },
+ {
+ "alias": "markdown_card",
+ "name": "Markdown/HTML Card",
+ "image": "data:image/png;base64,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",
+ "description": "Renders markdown/HTML using configurable pattern or function with applied attributes or timeseries values.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 5,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "#container tb-markdown-widget {\n height: 100%;\n display: block;\n}\n\n#container tb-markdown-widget .tb-markdown-view {\n height: 100%;\n overflow: auto;\n}\n",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.markdownWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n hasDataPageLink: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-markdown-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"markdownTextPattern\":\"### Markdown/HTML card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Random}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\",\"useMarkdownTextFunction\":false},\"title\":\"Markdown/HTML Card\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
+ }
+ },
+ {
+ "alias": "dashboard_state_widget",
+ "name": "Dashboard state widget",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAYAAABJ/yOpAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJztnXd8VFX6/993+iSTMmmkACEghN6UKij8RBF0XSxgb1hAFiyg7iIqu9YvKoLoIjbsywo2RIrSRKQlFBECIQkhQAppk0mZPnPv748xIwnJpE1IcO/79eJFZuaU59xzP/eec+5znitYLBbJ5XIhCAIybYMkSajVaoKCggCwWq3IfdK2VPeJyul0Eh4e3tb2/M9jNpt9ApH7pH1gNptRtLURMjLtGVkgMjJ+kAUiI+MHWSAyMn6QBSIj4wdZIDIyfpAFIiPjB1kgMoiieM532dnZlJaW1vu7P4qLi8nJyWlUPe2dBgVitVqpqqrC7XY3qwJJkrBarc3KK1OTf//736SkpPg+l5WV8eijj7aozIyMDD744INzvt+0aRMZGRnk5uayZMkSADIzM8nLy2uwzLS0NLZt23bO96+++iolJSX15vvpp58ab/h5QtVQgrlz5xIUFIRCoUCtVvPggw8SHx/f6ApOnjzJ1q1buffee1tkqAx4PJ5zrsLVF67y8nL0ej0ajQabzYbH40Gv11NRUYFKpcJkMtGxY0dEUSQ3N5fY2Fj0ej1dunShQ4cOvvLOnDmDQvHHdbNDhw7cfffdVFVVsWXLFqKjo7nqqquw2+1ERUX56tZqteh0uhq2lZaWYrPZ6NixIwAPPPAAYWFhgPfCm5+fT3x8PEFBQRQXF7N8+XJ69+5NTEwMAEVFRbhcLuLj4xEEwdcus9lMcHAwarUag8EAQGFhYY12BIoGBQIwe/ZsoqOjycrK4pVXXmHhwoUolUrsdjtHjhwhNjaW+Ph4ioqKMBgMBAUF4XQ6KS4uJjY2lokTJ/rKysnJoaysjF69evkOaGlpKSdPnqRbt26+A1hWVsbx48fp2LEjsbGxAW/4n40PPviAcePG0b9/f7Zv305JSQnjxo3jiSeeYNCgQbhcLkpLSzEajRgMBg4dOsTChQvJzMzkp59+4tFHH+XLL79k3759JCQk8Ouvv9KvXz9OnTrFihUruO6668jOzqawsJB+/fqxYMECFi1aREhICPPnz2fu3Lk1BJKSkkJ5eTlnzpxh0KBBTJ48mQULFjB79mwcDgevv/46gwYNYu/evcyZM4fU1FScTiebNm3itttuY/ny5Zw5cwaDwUBFRQXz5s1jx44drFq1ik6dOjFmzBi2bNnCs88+S35+PosWLeLVV18N+HFtlECqueiiizAajeTk5BAZGcnzzz/PiBEj+Pbbb5k4cSImk4mqqiqmTJnCnj17yMrKYujQoaxbt44nnniClStXcuLECZKSkvjss8944YUXyMrK4rPPPmP48OF8/vnnPPnkk7jdbhYsWMAVV1zBihUruP322xk8eHDAG38h8tlnn7F69WqARg17jUajbxh2zz338I9//IPw8HAWLlzI8ePHfencbjfr169n2bJlqNVq3nrrrRrl9O/fn0OHDtGhQwe6d+/OiBEj2LNnD/3790en051z9e7Tpw8zZswgPz+fN998k8mTJ/t+S09Pp0uXLtx2222MGjUKgMmTJ7Nu3Tpuu+028vLyOHbsGAsWLABgyZIlvqHl4MGDmTZtGpIk8emnn1JZWcmePXsYPXp0Uw9lo2iSQAAiIiIoKSkhNTWVkSNHMn78ePr27ct//vMfnnzySf75z38yZcoUdu3axU033YTNZgO8w4Mff/yRZcuWoVKpiImJobS0lJUrV3L33XfTuXNnJEli27ZtJCUlYTQaufzyyxk+fLivDBm44447GD58OOC9yz7zzDN+02s0Gt/fWq3W5xCp0WhwuVwolUrA65gXERGBWq0+J19djBkzhs8//xybzVbnyRkcHFyjnrMZO3YslZWVzJs3j44dO54z/C4oKCAxMdH3OTExkYKCAgwGg69cQRC49NJL2bNnDykpKTz++ON+7W0uTRZISUkJUVFRpKamkpeXR3Z2NuBthMFgwGg0kp2dTXFxMV27diUtLQ2AiooKQkNDUam8VY4ZMwbwrnisXr3a9/2AAQO45JJLsNvtLFmyBLfbzfTp0wPR1j81Go0Gp9MJgMPhaHL+8PBwTCYTLpcLtVrdYBndunXDZDKxbds25s2b16S60tLSGD58ODfccAMrVqxg7dq13HLLLUiSBEBcXBwnT570pT958iTDhw+noqKiRjljxoxhyZIlBAUFERkZ2SQbGkuTBJKenk55eTldunShU6dOREZGcuuttyJJEiaTyWf0O++8w7Bhw2rkDQ8Px2KxYLFYCA4OZtOmTfTu3ZuOHTtyzTXX0L9/f99qV2pq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",
+ "description": "Displays specified dashboard state inside widget. Advanced widget settings allows you to configure target dashboard state to be displayed.",
+ "descriptor": {
+ "type": "static",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "\n\n\n",
+ "templateCss": ".dashboard-state-widget-prompt {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .title {\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .subtitle {\n font-size: 24px;\n font-weight: normal;\n color: #999;\n text-align: center;\n}",
+ "controllerScript": "self.onInit = function() {\n var $injector = self.ctx.$scope.$injector;\n self.ctx.$scope.stateId = self.ctx.settings.stateId || \"\";\n self.ctx.$scope.defaultAutofillLayout = self.ctx.settings.defaultAutofillLayout;\n self.ctx.$scope.defaultMargin = self.ctx.settings.defaultMargin;\n self.ctx.$scope.defaultBackgroundColor = self.ctx.settings.defaultBackgroundColor;\n self.ctx.$scope.syncParentStateParams = self.ctx.settings.syncParentStateParams !== false;\n}\n\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-dashboard-state-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"syncParentStateParams\":true,\"defaultAutofillLayout\":true,\"defaultMargin\":0,\"defaultBackgroundColor\":\"#fff\"},\"title\":\"Dashboard state widget\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json
new file mode 100644
index 0000000000000000000000000000000000000000..de32604dbb2ab19e7f60739248dab6de36272fa4
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/charts.json
@@ -0,0 +1,209 @@
+{
+ "widgetsBundle": {
+ "alias": "charts",
+ "title": "Charts",
+ "image": "data:image/png;base64,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",
+ "description": "Display timeseries data using customizable line and bar charts. Use various pie charts to display latest values."
+ },
+ "widgetTypes": [
+ {
+ "alias": "bars",
+ "name": "Bars",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAA8FBMVEUhlvNMr1Bqamp5eXl7e3t8fHx9fX1+fn5/f3+AgICCgoKDg4OEhISGhoaHh4eKioqMjIyNjY2Ojo6QkJCRkZGSkpKWlpaXl5ebm5udnZ2enp6goKChoaGkpKSnp6epqamsrKyurq6xsbGzs7O1tbW2tra3t7e4uLi7u7u9vb3BwcHCwsLDw8PGxsbKysrNzc3Ozs7R0dHS0tLT09PZ2dna2trc3Nzd3d3e3t7g4ODh4eHj4+Pk5OTm5ubn5+fo6Ojp6enu7u7w8PDz8/P0Qzb09PT29vb39/f5+fn6+vr7+/v8/Pz9/f3+/v7/wQf///+dc+aLAAAAAWJLR0RPbmZBSQAAAcFJREFUeNrt3ds2AgEYhuHsaSOZbAvZi0r2YYjCJOW7/7txZhkcDNbM6h/vdwfPmlX/ybtmEorJErGCeJLadz3rkKPpZamaLTp925DHdFvSpKelU9uQ/cLKhtcdk7YqtiHruevtojch7ZZtQ0o1dcdfRqXNqm1IbVU3OaVamm/YhvQW5zIXOknnC5JUt7qEpE5fUv/5HVePy2UHAgQIECBAgAABAgQIECBxgrwGHBAgQIAAAQIECBAgQIAAAQIECJC/QRIBN0iQ+66voDMLuRp2fQWdVUhvNun6CjqrkJ0Dx/UVdEYhzXzfcX0FnVFIrlSZ2mx/LOiMQuqHh6k972NBZ/fv13G/KeiCQkIu4358EL8UdBafSGwuOxAgQIAAAQIECJDYQB4CDggQIECAAAECBAgQIECAAAECBAgQIECA/BrSufn0DjqjkEZmLXkWaUEXEmThXMeFSAu68H4j5b1IC7rQILfZTqQFXViQ1nRTL1EWdCFBnmYuJUVZ0IUEWR1xHKcXZUEXFDLwBR2XHQgQIECAAAEC5H9ChgIOCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgxiBmv+L6Bl9pkxYph15gAAAAAElFTkSuQmCC",
+ "description": "Displays latest values of the attributes or timeseries data for multiple entities as separate bars.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 5,
+ "resources": [
+ {
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"
+ }
+ ],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n \n for (var i = 0; i < self.ctx.datasources.length; i++) {\n var datasource = self.ctx.datasources[i];\n for (var d = 0; d < datasource.dataKeys.length; d++) {\n var dataKey = datasource.dataKeys[d];\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n var dataset = {\n label: dataKey.label + units,\n data: [0],\n backgroundColor: [dataKey.color],\n borderColor: [dataKey.color],\n borderWidth: 1\n }\n barData.datasets.push(dataset);\n }\n }\n\n var ctx = $('#barChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'bar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scales: {\n yAxes: [{\n ticks: {\n beginAtZero:true\n }\n }]\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var c = 0;\n for (var i = 0; i < self.ctx.chart.data.datasets.length; i++) {\n var dataset = self.ctx.chart.data.datasets[i];\n var cellData = self.ctx.data[i]; \n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n dataset.data[0] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-chart-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars\"}"
+ }
+ },
+ {
+ "alias": "doughnut_chart_js",
+ "name": "Doughnut",
+ "image": "data:image/png;base64,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",
+ "description": "Displays latest values of the attributes or timeseries data for multiple entities in a doughnut chart. Supports numeric values only.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 5,
+ "resources": [
+ {
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"
+ }
+ ],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = typeof self.ctx.settings.borderWidth !== 'undefined' ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-doughnut-chart-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#26a69a\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#afb42b\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#673ab7\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"borderWidth\":5,\"borderColor\":\"#fff\",\"legend\":{\"display\":true,\"labelsFontColor\":\"#666666\"}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "pie",
+ "name": "Pie - Flot",
+ "image": "data:image/png;base64,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",
+ "description": "Displays latest values of the attributes or timeseries data for multiple entities in a pie chart. Supports numeric values only.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n",
+ "settingsSchema": "{}\n",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-flot-pie-widget-settings",
+ "dataKeySettingsDirective": "tb-flot-pie-key-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "pie_chart_js",
+ "name": "Pie - Chart.js",
+ "image": "data:image/png;base64,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",
+ "description": "Displays latest values of the attributes or timeseries data for multiple entities in a pie chart. Supports numeric values only.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [
+ {
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"
+ }
+ ],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'pie',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false\n }\n }); \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-chart-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Pie - Chart.js\"}"
+ }
+ },
+ {
+ "alias": "polar_area_chart_js",
+ "name": "Polar Area",
+ "image": "data:image/png;base64,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",
+ "description": "Displays latest values of the attributes or timeseries data for multiple entities in a polar area chart. Supports numeric values only.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 5,
+ "resources": [
+ {
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"
+ }
+ ],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n\n pieData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'polarArea',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n try {\n self.ctx.chart.resize();\n } catch (e) {}\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-chart-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fifth\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.2074391823443591,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Polar Area\"}"
+ }
+ },
+ {
+ "alias": "radar_chart_js",
+ "name": "Radar",
+ "image": "data:image/png;base64,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",
+ "description": "Displays latest values of the attributes or timeseries data for multiple entities in a radar chart. Supports numeric values only.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 5,
+ "resources": [
+ {
+ "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"
+ }
+ ],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n\n var backgroundColor = tinycolor(self.ctx.data[0].dataKey.color);\n backgroundColor.setAlpha(0.2);\n var borderColor = tinycolor(self.ctx.data[0].dataKey.color);\n borderColor.setAlpha(1);\n var dataset = {\n label: self.ctx.datasources[0].name,\n data: [],\n backgroundColor: backgroundColor.toRgbString(),\n borderColor: borderColor.toRgbString(),\n pointBackgroundColor: borderColor.toRgbString(),\n pointBorderColor: borderColor.darken().toRgbString(),\n borderWidth: 1\n }\n \n barData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n barData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n var ctx = $('#radarChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'radar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n } \n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n self.ctx.chart.resize();\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-chart-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Radar\"}"
+ }
+ },
+ {
+ "alias": "state_chart",
+ "name": "State Chart",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAB9VBMVEUAAAAhlvMilvMymeE+nNVDoetInslNq/VZqOdpuPd3d3d5p5Z5suB6enp8fHyBgYGDg4ODqoqEhISIiIiKioqKuN2MjIyNjY2Ojo6QkJCRkZGSkpKUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKCgr3KhoaGioqKisXSjo6OjvsmkpKSlpaWlwdempqanp6eoqKiosGOo1vqpqampsGOqqqqqsWOrq6usrKysw9Wurq6wsLCysrK0tLS1tbW1wbK2tra3t7e4wau5ubm6urq7u7u8vLy9vb2+vr6/xbTAwMDAxbjCwsLC3ejDw8PExMTFxcXGxsbHx8fIv3jIyMjJycnKysrK5vzMzc7Nzc3Ozs7Pz8/QuDnRzcHS0tLT09PT39HU1NTU6/3VzLLV1dXV3sjW1tbX19fYuTHY2NjZ2dna0Ira2trb29vc3Nzex4De3t7f39/guyvhvCfhvCvh4eHh6Nbi4uLjvCTj4+PkyXbk5OTlvCTm5ubm693o6Ojpxl7q6urr6+vswTTs7Ozt7e3uvhju7u7vvhjv7+/wvxjw8PDx8fHyvxX0yTv09PT19fX29vb39/f4wyL4+Pj5+fn6+vr75J37+/v8whP8/Pz9/f39/v/+/v7/wQf/xRb/3HT/5JH/9tz/++/////APs7XAAAAAWJLR0Smt7AblQAAA1RJREFUeNrt3dlT01AUBvAEd8UNl2oLrdrFolZArUul1hWlVhQXFAUF1xaxIqi4FUQUrDsUClZi4nb+Th96S9NQkjDOOKZ+3wuZw8md++M25OXMlKOccJQnTUYocdRqdw8UAqTfOjG4lvr7uowOqbtAtG6o5OiGFoNDapuJHO8tFK4xOKS9msTVgoUiaQjPclnSl01snZ/y4ly2yBNZbRarbc+3yvdpt/hLawOPJp9ur7O0mRiEmzFkY1M6N/4I8qJpulzR2sBx1sgRjQuyA2pk+STdylw2VqR/FPFfWdcCvjhduiM7kVF2db1Nuspu7JGes+I76SRba7f0Wfnn/6F6It+mfo7m8TvYvh7KTiT3PQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggPwXkFa7e7AQIP3WiUFzdl7LuBDFvJZxIYp5LeNCFPNaBn7Yc+e1KlheSYcrFCniL7LZqPl8cbp0TjavNcqu9rZJB9gdPdIJVnwjbWG1ndI95UzWM9V5rS9Ti3P4VWy1+5jXAgQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAMK+FeS3Ma2FeC/NamNfCCxEQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBA/hZkTKSkwSGn7VUJOmOLV241NiRSRZ2uYcvt+Io+Y0N83USm+MqGWEnU2JCqXqI1ZE+QhYiIy36tXR5INpOQbB5kftcmK85mtdey2iJekeWqX6/3kc+TLCQTrq6eEuZCgKTsbnNXBsIZOERJMedNQnry73XpW8UAKVhIyBPScVfQE9RqEaP7iMT6g+pdw7vKbxKl3IJqV8K7OUqXPHvG9UMGykk2lz1d4g5yvNXo6QqZiA41aHT5OwQTUWBJSrWrvlMwj1jFU2f1Q8K1dCysCUmVhdenNLvMRKZt3hGNriEnxfxlGqt9aPYRkb9bP6Q1SMFrmltMukKuMT2QpckWjc/WhP2lYB/TgjzdHyCKVM/gGXnsp+qY5hbba+hIVA/ETL0+9SepsoNiTs/igGpXZIhKqVv9QVJARIfXIWqfiM1nS+qBnHdae1V7Qss8ngEijRO5a6sMCAtdqv+HfgPwpNPbU6ipOwAAAABJRU5ErkJggg==",
+ "description": "Displays changes to the state of the entity over time. For example, online and offline.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-flot-line-widget-settings",
+ "dataKeySettingsDirective": "tb-flot-line-key-settings",
+ "latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}"
+ }
+ },
+ {
+ "alias": "basic_timeseries",
+ "name": "Timeseries Line Chart",
+ "image": "data:image/png;base64,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",
+ "description": "Displays changes to timeseries data over time. For example, temperature or humidity readings.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "latestDataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-flot-line-widget-settings",
+ "dataKeySettingsDirective": "tb-flot-line-key-settings",
+ "latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries Line Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
+ }
+ },
+ {
+ "alias": "timeseries_bars_flot",
+ "name": "Timeseries Bar Chart",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABOFBMVEUAAAA3oPR3d3d6enp8fHyBgYGDg4OGhoaNjY2RkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6wsLCxsbGysrK0tLS1tbW2tra3t7e4uLi5ubm6urq8vLy9vb2+vr6/v7/AwMDBwcHDw8PExMTHx8fIyMjJycnLy8vNzc3Ozs7Pz8/S0tLT09PU1NTV1dXW1tbX19fZ2dna2trb29vc3Nzd3d3e3t7f39/h4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fH09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7/xx////8KXFhiAAAAAWJLR0RnW9PpswAAAvtJREFUeNrt3GtXElEUBuDpZlAqylVHCyMwyi4SpkaWAl4qk8wwUhJlmOn9//+gLzbKbc6AAm5991p82WvPOedZZw6zYM3aGupCQ4tYkZDSsKDrocNVvz8jHQKYY1ZiT/6OAJllRPSpsnzIqIG9SjYmHpKfAzI4CIuHjJeBtyHfZwCatiI1/m+BYV2Dw35NniOEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQUp/6Wx+DgbRbxOAhEb/fXJ98akiH1AIAHtSWlqRDSvdDGcOHnYR0SPV7zVsaQyHeO0jTJapEV5C9bUz9fIjsvHRIefhRxHqpjxzA3ftajXOudHGJOtHV+1rV04/wHenDc2QQkFbLJISQwUDaP90IIYQQQgghpAvIxRO9gFxG4lZ9XBFIx6sihJD+QVRjEEIIIYQQcoMgahkhhBDSe0iLX3ydQdSLIKR9ghCJkIYEIYQQIhPS8d+AhPQboppFw9HMxBrs/lqCIbObpgezP67YjnQ8iwagHDzrryUZUgsUz/prCYZY05vn+msJhiwM6XrR7q/V9OJU4xQuEpcxRsezNPbXEn3Yzz9Hulg3IYT0C3LxxI2CNCQIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQlpA7P5a0iF2fy3hkLP+WsIhVbu/lvRby+6vJR1i99eSG6ffu1XHxlRNcfWq3I0iIK4tJK2nXVyV0lOqEmvjGWAtvnCu+jMzkQWMiOlYdRSd3MAH/UnVPWR/ApFfSkcpiOBvRc3XtAeYW1ZUJbZMD5C8azhWLX4xvZVR692Se0huHm9ySogxkhs3lFVewPM4WlFUlUMoJEYUox1+jAFIbLuHrKaQWlMu8TicDp+4gdw7/qS4t2qBohk4UUF2nieBfLyDM/ItgXhBucT113i14QbixW7M+SRNb6EQ0u8kHavyZQxj2/kgNUCsYDRoqXfEF/Mdu4G8D43uOtakh3R9H1DsyKZvOmneDjt+D/0DTzolrPMHmggAAAAASUVORK5CYII=",
+ "description": "Displays changes to timeseries data over time. For example, daily water consumption for last month.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-flot-bar-widget-settings",
+ "dataKeySettingsDirective": "tb-flot-bar-key-settings",
+ "latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bar Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..2dd348c84c7683affc51100d70c36917dfdd20a4
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json
@@ -0,0 +1,200 @@
+{
+ "widgetsBundle": {
+ "alias": "control_widgets",
+ "title": "Control widgets",
+ "image": "data:image/png;base64,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",
+ "description": "Send commands to devices."
+ },
+ "widgetTypes": [
+ {
+ "alias": "rpc_debug_terminal",
+ "name": "RPC debug terminal",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAWcklEQVR42u2dB5QWRZeGi6QgklFUDJhFMCJGRFHMomMAMStiBvUXRTFgRjGLERMoiIBZ1GVXdNcF4yIe5agYdvVnfkAwIAoiIs4+/72na3u+NN8wAzsD73vmzOnur7rCrbfuvV3VfSuUlZXNnDnzmGOOadKkSRCEKgAKlZSUzJgxA1IFWNWyZUsJRagutGjRAlIFdJVkIVQvevbsGWQBhWpH06ZNJQRBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEITlRK9evc5KwAdAnTt3rlevXvz1oIMOir+efvrp3bp1a9WqVUYOderUOfDAAy+77LJLL7308MMPX2ONNYop99577+ULyaOPPrpwsvfff59kG264Ya0W8uDBg6+55pqq5PDRRx8hh3XXXbfWtPnzzz8vK4/PPvtsjz328F9ffvnljF9///33q6++Ot6+/vrrv/fee+kEX3zxxeabb15biHXqqafecsstK5S4a6655pIlS/7444+11lprtSPWEUcc0alTp/333//hhx/mdNasWa54nFj9+vXj19122+2MM8747rvvuHLiiSe6rnrrrbc4feyxxzp27Aifbr/9dk6nTp1aW4j10ksvkf8uu+yyQoVM/piC1VFjbbbZZtGu+RUXhBMLgxjTQymuvPrqqxzvs88+HH/44Ydps/jJJ59wccstt8wua+ONN7722mvvuOOOQw89NJtYlAIv7777buwp+aSJtd122w0YMIBbUDDRUjMYsL8bbbSRn+63336ctm/f3k/XXnvtiy66iFsw4htssAE/MWzSlWncuDEXUc/kT7L4cXn9+vVxD+65557bbrttr732iunJ7dxzz91mm22o4QEHHECIA27v0aMHgkKFDxs2jIsk23PPPWng0KFD995773jv3wx+3Ldv34svvrh58+a06L777jv77LMbNGiQtgBcf+CBB8hz6623XnWIBd59990CxOrSpQtX3n77bY6HDBnCMfJNZ4gfRl9uuummGQWhz3788UfS//nnn/z/4Ycf0sR6/PHHOcVkLF26lAMkmybW3LlzMSVuaseOHes/jRkzhlPI7aeuLI8//niOGzVq5PxetmzZX3/9VVpayvFdd92Vrs86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",
+ "description": "Allows to send any RPC command using its name and parameters to device. Useful for debug.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 9.5,
+ "sizeY": 5.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n",
+ "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, persistentPollingInterval, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-rpc-terminal-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "rpc_remote_shell",
+ "name": "RPC remote shell",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAU6klEQVR42u2dCbSW0xrHtyZDOpVkyFCKIplCZpLMIVJIhsgYmSUakLniInOGyFRXaCn3ilws89Q1RrpRSq4k15Tx3N96Ht9e73m/4Xzn9EVH//8666z97u99997v3v/9PM8e3meH8vLyWbNmdevWrUGDBkEQFgNQqGvXrtOmTYNUAVatssoqqhShVGjcuDGkCsgq1YVQWnTv3j1IAwolR1lZmSpBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEAShmujRo8fxGfAB0NZbb127du3465577hl/7d2796677tqkSZNUCsstt9wee+zRv3//c845p0uXLvXq1ftLVtQ222zDJ50PP/xwlZ7abbfdeOruu+8mXKtWLcKzZ89eJoj1/vvvl1fEe++9t9122/mvEyZMSP26aNGiQYMGxcfXXHPNl156KXnDBx980KpVq6Xh1aD7FVdcsdVWW4lYfxqx9t9//y233JJauPXWW7mcM2eOCx4n1imnnMKv1Oyxxx47b948Yg4//HCXVc888wyXt99+e7t27eDT8OHDuXzttdeWhlcbOnQohTnmmGNErD+NWC1btox6zWPQiZFYKMR4P5QiZuLEiYR32WUXwq+//npSLb711ltEbrDBBqmMDjnkENTluuuue+mllx533HEe2axZs/POO++mm24666yzVl11VY9s27Ytd3bo0GHvvfe+/PLLhw0btummmxJ/0EEHXXfddRdddFGbNm2SKcPpCy+8kET69evXqFEjj6QzPPXUU5Rk3LhxJ554Yry5c+fOJHjttdfisIDS5qyTpk2bnn322eR1/vnnR+kbiUV2Q4YMufLKK7fffvvkU+uss86AAQMoxhlnnBEdIIhYLWPMiy++WIBYO+64IzHPP/884csuu4wwJEgmiB2GQbbeeuulMpo8eTI3z5w5k/8kS8xOO+20cOFCLr/77jv+z507t3nz5sT37NnTpSb/f/75Z/5///33Dz74IIGffvrJ74/pQ5pffvmFyB9++MGbbf311yceLxcew81Tp071m2l1T8TTueOOO7IrZOONN/7qq6/IlxTQ+9y57777RmKRPsl6qX799VdeNhLom2++ie+CH4S11lpLxPqd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",
+ "description": "Allows to emulate remote shell. Requires custom implementation on the target device to work properly.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 9.5,
+ "sizeY": 5.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n",
+ "controllerScript": "var requestTimeout = 500;\nvar commandStatusPollingInterval = 200;\n\nvar welcome = 'Welcome to ThingsBoard RPC remote shell.\\n';\n\nvar terminal, rpcEnabled, simulated, deviceName, cwd;\nvar commandExecuting = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n rpcEnabled = subscription.rpcEnabled;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n } else {\n deviceName = 'Simulated';\n simulated = true;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n \n terminal = $('#device-terminal', self.ctx.$container).terminal(\n function (command) {\n if (command && command.trim().length) {\n try {\n if (simulated) {\n this.echo(command);\n } else {\n sendCommand(this, command);\n }\n } catch(e) {\n this.error(e + '');\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: false,\n enabled: rpcEnabled,\n prompt: rpcEnabled ? currentPrompt : '',\n name: 'shell',\n pauseEvents: false,\n keydown: function (e, term) {\n if ((e.which == 67 || e.which == 68) && e.ctrlKey) { // CTRL+C || CTRL+D\n if (commandExecuting) {\n terminateCommand(term);\n return false;\n }\n }\n },\n onInit: initTerm\n }\n );\n \n};\n\nfunction initTerm(terminal) {\n terminal.echo(welcome);\n if (!rpcEnabled) {\n terminal.error('Target device is not set!\\n');\n } else {\n terminal.echo('Current target device for RPC terminal: [[b;#fff;]' + deviceName + ']\\n');\n if (!simulated) {\n terminal.pause();\n getTermInfo(terminal,\n function (remoteTermInfo) {\n if (remoteTermInfo) {\n terminal.echo('Remote platform info:');\n terminal.echo('OS: [[b;#fff;]' + remoteTermInfo.platform + ']');\n if (remoteTermInfo.release) {\n terminal.echo('OS release: [[b;#fff;]' + remoteTermInfo.release + ']');\n }\n terminal.echo('\\r');\n } else {\n terminal.echo('[[;#f00;]Unable to get remote platform info.\\nDevice is not responding.]\\n');\n }\n terminal.resume();\n });\n }\n }\n}\n\nfunction currentPrompt(callback) {\n if (cwd) {\n callback('[[b;#2196f3;]' + deviceName +']: [[b;#8bc34a;]' + cwd +']> ');\n } else {\n callback('[[b;#8bc34a;]' + deviceName +']> ');\n }\n}\n\nfunction getTermInfo(terminal, callback) {\n self.ctx.controlApi.sendTwoWayCommand('getTermInfo', null, requestTimeout).subscribe(\n function (termInfo) {\n cwd = termInfo.cwd;\n if (callback) {\n callback(termInfo);\n } \n },\n function () {\n if (callback) {\n callback(null);\n }\n }\n );\n}\n\nfunction sendCommand(terminal, command) {\n terminal.pause();\n var sendCommandRequest = {\n command: command,\n cwd: cwd\n };\n self.ctx.controlApi.sendTwoWayCommand('sendCommand', sendCommandRequest, requestTimeout).subscribe(\n function (responseBody) {\n if (responseBody && responseBody.ok) {\n commandExecuting = true;\n setTimeout( pollCommandStatus.bind(null,terminal), commandStatusPollingInterval );\n } else {\n var error = responseBody ? responseBody.error : 'Unhandled error.';\n terminal.error(error);\n terminal.resume();\n }\n },\n function () {\n onRpcError(terminal);\n }\n );\n}\n\nfunction terminateCommand(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('terminateCommand', null, requestTimeout).subscribe(\n function (responseBody) {\n if (!responseBody.ok) {\n commandExecuting = false;\n terminal.error(responseBody.error);\n terminal.resume();\n } \n },\n function () {\n onRpcError(terminal);\n }\n ); \n}\n\nfunction onRpcError(terminal) {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.resume();\n}\n\nfunction pollCommandStatus(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('getCommandStatus', null, requestTimeout).subscribe(\n function (commandStatusResponse) {\n for (var i=0;i",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-knob-control-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
+ }
+ },
+ {
+ "alias": "switch_control",
+ "name": "Switch Control",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to send the RPC call to device when user toggle the switch. Advanced widget settings allow you to configure how to fetch the initial value of the switch.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 4,
+ "sizeY": 2.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-switch-control-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
+ }
+ },
+ {
+ "alias": "round_switch",
+ "name": "Round switch",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to send the RPC call to device when user toggle the switch. Advanced widget settings allow you to configure how to fetch the initial value of the switch.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 2.5,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-round-switch-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
+ }
+ },
+ {
+ "alias": "led_indicator",
+ "name": "Led indicator",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAABCoklEQVR42u19aZBkV3XmPS8zq6qr90ZILQkEaEXsm0ACg9hNBCZmBsZAYMcYRxA22GGH/csRjvAP//QPhyPswI7xTBgCM8yAPfaETdh4AAMDQgIEQmKxQGhF+95bdVVl5jtz7j3rfZlZ3S1R1ZXtTpWqX77KfPnyvu+d5TsbIGI6+zj7+Fk/mrNLcPZxFlhnH3Pz6J/B3+2GG25YXV196Utfun///sm/Hj16dOfOnQDwFI58/fXXr62tvexlL9u3bx89PX78+GAw6Pf7Z/G0TYH1wAMP/M3f/A1d7N/6rd96+kf70pe+9MQTTzzrWc+aBNYXv/jFf/mXf3n961//rne96ykc+V//9V8PHTp00UUXEbDonP/sz/5s9+7dv/d7v9c0ZzXAtgQWiYF77733qUmRU3qMx2P7/TQfbduSA0S/n/6hvlEez3/+89/+9refBdZcPujKXX311SRmnv6hLrzwwj/4gz8gVfj0xdXhw4fpvjr33HPPSqzT8LjvvvtuuummJ598ktTQK1/5yvPPP9/+RBYVWT+km0j3vfzlL9/gILfeeusdd9xBuuxFL3oRa0YSlm984xu///3v33777YuLi7T/8ssvj0f++te//uCDD9KRX/3qV8dD0ZnQnxYWFt761rfazn8rDzrmOeecc9VVV7Epxg/Szt/+9rcffvjhpaWl5zznOXSehMjhcPj5z3/+rrvu4i/4T//0T/TVzjvvPHq6srJy44033n///bT9vOc9j/abMUf76TgveclL6I333HPPW97yFn7LWWCd8uOrX/3qZz/7WePevva1r33gAx+gleVr/9GPfvShhx6yP21wHELVl7/85de85jUMrOuuu45s+R/96EcESjP8f+mXfokMf7bN6ch0Cc1yjzr0yJEjdKjl5WUGFp3bpz/96e985zv2gq985Ssf/OAHL730Utqmj/jEJz5BMOr1enQQ+hQ62oc//OHRaEQH4dc/VB4EekIJgexjH/sYSTL+Ex2WQPxrv/Zr5HbQ0+9973sE35tvvpnASk8Ni2eBdWqPn/70p4QqUjoEpuc+97m0rP+7PMgoIYFBUoeux969e9///veTnCBj5Qtf+MIpHZ+O/Lu/+7s7duygT7nlllsI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",
+ "description": "Visualize the state of the device. Fetches the value from device using RPC or using attribute subscription.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 2.5,
+ "sizeY": 2.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-led-indicator-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
+ }
+ },
+ {
+ "alias": "rpcbutton",
+ "name": "RPC Button",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAN8SURBVHja7dvbSxRRHAfw/X9+bhcvrJZSmllhZFSCWBDaY4FUhBRZCQXR7akLyoJYlqxU0g0tukF0oYQM3bxsYVRgbuuyu7k7szNn9vSb2Wx9qB56ac72/T7MHGYZmA/nMjvDbzzSTkbpOASPw7CUjkPx2AwhTNNQNKYphE1hiCXMtK5pWkrB8GXraZMl0sMOQ9dEdqApmIzQdIMlDDH1lLIMh5LSTYZYIp1MS6XDAGF5uEPiltoQK85dwhAtKhVPVLMhRj5ADAcyqzpkNgtJqQ9JAQIIIIAAAggggAACCCB/8wog8btfAjd+f9Y3t0GmW5bS8rO/fvavaXB2jWVlZbVHwjLC+7L1x2P8rN1ZQcWtX90EyWws7r7bSuf+BKnz+f1t3norTPV+/0HvNiFP0+5b54sahYsgn+mQrdksZfL6hYcZGe2betoxyG/LtP7OiXlIJW/a6F2Y9nPjKI3MLtrBjbM05CJIsqT6tdOI1BbWFuyRQVpbupw7yGygqqLCBZCTNJqFdNGTAQpwY2bwo5vmyL1iqr+uS3l4yaTsoaEg7bT0VXWyn/xyquQHpDz4JuCrFWHaG48OrSuJXqLHLly1Yl01VDkuq6r7+i5Qd5ABsqlctlMiN0eI0/BZhu09rXggu10J4TVoYMlWWeKr51x2IM3lcp83k4NUfAqVbrQYsn3w/lvuvTt0lQ9Hn027CNJLt3m7YaXctIaXYCHnIWfog5Src3Okg/pldo7Ys8O7S9qHXrgIEllWee35uYJ9MkAHXgVq3s9Dxguan56iHCRZUa3/hPDAa3/U7dtiuGloTTZ7aXFrnO9xPio8Zs5D5JViaqpbsGpdpK4cxDxRRItbpl02R1Jfsje2zIy+8LCI/ekkc0a4b7Lj3y8ggAACCCCAAAIIIIAA8v9C8qaoRosqXngmsmVOXHgWVxsSyxaemXosJFR2mBMxpxRQpOPB0YihKsOIjAbjdnEml8vOhUaGX97s7VEwvTdfDo+E5kyn7pe7JDQWVDZjIbtDMk5JuZGcSyQScQXDlz2XNJyS8vwp8s+bzy6kzJsPYfIigAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggg/yTfATinNXQTM7TQAAAAAElFTkSuQmCC",
+ "description": "Allows to send RPC command when user press the button.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 4,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}",
+ "controllerScript": "var requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n \n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n if (rpcParams.length) {\n try {\n rpcParams = JSON.parse(rpcParams);\n } catch (e) {}\n }\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n\nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-send-rpc-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"oneWayElseTwoWay\":true,\"buttonText\":\"Send RPC\",\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"methodName\":\"rpcCommand\",\"methodParams\":\"{}\"},\"title\":\"RPC Button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_attributes",
+ "name": "Update device attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to send shared attribute update when user press the button.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 4,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n let entityAttributeType = self.ctx.settings.entityAttributeType;\n let entityParameters = JSON.parse(self.ctx.settings.entityParameters);\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton\n .bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n\n self.ctx.$scope.sendUpdate = function() {\n let attributes = [];\n for (let key in entityParameters) {\n attributes.push({\n \"key\": key,\n \"value\": entityParameters[key]\n });\n }\n \n let entityId = {\n entityType: \"DEVICE\",\n id: self.ctx.defaultSubscription.targetDeviceId\n };\n attributeService.saveEntityAttributes(entityId,\n entityAttributeType, attributes).subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n\n );\n };\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-device-attribute-widget-settings",
+ "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"entityParameters\":\"{}\",\"entityAttributeType\":\"SERVER_SCOPE\",\"buttonText\":\"Update device attribute\"},\"title\":\"Update device attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[]}"
+ }
+ },
+ {
+ "alias": "persistent_table",
+ "name": "Persistent RPC table",
+ "image": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yMDAgMEgwVjE2MEgyMDBWMFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yMDAgMTIwSDBWMTIxSDIwMFYxMjBaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0yMDAgODBIMFY4MUgyMDBWODBaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0yMDAgMzlIMFY0MEgyMDBWMzlaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0xNS42Njg1IDE5Ljk4NjhIMTQuMTUzOFYyM0gxMi43OTQ5VjE1LjE3OTdIMTUuNTQ0OUMxNi40NDczIDE1LjE3OTcgMTcuMTQzNyAxNS4zODIgMTcuNjM0MyAxNS43ODY2QzE4LjEyNDggMTYuMTkxMiAxOC4zNzAxIDE2Ljc3NjcgMTguMzcwMSAxNy41NDNDMTguMzcwMSAxOC4wNjU4IDE4LjI0MyAxOC41MDQ0IDE3Ljk4ODggMTguODU4OUMxNy43MzgxIDE5LjIwOTggMTcuMzg3MiAxOS40ODAxIDE2LjkzNiAxOS42Njk5TDE4LjY5MjQgMjIuOTMwMlYyM0gxNy4yMzY4TDE1LjY2ODUgMTkuOTg2OFpNMTQuMTUzOCAxOC44OTY1SDE1LjU1MDNDMTYuMDA4NiAxOC44OTY1IDE2LjM2NjcgMTguNzgxOSAxNi42MjQ1IDE4LjU1MjdDMTYuODgyMyAxOC4zMiAxNy4wMTEyIDE4LjAwMzEgMTcuMDExMiAxNy42MDIxQzE3LjAxMTIgMTcuMTgzMSAxNi44OTEzIDE2Ljg1OSAxNi42NTE0IDE2LjYyOTlDMTYuNDE1IDE2LjQwMDcgMTYuMDYwNSAxNi4yODI2IDE1LjU4NzkgMTYuMjc1NEgxNC4xNTM4VjE4Ljg5NjVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0yMS4wMTgxIDIwLjA5NDJWMjNIMTkuNjU5MlYxNS4xNzk3SDIyLjY1MDlDMjMuNTI0NiAxNS4xNzk3IDI0LjIxNzQgMTUuNDA3MSAyNC43Mjk1IDE1Ljg2MThDMjUuMjQ1MSAxNi4zMTY2IDI1LjUwMjkgMTYuOTE4MSAyNS41MDI5IDE3LjY2NjVDMjUuNTAyOSAxOC40MzI4IDI1LjI1MDUgMTkuMDI5IDI0Ljc0NTYgMTkuNDU1MUMyNC4yNDQzIDE5Ljg4MTIgMjMuNTQwNyAyMC4wOTQyIDIyLjYzNDggMjAuMDk0MkgyMS4wMTgxWk0yMS4wMTgxIDE5LjAwMzlIMjIuNjUwOUMyMy4xMzQzIDE5LjAwMzkgMjMuNTAzMSAxOC44OTExIDIzLjc1NzMgMTguNjY1NUMyNC4wMTE2IDE4LjQzNjQgMjQuMTM4NyAxOC4xMDY5IDI0LjEzODcgMTcuNjc3MkMyNC4xMzg3IDE3LjI1NDcgMjQuMDA5OCAxNi45MTgxIDIzLjc1MiAxNi42Njc1QzIzLjQ5NDEgMTYuNDEzMiAyMy4xMzk2IDE2LjI4MjYgMjIuNjg4NSAxNi4yNzU0SDIxLjAxODFWMTkuMDAzOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTMyLjY2MjYgMjAuNDU0MUMzMi41ODM4IDIxLjI4ODQgMzIuMjc1OSAyMS45NDAxIDMxLjczODggMjIuNDA5MkMzMS4yMDE3IDIyLjg3NDcgMzAuNDg3MyAyMy4xMDc0IDI5LjU5NTcgMjMuMTA3NEMyOC45NzI3IDIzLjEwNzQgMjguNDIzIDIyLjk2MDYgMjcuOTQ2OCAyMi42NjdDMjcuNDc0MSAyMi4zNjk4IDI3LjEwODkgMjEuOTQ5MSAyNi44NTExIDIxLjQwNDhDMjYuNTkzMyAyMC44NjA1IDI2LjQ1OSAyMC4yMjg1IDI2LjQ0ODIgMTkuNTA4OFYxOC43NzgzQzI2LjQ0ODIgMTguMDQwNyAyNi41Nzg5IDE3LjM5MDggMjYuODQwMyAxNi44Mjg2QzI3LjEwMTcgMTYuMjY2NCAyNy40NzU5IDE1LjgzMzIgMjcuOTYyOSAxNS41Mjg4QzI4LjQ1MzUgMTUuMjI0NCAyOS4wMTkyIDE1LjA3MjMgMjkuNjYwMiAxNS4wNzIzQzMwLjUyMzEgMTUuMDcyMyAzMS4yMTc4IDE1LjMwNjggMzEuNzQ0MSAxNS43NzU5QzMyLjI3MDUgMTYuMjQ1IDMyLjU3NjcgMTYuOTA3NCAzMi42NjI2IDE3Ljc2MzJIMzEuMzA5MUMzMS4yNDQ2IDE3LjIwMSAzMS4wNzk5IDE2Ljc5NjQgMzAuODE0OSAxNi41NDkzQzMwLjU1MzUgMTYuMjk4NyAzMC4xNjg2IDE2LjE3MzMgMjkuNjYwMiAxNi4xNzMzQzI5LjA2OTMgMTYuMTczMyAyOC42MTQ2IDE2LjM5IDI4LjI5NTkgMTYuODIzMkMyNy45ODA4IDE3LjI1MjkgMjcuODE5NyAxNy44ODQ5IDI3LjgxMjUgMTguNzE5MlYxOS40MTIxQzI3LjgxMjUgMjAuMjU3MiAyNy45NjI5IDIwLjkwMTcgMjguMjYzNyAyMS4zNDU3QzI4LjU2OCAyMS43ODk3IDI5LjAxMiAyMi4wMTE3IDI5LjU5NTcgMjIuMDExN0MzMC4xMjkyIDIyLjAxMTcgMzAuNTMwMyAyMS44OTE4IDMwLjc5ODggMjEuNjUxOUMzMS4wNjc0IDIxLjQxMTkgMzEuMjM3NSAyMS4wMTI3IDMxLjMwOTEgMjAuNDU0MUgzMi42NjI2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMzguMDU1MiAyM0gzNi43MDE3VjE1LjE3OTdIMzguMDU1MlYyM1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTM5LjcyNTYgMjNWMTUuMTc5N0g0Mi4wMzUyQzQyLjcyNjIgMTUuMTc5NyA0My4zMzg1IDE1LjMzMzcgNDMuODcyMSAxNS42NDE2QzQ0LjQwOTIgMTUuOTQ5NSA0NC44MjQ1IDE2LjM4NjQgNDUuMTE4MiAxNi45NTIxQzQ1LjQxMTggMTcuNTE3OSA0NS41NTg2IDE4LjE2NiA0NS41NTg2IDE4Ljg5NjVWMTkuMjg4NkM0NS41NTg2IDIwLjAyOTggNDUuNDEgMjAuNjgxNSA0NS4xMTI4IDIxLjI0MzdDNDQuODE5MiAyMS44MDU4IDQ0LjM5ODQgMjIuMjM5MSA0My44NTA2IDIyLjU0MzVDNDMuMzA2MyAyMi44NDc4IDQyLjY4MTUgMjMgNDEuOTc2MSAyM0gzOS43MjU2Wk00MS4wODQ1IDE2LjI3NTRWMjEuOTE1SDQxLjk3MDdDNDIuNjgzMyAyMS45MTUgNDMuMjI5MyAyMS42OTMgNDMuNjA4OSAyMS4yNDlDNDMuOTkyIDIwLjgwMTQgNDQuMTg3MiAyMC4xNjA1IDQ0LjE5NDMgMTkuMzI2MlYxOC44OTExQzQ0LjE5NDMgMTguMDQyNSA0NC4wMDk5IDE3LjM5NDQgNDMuNjQxMSAxNi45NDY4QzQzLjI3MjMgMTYuNDk5MiA0Mi43MzcgMTYuMjc1NCA0Mi4wMzUyIDE2LjI3NTRINDEuMDg0NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTYxLjU1NjYgMTUuMTc5N0w2My44MTI1IDIxLjE3MzhMNjYuMDYzIDE1LjE3OTdINjcuODE5M1YyM0g2Ni40NjU4VjIwLjQyMTlMNjYuNjAwMSAxNi45NzM2TDY0LjI5MDUgMjNINjMuMzE4NEw2MS4wMTQyIDE2Ljk3OUw2MS4xNDg0IDIwLjQyMTlWMjNINTkuNzk0OVYxNS4xNzk3SDYxLjU1NjZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03MS44NjM4IDIzLjEwNzRDNzEuMDM2NiAyMy4xMDc0IDcwLjM2NTIgMjIuODQ3OCA2OS44NDk2IDIyLjMyODZDNjkuMzM3NiAyMS44MDU4IDY5LjA4MTUgMjEuMTExMiA2OS4wODE1IDIwLjI0NDZWMjAuMDgzNUM2OS4wODE1IDE5LjUwMzQgNjkuMTkyNSAxOC45ODYgNjkuNDE0NiAxOC41MzEyQzY5LjY0MDEgMTguMDcyOSA2OS45NTUyIDE3LjcxNjYgNzAuMzU5OSAxNy40NjI0QzcwLjc2NDUgMTcuMjA4MiA3MS4yMTU3IDE3LjA4MTEgNzEuNzEzNCAxNy4wODExQzcyLjUwNDcgMTcuMDgxMSA3My4xMTUyIDE3LjMzMzUgNzMuNTQ0OSAxNy44Mzg0QzczLjk3ODIgMTguMzQzMyA3NC4xOTQ4IDE5LjA1NzYgNzQuMTk0OCAxOS45ODE0VjIwLjUwNzhINzAuMzk3NUM3MC40MzY4IDIwLjk4NzYgNzAuNTk2MiAyMS4zNjcyIDcwLjg3NTUgMjEuNjQ2NUM3MS4xNTg0IDIxLjkyNTggNzEuNTEyOSAyMi4wNjU0IDcxLjkzOSAyMi4wNjU0QzcyLjUzNjkgMjIuMDY1NCA3My4wMjM5IDIxLjgyMzcgNzMuMzk5OSAyMS4zNDAzTDc0LjEwMzUgMjIuMDExN0M3My44NzA4IDIyLjM1OSA3My41NTkyIDIyLjYyOTQgNzMuMTY4OSAyMi44MjI4QzcyLjc4MjIgMjMuMDEyNSA3Mi4zNDcyIDIzLjEwNzQgNzEuODYzOCAyMy4xMDc0Wk03MS43MDggMTguMTI4NEM3MS4zNDk5IDE4LjEyODQgNzEuMDU5OSAxOC4yNTM3IDcwLjgzNzkgMTguNTA0NEM3MC42MTk1IDE4Ljc1NSA3MC40Nzk4IDE5LjEwNDIgNzAuNDE4OSAxOS41NTE4SDcyLjkwNThWMTkuNDU1MUM3Mi44NzcxIDE5LjAxODIgNzIuNzYwNyAxOC42ODg4IDcyLjU1NjYgMTguNDY2OEM3Mi4zNTI1IDE4LjI0MTIgNzIuMDY5NyAxOC4xMjg0IDcxLjcwOCAxOC4xMjg0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNzguNDcwMiAyMS40MjA5Qzc4LjQ3MDIgMjEuMTg4MiA3OC4zNzM1IDIxLjAxMDkgNzguMTgwMiAyMC44ODkyQzc3Ljk5MDQgMjAuNzY3NCA3Ny42NzM1IDIwLjY2IDc3LjIyOTUgMjAuNTY2OUM3Ni43ODU1IDIwLjQ3MzggNzYuNDE0OSAyMC4zNTU2IDc2LjExNzcgMjAuMjEyNEM3NS40NjYgMTkuODk3MyA3NS4xNDAxIDE5LjQ0MDggNzUuMTQwMSAxOC44NDI4Qzc1LjE0MDEgMTguMzQxNSA3NS4zNTE0IDE3LjkyMjUgNzUuNzczOSAxNy41ODU5Qzc2LjE5NjUgMTcuMjQ5MyA3Ni43MzM2IDE3LjA4MTEgNzcuMzg1MyAxNy4wODExQzc4LjA3OTkgMTcuMDgxMSA3OC42NDAzIDE3LjI1MjkgNzkuMDY2NCAxNy41OTY3Qzc5LjQ5NjEgMTcuOTQwNCA3OS43MTA5IDE4LjM4NjIgNzkuNzEwOSAxOC45MzQxSDc4LjQwNThDNzguNDA1OCAxOC42ODM0IDc4LjMxMjcgMTguNDc1NyA3OC4xMjY1IDE4LjMxMUM3Ny45NDAzIDE4LjE0MjcgNzcuNjkzMiAxOC4wNTg2IDc3LjM4NTMgMTguMDU4NkM3Ny4wOTg4IDE4LjA1ODYgNzYuODY0MyAxOC4xMjQ4IDc2LjY4MTYgMTguMjU3M0M3Ni41MDI2IDE4LjM4OTggNzYuNDEzMSAxOC41NjcxIDc2LjQxMzEgMTguNzg5MUM3Ni40MTMxIDE4Ljk4OTYgNzYuNDk3MiAxOS4xNDUzIDc2LjY2NTUgMTkuMjU2M0M3Ni44MzM4IDE5LjM2NzQgNzcuMTc0IDE5LjQ4MDEgNzcuNjg2IDE5LjU5NDdDNzguMTk4MSAxOS43MDU3IDc4LjU5OTEgMTkuODQgNzguODg5MiAxOS45OTc2Qzc5LjE4MjggMjAuMTUxNSA3OS4zOTk0IDIwLjMzNzcgNzkuNTM5MSAyMC41NTYyQzc5LjY4MjMgMjAuNzc0NiA3OS43NTM5IDIxLjAzOTYgNzkuNzUzOSAyMS4zNTExQzc5Ljc1MzkgMjEuODczOSA3OS41MzczIDIyLjI5ODIgNzkuMTA0IDIyLjYyNEM3OC42NzA3IDIyLjk0NjMgNzguMTAzMiAyMy4xMDc0IDc3LjQwMTQgMjMuMTA3NEM3Ni45MjUxIDIzLjEwNzQgNzYuNTAwOCAyMy4wMjE1IDc2LjEyODQgMjIuODQ5NkM3NS43NTYgMjIuNjc3NyA3NS40NjYgMjIuNDQxNCA3NS4yNTgzIDIyLjE0MDZDNzUuMDUwNiAyMS44Mzk4IDc0Ljk0NjggMjEuNTE1OCA3NC45NDY4IDIxLjE2ODVINzYuMjE0NEM3Ni4yMzIzIDIxLjQ3NjQgNzYuMzQ4NiAyMS43MTQ1IDc2LjU2MzUgMjEuODgyOEM3Ni43NzgzIDIyLjA0NzUgNzcuMDYzIDIyLjEyOTkgNzcuNDE3NSAyMi4xMjk5Qzc3Ljc2MTIgMjIuMTI5OSA3OC4wMjI2IDIyLjA2NTQgNzguMjAxNyAyMS45MzY1Qzc4LjM4MDcgMjEuODA0IDc4LjQ3MDIgMjEuNjMyMiA3OC40NzAyIDIxLjQyMDlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04NC4xNTI4IDIxLjQyMDlDODQuMTUyOCAyMS4xODgyIDg0LjA1NjIgMjEuMDEwOSA4My44NjI4IDIwLjg4OTJDODMuNjczIDIwLjc2NzQgODMuMzU2MSAyMC42NiA4Mi45MTIxIDIwLjU2NjlDODIuNDY4MSAyMC40NzM4IDgyLjA5NzUgMjAuMzU1NiA4MS44MDAzIDIwLjIxMjRDODEuMTQ4NiAxOS44OTczIDgwLjgyMjggMTkuNDQwOCA4MC44MjI4IDE4Ljg0MjhDODAuODIyOCAxOC4zNDE1IDgxLjAzNCAxNy45MjI1IDgxLjQ1NjUgMTcuNTg1OUM4MS44NzkxIDE3LjI0OTMgODIuNDE2MiAxNy4wODExIDgzLjA2NzkgMTcuMDgxMUM4My43NjI1IDE3LjA4MTEgODQuMzIyOSAxNy4yNTI5IDg0Ljc0OSAxNy41OTY3Qzg1LjE3ODcgMTcuOTQwNCA4NS4zOTM2IDE4LjM4NjIgODUuMzkzNiAxOC45MzQxSDg0LjA4ODRDODQuMDg4NCAxOC42ODM0IDgzLjk5NTMgMTguNDc1NyA4My44MDkxIDE4LjMxMUM4My42MjI5IDE4LjE0MjcgODMuMzc1OCAxOC4wNTg2IDgzLjA2NzkgMTguMDU4NkM4Mi43ODE0IDE4LjA1ODYgODIuNTQ2OSAxOC4xMjQ4IDgyLjM2NDMgMTguMjU3M0M4Mi4xODUyIDE4LjM4OTggODIuMDk1NyAxOC41NjcxIDgyLjA5NTcgMTguNzg5MUM4Mi4wOTU3IDE4Ljk4OTYgODIuMTc5OSAxOS4xNDUzIDgyLjM0ODEgMTkuMjU2M0M4Mi41MTY0IDE5LjM2NzQgODIuODU2NiAxOS40ODAxIDgzLjM2ODcgMTkuNTk0N0M4My44ODA3IDE5LjcwNTcgODQuMjgxNyAxOS44NCA4NC41NzE4IDE5Ljk5NzZDODQuODY1NCAyMC4xNTE1IDg1LjA4MiAyMC4zMzc3IDg1LjIyMTcgMjAuNTU2MkM4NS4zNjQ5IDIwLjc3NDYgODUuNDM2NSAyMS4wMzk2IDg1LjQzNjUgMjEuMzUxMUM4NS40MzY1IDIxLjg3MzkgODUuMjE5OSAyMi4yOTgyIDg0Ljc4NjYgMjIuNjI0Qzg0LjM1MzQgMjIuOTQ2MyA4My43ODU4IDIzLjEwNzQgODMuMDg0IDIzLjEwNzRDODIuNjA3NyAyMy4xMDc0IDgyLjE4MzQgMjMuMDIxNSA4MS44MTEgMjIuODQ5NkM4MS40Mzg2IDIyLjY3NzcgODEuMTQ4NiAyMi40NDE0IDgwLjk0MDkgMjIuMTQwNkM4MC43MzMyIDIxLjgzOTggODAuNjI5NCAyMS41MTU4IDgwLjYyOTQgMjEuMTY4NUg4MS44OTdDODEuOTE0OSAyMS40NzY0IDgyLjAzMTIgMjEuNzE0NSA4Mi4yNDYxIDIxLjg4MjhDODIuNDYwOSAyMi4wNDc1IDgyLjc0NTYgMjIuMTI5OSA4My4xMDAxIDIyLjEyOTlDODMuNDQzOCAyMi4xMjk5IDgzLjcwNTIgMjIuMDY1NCA4My44ODQzIDIxLjkzNjVDODQuMDYzMyAyMS44MDQgODQuMTUyOCAyMS42MzIyIDg0LjE1MjggMjEuNDIwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTkwLjA1MDMgMjNDODkuOTkzIDIyLjg4OSA4OS45NDI5IDIyLjcwODIgODkuODk5OSAyMi40NTc1Qzg5LjQ4NDUgMjIuODkwOCA4OC45NzYxIDIzLjEwNzQgODguMzc0NSAyMy4xMDc0Qzg3Ljc5MDkgMjMuMTA3NCA4Ny4zMTQ2IDIyLjk0MDkgODYuOTQ1OCAyMi42MDc5Qzg2LjU3NyAyMi4yNzQ5IDg2LjM5MjYgMjEuODYzMSA4Ni4zOTI2IDIxLjM3MjZDODYuMzkyNiAyMC43NTMxIDg2LjYyMTcgMjAuMjc4NiA4Ny4wODAxIDE5Ljk0OTJDODcuNTQyIDE5LjYxNjIgODguMjAwOCAxOS40NDk3IDg5LjA1NjYgMTkuNDQ5N0g4OS44NTY5VjE5LjA2ODRDODkuODU2OSAxOC43Njc2IDg5Ljc3MjggMTguNTI3NyA4OS42MDQ1IDE4LjM0ODZDODkuNDM2MiAxOC4xNjYgODkuMTgwMiAxOC4wNzQ3IDg4LjgzNjQgMTguMDc0N0M4OC41MzkyIDE4LjA3NDcgODguMjk1NyAxOC4xNDk5IDg4LjEwNiAxOC4zMDAzQzg3LjkxNjIgMTguNDQ3MSA4Ny44MjEzIDE4LjYzNTEgODcuODIxMyAxOC44NjQzSDg2LjUxNjFDODYuNTE2MSAxOC41NDU2IDg2LjYyMTcgMTguMjQ4NCA4Ni44MzMgMTcuOTcyN0M4Ny4wNDQzIDE3LjY5MzQgODcuMzMwNyAxNy40NzQ5IDg3LjY5MjQgMTcuMzE3NEM4OC4wNTc2IDE3LjE1OTggODguNDY0IDE3LjA4MTEgODguOTExNiAxNy4wODExQzg5LjU5MiAxNy4wODExIDkwLjEzNDQgMTcuMjUyOSA5MC41MzkxIDE3LjU5NjdDOTAuOTQzNyAxNy45MzY4IDkxLjE1MTQgMTguNDE2NyA5MS4xNjIxIDE5LjAzNjFWMjEuNjU3MkM5MS4xNjIxIDIyLjE4IDkxLjIzNTUgMjIuNTk3MiA5MS4zODIzIDIyLjkwODdWMjNIOTAuMDUwM1pNODguNjE2MiAyMi4wNjAxQzg4Ljg3NCAyMi4wNjAxIDg5LjExNTcgMjEuOTk3NCA4OS4zNDEzIDIxLjg3MjFDODkuNTcwNSAyMS43NDY3IDg5Ljc0MjQgMjEuNTc4NSA4OS44NTY5IDIxLjM2NzJWMjAuMjcxNUg4OS4xNTMzQzg4LjY2OTkgMjAuMjcxNSA4OC4zMDY1IDIwLjM1NTYgODguMDYzIDIwLjUyMzlDODcuODE5NSAyMC42OTIyIDg3LjY5NzggMjAuOTMwMyA4Ny42OTc4IDIxLjIzODNDODcuNjk3OCAyMS40ODg5IDg3Ljc4MDEgMjEuNjg5NSA4Ny45NDQ4IDIxLjgzOThDODguMTEzMSAyMS45ODY3IDg4LjMzNjkgMjIuMDYwMSA4OC42MTYyIDIyLjA2MDFaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05Mi4zMDA4IDIwLjA1MTNDOTIuMzAwOCAxOS4xNDg5IDkyLjUxMiAxOC40MjkyIDkyLjkzNDYgMTcuODkyMUM5My4zNjA3IDE3LjM1MTQgOTMuOTI0NiAxNy4wODExIDk0LjYyNjUgMTcuMDgxMUM5NS4yODg5IDE3LjA4MTEgOTUuODA5OSAxNy4zMTIgOTYuMTg5NSAxNy43NzM5TDk2LjI0ODUgMTcuMTg4NUg5Ny40MjQ4VjIyLjgyMjhDOTcuNDI0OCAyMy41ODU0IDk3LjE4NjcgMjQuMTg3IDk2LjcxMDQgMjQuNjI3NEM5Ni4yMzc4IDI1LjA2NzkgOTUuNTk4NiAyNS4yODgxIDk0Ljc5MyAyNS4yODgxQzk0LjM2NjkgMjUuMjg4MSA5My45NDk3IDI1LjE5ODYgOTMuNTQxNSAyNS4wMTk1QzkzLjEzNjkgMjQuODQ0MSA5Mi44Mjg5IDI0LjYxMzEgOTIuNjE3NyAyNC4zMjY3TDkzLjIzNTQgMjMuNTQyNUM5My42MzY0IDI0LjAxODcgOTQuMTMwNSAyNC4yNTY4IDk0LjcxNzggMjQuMjU2OEM5NS4xNTEgMjQuMjU2OCA5NS40OTMgMjQuMTM4NyA5NS43NDM3IDIzLjkwMjNDOTUuOTk0MyAyMy42Njk2IDk2LjExOTYgMjMuMzI1OCA5Ni4xMTk2IDIyLjg3MTFWMjIuNDc5Qzk1Ljc0MzcgMjIuODk3OSA5NS4yNDI0IDIzLjEwNzQgOTQuNjE1NyAyMy4xMDc0QzkzLjkzNTQgMjMuMTA3NCA5My4zNzg2IDIyLjgzNzEgOTIuOTQ1MyAyMi4yOTY0QzkyLjUxNTYgMjEuNzU1NyA5Mi4zMDA4IDIxLjAwNzMgOTIuMzAwOCAyMC4wNTEzWk05My42MDA2IDIwLjE2NDFDOTMuNjAwNiAyMC43NDc3IDkzLjcxODggMjEuMjA3OCA5My45NTUxIDIxLjU0NDRDOTQuMTk1IDIxLjg3NzQgOTQuNTI2MiAyMi4wNDM5IDk0Ljk0ODcgMjIuMDQzOUM5NS40NzUxIDIyLjA0MzkgOTUuODY1NCAyMS44MTg0IDk2LjExOTYgMjEuMzY3MlYxOC44MTA1Qzk1Ljg3MjYgMTguMzcwMSA5NS40ODU4IDE4LjE0OTkgOTQuOTU5NSAxOC4xNDk5Qzk0LjUyOTggMTguMTQ5OSA5NC4xOTUgMTguMzIgOTMuOTU1MSAxOC42NjAyQzkzLjcxODggMTkuMDAwMyA5My42MDA2IDE5LjUwMTYgOTMuNjAwNiAyMC4xNjQxWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTAxLjMzIDIzLjEwNzRDMTAwLjUwMiAyMy4xMDc0IDk5LjgzMTEgMjIuODQ3OCA5OS4zMTU0IDIyLjMyODZDOTguODAzNCAyMS44MDU4IDk4LjU0NzQgMjEuMTExMiA5OC41NDc0IDIwLjI0NDZWMjAuMDgzNUM5OC41NDc0IDE5LjUwMzQgOTguNjU4NCAxOC45ODYgOTguODgwNCAxOC41MzEyQzk5LjEwNiAxOC4wNzI5IDk5LjQyMTEgMTcuNzE2NiA5OS44MjU3IDE3LjQ2MjRDMTAwLjIzIDE3LjIwODIgMTAwLjY4MSAxNy4wODExIDEwMS4xNzkgMTcuMDgxMUMxMDEuOTcxIDE3LjA4MTEgMTAyLjU4MSAxNy4zMzM1IDEwMy4wMTEgMTcuODM4NEMxMDMuNDQ0IDE4LjM0MzMgMTAzLjY2MSAxOS4wNTc2IDEwMy42NjEgMTkuOTgxNFYyMC41MDc4SDk5Ljg2MzNDOTkuOTAyNyAyMC45ODc2IDEwMC4wNjIgMjEuMzY3MiAxMDAuMzQxIDIxLjY0NjVDMTAwLjYyNCAyMS45MjU4IDEwMC45NzkgMjIuMDY1NCAxMDEuNDA1IDIyLjA2NTRDMTAyLjAwMyAyMi4wNjU0IDEwMi40OSAyMS44MjM3IDEwMi44NjYgMjEuMzQwM0wxMDMuNTY5IDIyLjAxMTdDMTAzLjMzNyAyMi4zNTkgMTAzLjAyNSAyMi42Mjk0IDEwMi42MzUgMjIuODIyOEMxMDIuMjQ4IDIzLjAxMjUgMTAxLjgxMyAyMy4xMDc0IDEwMS4zMyAyMy4xMDc0Wk0xMDEuMTc0IDE4LjEyODRDMTAwLjgxNiAxOC4xMjg0IDEwMC41MjYgMTguMjUzNyAxMDAuMzA0IDE4LjUwNDRDMTAwLjA4NSAxOC43NTUgOTkuOTQ1NiAxOS4xMDQyIDk5Ljg4NDggMTkuNTUxOEgxMDIuMzcyVjE5LjQ1NTFDMTAyLjM0MyAxOS4wMTgyIDEwMi4yMjcgMTguNjg4OCAxMDIuMDIyIDE4LjQ2NjhDMTAxLjgxOCAxOC4yNDEyIDEwMS41MzUgMTguMTI4NCAxMDEuMTc0IDE4LjEyODRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xMDkuMDUzIDE1Ljc3NTlWMTcuMTg4NUgxMTAuMDc5VjE4LjE1NTNIMTA5LjA1M1YyMS4zOTk0QzEwOS4wNTMgMjEuNjIxNCAxMDkuMDk2IDIxLjc4MjYgMTA5LjE4MiAyMS44ODI4QzEwOS4yNzIgMjEuOTc5NSAxMDkuNDI5IDIyLjAyNzggMTA5LjY1NSAyMi4wMjc4QzEwOS44MDUgMjIuMDI3OCAxMDkuOTU3IDIyLjAwOTkgMTEwLjExMSAyMS45NzQxVjIyLjk4MzlDMTA5LjgxNCAyMy4wNjYyIDEwOS41MjggMjMuMTA3NCAxMDkuMjUyIDIzLjEwNzRDMTA4LjI0OSAyMy4xMDc0IDEwNy43NDggMjIuNTU0MiAxMDcuNzQ4IDIxLjQ0NzhWMTguMTU1M0gxMDYuNzkyVjE3LjE4ODVIMTA3Ljc0OFYxNS43NzU5SDEwOS4wNTNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xMTMuMTE0IDIxLjEzMDlMMTE0LjI5NSAxNy4xODg1SDExNS42ODdMMTEzLjM3NyAyMy44ODA5QzExMy4wMjIgMjQuODU4NCAxMTIuNDIxIDI1LjM0NzIgMTExLjU3MiAyNS4zNDcyQzExMS4zODIgMjUuMzQ3MiAxMTEuMTczIDI1LjMxNDkgMTEwLjk0NCAyNS4yNTA1VjI0LjI0MDdMMTExLjE5MSAyNC4yNTY4QzExMS41MiAyNC4yNTY4IDExMS43NjcgMjQuMTk2IDExMS45MzIgMjQuMDc0MkMxMTIuMSAyMy45NTYxIDExMi4yMzMgMjMuNzU1NSAxMTIuMzMgMjMuNDcyN0wxMTIuNTE4IDIyLjk3MzFMMTEwLjQ3NyAxNy4xODg1SDExMS44ODRMMTEzLjExNCAyMS4xMzA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIxLjUzIDIwLjE1MzNDMTIxLjUzIDIxLjA1MjEgMTIxLjMyNiAyMS43NyAxMjAuOTE4IDIyLjMwNzFDMTIwLjUxIDIyLjg0MDcgMTE5Ljk2MiAyMy4xMDc0IDExOS4yNzQgMjMuMTA3NEMxMTguNjM3IDIzLjEwNzQgMTE4LjEyNyAyMi44OTc5IDExNy43NDQgMjIuNDc5VjI1LjIzNDRIMTE2LjQzOFYxNy4xODg1SDExNy42NDJMMTE3LjY5NSAxNy43NzkzQzExOC4wNzggMTcuMzEzOCAxMTguNTk5IDE3LjA4MTEgMTE5LjI1OCAxNy4wODExQzExOS45NjcgMTcuMDgxMSAxMjAuNTIyIDE3LjM0NiAxMjAuOTIzIDE3Ljg3NkMxMjEuMzI4IDE4LjQwMjMgMTIxLjUzIDE5LjEzNDYgMTIxLjUzIDIwLjA3MjhWMjAuMTUzM1pNMTIwLjIzIDIwLjA0MDVDMTIwLjIzIDE5LjQ2MDQgMTIwLjExNCAxOS4wMDAzIDExOS44ODEgMTguNjYwMkMxMTkuNjUyIDE4LjMyIDExOS4zMjMgMTguMTQ5OSAxMTguODkzIDE4LjE0OTlDMTE4LjM2IDE4LjE0OTkgMTE3Ljk3NiAxOC4zNzAxIDExNy43NDQgMTguODEwNVYyMS4zODg3QzExNy45OCAyMS44Mzk4IDExOC4zNjcgMjIuMDY1NCAxMTguOTA0IDIyLjA2NTRDMTE5LjMxOSAyMi4wNjU0IDExOS42NDMgMjEuODk4OSAxMTkuODc2IDIxLjU2NTlDMTIwLjExMiAyMS4yMjkzIDEyMC4yMyAyMC43MjA5IDEyMC4yMyAyMC4wNDA1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTI1LjE5OSAyMy4xMDc0QzEyNC4zNzIgMjMuMTA3NCAxMjMuNyAyMi44NDc4IDEyMy4xODUgMjIuMzI4NkMxMjIuNjczIDIxLjgwNTggMTIyLjQxNyAyMS4xMTEyIDEyMi40MTcgMjAuMjQ0NlYyMC4wODM1QzEyMi40MTcgMTkuNTAzNCAxMjIuNTI4IDE4Ljk4NiAxMjIuNzUgMTguNTMxMkMxMjIuOTc1IDE4LjA3MjkgMTIzLjI5IDE3LjcxNjYgMTIzLjY5NSAxNy40NjI0QzEyNC4wOTkgMTcuMjA4MiAxMjQuNTUxIDE3LjA4MTEgMTI1LjA0OCAxNy4wODExQzEyNS44NCAxNy4wODExIDEyNi40NSAxNy4zMzM1IDEyNi44OCAxNy44Mzg0QzEyNy4zMTMgMTguMzQzMyAxMjcuNTMgMTkuMDU3NiAxMjcuNTMgMTkuOTgxNFYyMC41MDc4SDEyMy43MzJDMTIzLjc3MiAyMC45ODc2IDEyMy45MzEgMjEuMzY3MiAxMjQuMjEgMjEuNjQ2NUMxMjQuNDkzIDIxLjkyNTggMTI0Ljg0OCAyMi4wNjU0IDEyNS4yNzQgMjIuMDY1NEMxMjUuODcyIDIyLjA2NTQgMTI2LjM1OSAyMS44MjM3IDEyNi43MzUgMjEuMzQwM0wxMjcuNDM4IDIyLjAxMTdDMTI3LjIwNiAyMi4zNTkgMTI2Ljg5NCAyMi42Mjk0IDEyNi41MDQgMjIuODIyOEMxMjYuMTE3IDIzLjAxMjUgMTI1LjY4MiAyMy4xMDc0IDEyNS4xOTkgMjMuMTA3NFpNMTI1LjA0MyAxOC4xMjg0QzEyNC42ODUgMTguMTI4NCAxMjQuMzk1IDE4LjI1MzcgMTI0LjE3MyAxOC41MDQ0QzEyMy45NTQgMTguNzU1IDEyMy44MTUgMTkuMTA0MiAxMjMuNzU0IDE5LjU1MThIMTI2LjI0MVYxOS40NTUxQzEyNi4yMTIgMTkuMDE4MiAxMjYuMDk2IDE4LjY4ODggMTI1Ljg5MiAxOC40NjY4QzEyNS42ODggMTguMjQxMiAxMjUuNDA1IDE4LjEyODQgMTI1LjA0MyAxOC4xMjg0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTQ0Ljg4MiAyMC45ODU4QzE0NC44ODIgMjAuNjQyMSAxNDQuNzYxIDIwLjM3NzEgMTQ0LjUxNyAyMC4xOTA5QzE0NC4yNzcgMjAuMDA0NyAxNDMuODQyIDE5LjgxNjcgMTQzLjIxMiAxOS42MjdDMTQyLjU4MiAxOS40MzcyIDE0Mi4wOCAxOS4yMjU5IDE0MS43MDggMTguOTkzMkMxNDAuOTk1IDE4LjU0NTYgMTQwLjYzOSAxNy45NjE5IDE0MC42MzkgMTcuMjQyMkMxNDAuNjM5IDE2LjYxMiAxNDAuODk1IDE2LjA5MjggMTQxLjQwNyAxNS42ODQ2QzE0MS45MjMgMTUuMjc2NCAxNDIuNTkxIDE1LjA3MjMgMTQzLjQxMSAxNS4wNzIzQzE0My45NTUgMTUuMDcyMyAxNDQuNDQgMTUuMTcyNSAxNDQuODY2IDE1LjM3M0MxNDUuMjkyIDE1LjU3MzYgMTQ1LjYyNyAxNS44NiAxNDUuODcxIDE2LjIzMjRDMTQ2LjExNCAxNi42MDEyIDE0Ni4yMzYgMTcuMDExMiAxNDYuMjM2IDE3LjQ2MjRIMTQ0Ljg4MkMxNDQuODgyIDE3LjA1NDIgMTQ0Ljc1MyAxNi43MzU1IDE0NC40OTYgMTYuNTA2M0MxNDQuMjQxIDE2LjI3MzYgMTQzLjg3NiAxNi4xNTcyIDE0My40IDE2LjE1NzJDMTQyLjk1NiAxNi4xNTcyIDE0Mi42MSAxNi4yNTIxIDE0Mi4zNjMgMTYuNDQxOUMxNDIuMTIgMTYuNjMxNyAxNDEuOTk4IDE2Ljg5NjYgMTQxLjk5OCAxNy4yMzY4QzE0MS45OTggMTcuNTIzMyAxNDIuMTMxIDE3Ljc2MzIgMTQyLjM5NiAxNy45NTY1QzE0Mi42NiAxOC4xNDYzIDE0My4wOTcgMTguMzMyNSAxNDMuNzA2IDE4LjUxNTFDMTQ0LjMxNSAxOC42OTQyIDE0NC44MDQgMTguOTAwMSAxNDUuMTcyIDE5LjEzMjhDMTQ1LjU0MSAxOS4zNjIgMTQ1LjgxMiAxOS42MjcgMTQ1Ljk4MyAxOS45Mjc3QzE0Ni4xNTUgMjAuMjI0OSAxNDYuMjQxIDIwLjU3NDEgMTQ2LjI0MSAyMC45NzUxQzE0Ni4yNDEgMjEuNjI2OCAxNDUuOTkxIDIyLjE0NiAxNDUuNDg5IDIyLjUzMjdDMTQ0Ljk5MiAyMi45MTU5IDE0NC4zMTUgMjMuMTA3NCAxNDMuNDU5IDIzLjEwNzRDMTQyLjg5MyAyMy4xMDc0IDE0Mi4zNzIgMjMuMDAzNiAxNDEuODk2IDIyLjc5NTlDMTQxLjQyMyAyMi41ODQ2IDE0MS4wNTUgMjIuMjk0NiAxNDAuNzkgMjEuOTI1OEMxNDAuNTI4IDIxLjU1NyAxNDAuMzk3IDIxLjEyNzMgMTQwLjM5NyAyMC42MzY3SDE0MS43NTZDMTQxLjc1NiAyMS4wODA3IDE0MS45MDMgMjEuNDI0NSAxNDIuMTk3IDIxLjY2OEMxNDIuNDkgMjEuOTExNSAxNDIuOTExIDIyLjAzMzIgMTQzLjQ1OSAyMi4wMzMyQzE0My45MzIgMjIuMDMzMiAxNDQuMjg2IDIxLjkzODMgMTQ0LjUyMiAyMS43NDg1QzE0NC43NjIgMjEuNTU1MiAxNDQuODgyIDIxLjMwMDkgMTQ0Ljg4MiAyMC45ODU4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTQ4Ljk0MyAxNS43NzU5VjE3LjE4ODVIMTQ5Ljk2OVYxOC4xNTUzSDE0OC45NDNWMjEuMzk5NEMxNDguOTQzIDIxLjYyMTQgMTQ4Ljk4NiAyMS43ODI2IDE0OS4wNzIgMjEuODgyOEMxNDkuMTYxIDIxLjk3OTUgMTQ5LjMxOSAyMi4wMjc4IDE0OS41NDQgMjIuMDI3OEMxNDkuNjk1IDIyLjAyNzggMTQ5Ljg0NyAyMi4wMDk5IDE1MC4wMDEgMjEuOTc0MVYyMi45ODM5QzE0OS43MDQgMjMuMDY2MiAxNDkuNDE3IDIzLjEwNzQgMTQ5LjE0MiAyMy4xMDc0QzE0OC4xMzkgMjMuMTA3NCAxNDcuNjM4IDIyLjU1NDIgMTQ3LjYzOCAyMS40NDc4VjE4LjE1NTNIMTQ2LjY4MlYxNy4xODg1SDE0Ny42MzhWMTUuNzc1OUgxNDguOTQzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTU0LjQ0MyAyM0MxNTQuMzg2IDIyLjg4OSAxNTQuMzM1IDIyLjcwODIgMTU0LjI5MiAyMi40NTc1QzE1My44NzcgMjIuODkwOCAxNTMuMzY5IDIzLjEwNzQgMTUyLjc2NyAyMy4xMDc0QzE1Mi4xODMgMjMuMTA3NCAxNTEuNzA3IDIyLjk0MDkgMTUxLjMzOCAyMi42MDc5QzE1MC45NyAyMi4yNzQ5IDE1MC43ODUgMjEuODYzMSAxNTAuNzg1IDIxLjM3MjZDMTUwLjc4NSAyMC43NTMxIDE1MS4wMTQgMjAuMjc4NiAxNTEuNDczIDE5Ljk0OTJDMTUxLjkzNSAxOS42MTYyIDE1Mi41OTMgMTkuNDQ5NyAxNTMuNDQ5IDE5LjQ0OTdIMTU0LjI1VjE5LjA2ODRDMTU0LjI1IDE4Ljc2NzYgMTU0LjE2NSAxOC41Mjc3IDE1My45OTcgMTguMzQ4NkMxNTMuODI5IDE4LjE2NiAxNTMuNTczIDE4LjA3NDcgMTUzLjIyOSAxOC4wNzQ3QzE1Mi45MzIgMTguMDc0NyAxNTIuNjg4IDE4LjE0OTkgMTUyLjQ5OSAxOC4zMDAzQzE1Mi4zMDkgMTguNDQ3MSAxNTIuMjE0IDE4LjYzNTEgMTUyLjIxNCAxOC44NjQzSDE1MC45MDlDMTUwLjkwOSAxOC41NDU2IDE1MS4wMTQgMTguMjQ4NCAxNTEuMjI2IDE3Ljk3MjdDMTUxLjQzNyAxNy42OTM0IDE1MS43MjMgMTcuNDc0OSAxNTIuMDg1IDE3LjMxNzRDMTUyLjQ1IDE3LjE1OTggMTUyLjg1NyAxNy4wODExIDE1My4zMDQgMTcuMDgxMUMxNTMuOTg1IDE3LjA4MTEgMTU0LjUyNyAxNy4yNTI5IDE1NC45MzIgMTcuNTk2N0MxNTUuMzM2IDE3LjkzNjggMTU1LjU0NCAxOC40MTY3IDE1NS41NTUgMTkuMDM2MVYyMS42NTcyQzE1NS41NTUgMjIuMTggMTU1LjYyOCAyMi41OTcyIDE1NS43NzUgMjIuOTA4N1YyM0gxNTQuNDQzWk0xNTMuMDA5IDIyLjA2MDFDMTUzLjI2NyAyMi4wNjAxIDE1My41MDggMjEuOTk3NCAxNTMuNzM0IDIxLjg3MjFDMTUzLjk2MyAyMS43NDY3IDE1NC4xMzUgMjEuNTc4NSAxNTQuMjUgMjEuMzY3MlYyMC4yNzE1SDE1My41NDZDMTUzLjA2MiAyMC4yNzE1IDE1Mi42OTkgMjAuMzU1NiAxNTIuNDU2IDIwLjUyMzlDMTUyLjIxMiAyMC42OTIyIDE1Mi4wOSAyMC45MzAzIDE1Mi4wOSAyMS4yMzgzQzE1Mi4wOSAyMS40ODg5IDE1Mi4xNzMgMjEuNjg5NSAxNTIuMzM3IDIxLjgzOThDMTUyLjUwNiAyMS45ODY3IDE1Mi43MjkgMjIuMDYwMSAxNTMuMDA5IDIyLjA2MDFaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNTguNTU3IDE1Ljc3NTlWMTcuMTg4NUgxNTkuNTgzVjE4LjE1NTNIMTU4LjU1N1YyMS4zOTk0QzE1OC41NTcgMjEuNjIxNCAxNTguNiAyMS43ODI2IDE1OC42ODYgMjEuODgyOEMxNTguNzc2IDIxLjk3OTUgMTU4LjkzMyAyMi4wMjc4IDE1OS4xNTkgMjIuMDI3OEMxNTkuMzA5IDIyLjAyNzggMTU5LjQ2MSAyMi4wMDk5IDE1OS42MTUgMjEuOTc0MVYyMi45ODM5QzE1OS4zMTggMjMuMDY2MiAxNTkuMDMyIDIzLjEwNzQgMTU4Ljc1NiAyMy4xMDc0QzE1Ny43NTMgMjMuMTA3NCAxNTcuMjUyIDIyLjU1NDIgMTU3LjI1MiAyMS40NDc4VjE4LjE1NTNIMTU2LjI5NlYxNy4xODg1SDE1Ny4yNTJWMTUuNzc1OUgxNTguNTU3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTY0LjEwNSAyMi40MzA3QzE2My43MjIgMjIuODgxOCAxNjMuMTc4IDIzLjEwNzQgMTYyLjQ3MyAyMy4xMDc0QzE2MS44NDIgMjMuMTA3NCAxNjEuMzY0IDIyLjkyMyAxNjEuMDM5IDIyLjU1NDJDMTYwLjcxNiAyMi4xODU0IDE2MC41NTUgMjEuNjUxOSAxNjAuNTU1IDIwLjk1MzZWMTcuMTg4NUgxNjEuODZWMjAuOTM3NUMxNjEuODYgMjEuNjc1MSAxNjIuMTY3IDIyLjA0MzkgMTYyLjc3OSAyMi4wNDM5QzE2My40MTMgMjIuMDQzOSAxNjMuODQgMjEuODE2NiAxNjQuMDYyIDIxLjM2MThWMTcuMTg4NUgxNjUuMzY4VjIzSDE2NC4xMzhMMTY0LjEwNSAyMi40MzA3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTY5Ljk1NSAyMS40MjA5QzE2OS45NTUgMjEuMTg4MiAxNjkuODU4IDIxLjAxMDkgMTY5LjY2NSAyMC44ODkyQzE2OS40NzUgMjAuNzY3NCAxNjkuMTU4IDIwLjY2IDE2OC43MTQgMjAuNTY2OUMxNjguMjcgMjAuNDczOCAxNjcuODk5IDIwLjM1NTYgMTY3LjYwMiAyMC4yMTI0QzE2Ni45NSAxOS44OTczIDE2Ni42MjUgMTkuNDQwOCAxNjYuNjI1IDE4Ljg0MjhDMTY2LjYyNSAxOC4zNDE1IDE2Ni44MzYgMTcuOTIyNSAxNjcuMjU4IDE3LjU4NTlDMTY3LjY4MSAxNy4yNDkzIDE2OC4yMTggMTcuMDgxMSAxNjguODcgMTcuMDgxMUMxNjkuNTY0IDE3LjA4MTEgMTcwLjEyNSAxNy4yNTI5IDE3MC41NTEgMTcuNTk2N0MxNzAuOTggMTcuOTQwNCAxNzEuMTk1IDE4LjM4NjIgMTcxLjE5NSAxOC45MzQxSDE2OS44OUMxNjkuODkgMTguNjgzNCAxNjkuNzk3IDE4LjQ3NTcgMTY5LjYxMSAxOC4zMTFDMTY5LjQyNSAxOC4xNDI3IDE2OS4xNzggMTguMDU4NiAxNjguODcgMTguMDU4NkMxNjguNTgzIDE4LjA1ODYgMTY4LjM0OSAxOC4xMjQ4IDE2OC4xNjYgMTguMjU3M0MxNjcuOTg3IDE4LjM4OTggMTY3Ljg5NyAxOC41NjcxIDE2Ny44OTcgMTguNzg5MUMxNjcuODk3IDE4Ljk4OTYgMTY3Ljk4MiAxOS4xNDUzIDE2OC4xNSAxOS4yNTYzQzE2OC4zMTggMTkuMzY3NCAxNjguNjU4IDE5LjQ4MDEgMTY5LjE3IDE5LjU5NDdDMTY5LjY4MiAxOS43MDU3IDE3MC4wODMgMTkuODQgMTcwLjM3NCAxOS45OTc2QzE3MC42NjcgMjAuMTUxNSAxNzAuODg0IDIwLjMzNzcgMTcxLjAyMyAyMC41NTYyQzE3MS4xNjcgMjAuNzc0NiAxNzEuMjM4IDIxLjAzOTYgMTcxLjIzOCAyMS4zNTExQzE3MS4yMzggMjEuODczOSAxNzEuMDIyIDIyLjI5ODIgMTcwLjU4OCAyMi42MjRDMTcwLjE1NSAyMi45NDYzIDE2OS41ODggMjMuMTA3NCAxNjguODg2IDIzLjEwNzRDMTY4LjQxIDIzLjEwNzQgMTY3Ljk4NSAyMy4wMjE1IDE2Ny42MTMgMjIuODQ5NkMxNjcuMjQgMjIuNjc3NyAxNjYuOTUgMjIuNDQxNCAxNjYuNzQzIDIyLjE0MDZDMTY2LjUzNSAyMS44Mzk4IDE2Ni40MzEgMjEuNTE1OCAxNjYuNDMxIDIxLjE2ODVIMTY3LjY5OUMxNjcuNzE3IDIxLjQ3NjQgMTY3LjgzMyAyMS43MTQ1IDE2OC4wNDggMjEuODgyOEMxNjguMjYzIDIyLjA0NzUgMTY4LjU0NyAyMi4xMjk5IDE2OC45MDIgMjIuMTI5OUMxNjkuMjQ2IDIyLjEyOTkgMTY5LjUwNyAyMi4wNjU0IDE2OS42ODYgMjEuOTM2NUMxNjkuODY1IDIxLjgwNCAxNjkuOTU1IDIxLjYzMjIgMTY5Ljk1NSAyMS40MjA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIuNzczNCA2My40NzlDMTIuNzczNCA2My4zMDcxIDEyLjgyMzYgNjMuMTYzOSAxMi45MjM4IDYzLjA0OTNDMTMuMDI3NyA2Mi45MzQ3IDEzLjE4MTYgNjIuODc3NCAxMy4zODU3IDYyLjg3NzRDMTMuNTg5OCA2Mi44Nzc0IDEzLjc0MzggNjIuOTM0NyAxMy44NDc3IDYzLjA0OTNDMTMuOTU1MSA2My4xNjM5IDE0LjAwODggNjMuMzA3MSAxNC4wMDg4IDYzLjQ3OUMxNC4wMDg4IDYzLjY0MzcgMTMuOTU1MSA2My43ODE2IDEzLjg0NzcgNjMuODkyNkMxMy43NDM4IDY0LjAwMzYgMTMuNTg5OCA2NC4wNTkxIDEzLjM4NTcgNjQuMDU5MUMxMy4xODE2IDY0LjA1OTEgMTMuMDI3NyA2NC4wMDM2IDEyLjkyMzggNjMuODkyNkMxMi44MjM2IDYzLjc4MTYgMTIuNzczNCA2My42NDM3IDEyLjc3MzQgNjMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTUuNjczOCA2My40NzlDMTUuNjczOCA2My4zMDcxIDE1LjcyNCA2My4xNjM5IDE1LjgyNDIgNjMuMDQ5M0MxNS45MjgxIDYyLjkzNDcgMTYuMDgyIDYyLjg3NzQgMTYuMjg2MSA2Mi44Nzc0QzE2LjQ5MDIgNjIuODc3NCAxNi42NDQyIDYyLjkzNDcgMTYuNzQ4IDYzLjA0OTNDMTYuODU1NSA2My4xNjM5IDE2LjkwOTIgNjMuMzA3MSAxNi45MDkyIDYzLjQ3OUMxNi45MDkyIDYzLjY0MzcgMTYuODU1NSA2My43ODE2IDE2Ljc0OCA2My44OTI2QzE2LjY0NDIgNjQuMDAzNiAxNi40OTAyIDY0LjA1OTEgMTYuMjg2MSA2NC4wNTkxQzE2LjA4MiA2NC4wNTkxIDE1LjkyODEgNjQuMDAzNiAxNS44MjQyIDYzLjg5MjZDMTUuNzI0IDYzLjc4MTYgMTUuNjczOCA2My42NDM3IDE1LjY3MzggNjMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjAuODgzOCA2My4yOTY0QzIxLjIzODMgNjMuMjk2NCAyMS41NDggNjMuMTg5IDIxLjgxMyA2Mi45NzQxQzIyLjA3OCA2Mi43NTkzIDIyLjIyNDggNjIuNDkwNyAyMi4yNTM0IDYyLjE2ODVIMjMuMTkzNEMyMy4xNzU1IDYyLjUwMTUgMjMuMDYwOSA2Mi44MTg0IDIyLjg0OTYgNjMuMTE5MUMyMi42MzgzIDYzLjQxOTkgMjIuMzU1NSA2My42NTk4IDIyLjAwMSA2My44Mzg5QzIxLjY1MDEgNjQuMDE3OSAyMS4yNzc3IDY0LjEwNzQgMjAuODgzOCA2NC4xMDc0QzIwLjA5MjQgNjQuMTA3NCAxOS40NjIyIDYzLjg0NDIgMTguOTkzMiA2My4zMTc5QzE4LjUyNzcgNjIuNzg3OSAxOC4yOTQ5IDYyLjA2NDYgMTguMjk0OSA2MS4xNDc5VjYwLjk4MTRDMTguMjk0OSA2MC40MTU3IDE4LjM5ODggNTkuOTEyNiAxOC42MDY0IDU5LjQ3MjJDMTguODE0MSA1OS4wMzE3IDE5LjExMTMgNTguNjg5OCAxOS40OTggNTguNDQ2M0MxOS44ODgzIDU4LjIwMjggMjAuMzQ4NSA1OC4wODExIDIwLjg3ODQgNTguMDgxMUMyMS41MzAxIDU4LjA4MTEgMjIuMDcwOCA1OC4yNzYyIDIyLjUwMDUgNTguNjY2NUMyMi45MzM4IDU5LjA1NjggMjMuMTY0NyA1OS41NjM1IDIzLjE5MzQgNjAuMTg2NUgyMi4yNTM0QzIyLjIyNDggNTkuODEwNSAyMi4wODE1IDU5LjUwMjYgMjEuODIzNyA1OS4yNjI3QzIxLjU2OTUgNTkuMDE5MiAyMS4yNTQ0IDU4Ljg5NzUgMjAuODc4NCA1OC44OTc1QzIwLjM3MzUgNTguODk3NSAxOS45ODE0IDU5LjA4MDEgMTkuNzAyMSA1OS40NDUzQzE5LjQyNjQgNTkuODA3IDE5LjI4ODYgNjAuMzMxNSAxOS4yODg2IDYxLjAxOVY2MS4yMDdDMTkuMjg4NiA2MS44NzY2IDE5LjQyNjQgNjIuMzkyMyAxOS43MDIxIDYyLjc1MzlDMTkuOTc3OSA2My4xMTU2IDIwLjM3MTcgNjMuMjk2NCAyMC44ODM4IDYzLjI5NjRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0yNC4wNDc0IDYxLjA0MDVDMjQuMDQ3NCA2MC40NzEyIDI0LjE1ODQgNTkuOTU5MSAyNC4zODA0IDU5LjUwNDRDMjQuNjA2IDU5LjA0OTYgMjQuOTE3NSA1OC42OTg3IDI1LjMxNDkgNTguNDUxN0MyNS43MTYgNTguMjA0NiAyNi4xNzI1IDU4LjA4MTEgMjYuNjg0NiA1OC4wODExQzI3LjQ3NTkgNTguMDgxMSAyOC4xMTUxIDU4LjM1NSAyOC42MDIxIDU4LjkwMjhDMjkuMDkyNiA1OS40NTA3IDI5LjMzNzkgNjAuMTc5NCAyOS4zMzc5IDYxLjA4ODlWNjEuMTU4N0MyOS4zMzc5IDYxLjcyNDQgMjkuMjI4NyA2Mi4yMzI5IDI5LjAxMDMgNjIuNjg0MUMyOC43OTU0IDYzLjEzMTcgMjguNDg1NyA2My40ODA4IDI4LjA4MTEgNjMuNzMxNEMyNy42OCA2My45ODIxIDI3LjIxODEgNjQuMTA3NCAyNi42OTUzIDY0LjEwNzRDMjUuOTA3NiA2NC4xMDc0IDI1LjI2ODQgNjMuODMzNSAyNC43Nzc4IDYzLjI4NTZDMjQuMjkwOSA2Mi43Mzc4IDI0LjA0NzQgNjIuMDEyNyAyNC4wNDc0IDYxLjExMDRWNjEuMDQwNVpNMjUuMDQ2NCA2MS4xNTg3QzI1LjA0NjQgNjEuODAzMiAyNS4xOTUgNjIuMzIwNiAyNS40OTIyIDYyLjcxMDlDMjUuNzkzIDYzLjEwMTIgMjYuMTk0IDYzLjI5NjQgMjYuNjk1MyA2My4yOTY0QzI3LjIwMDIgNjMuMjk2NCAyNy42MDEyIDYzLjA5OTQgMjcuODk4NCA2Mi43MDU2QzI4LjE5NTYgNjIuMzA4MSAyOC4zNDQyIDYxLjc1MzEgMjguMzQ0MiA2MS4wNDA1QzI4LjM0NDIgNjAuNDAzMiAyOC4xOTIxIDU5Ljg4NzUgMjcuODg3NyA1OS40OTM3QzI3LjU4NjkgNTkuMDk2MiAyNy4xODU5IDU4Ljg5NzUgMjYuNjg0NiA1OC44OTc1QzI2LjE5NCA1OC44OTc1IDI1Ljc5ODMgNTkuMDkyNiAyNS40OTc2IDU5LjQ4MjlDMjUuMTk2OCA1OS44NzMyIDI1LjA0NjQgNjAuNDMxOCAyNS4wNDY0IDYxLjE1ODdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNC42NzY4IDYxLjM3MzVIMzUuNzYxN1Y2Mi4xODQ2SDM0LjY3NjhWNjRIMzMuNjc3N1Y2Mi4xODQ2SDMwLjExNjdWNjEuNTk5MUwzMy42MTg3IDU2LjE3OTdIMzQuNjc2OFY2MS4zNzM1Wk0zMS4yNDQ2IDYxLjM3MzVIMzMuNjc3N1Y1Ny41Mzg2TDMzLjU1OTYgNTcuNzUzNEwzMS4yNDQ2IDYxLjM3MzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNy43ODY2IDU4LjE4ODVWNjQuNjcxNEMzNy43ODY2IDY1Ljc4ODYgMzcuMjc5OSA2Ni4zNDcyIDM2LjI2NjYgNjYuMzQ3MkMzNi4wNDgyIDY2LjM0NzIgMzUuODQ1OSA2Ni4zMTQ5IDM1LjY1OTcgNjYuMjUwNVY2NS40NTU2QzM1Ljc3NDMgNjUuNDg0MiAzNS45MjQ2IDY1LjQ5ODUgMzYuMTEwOCA2NS40OTg1QzM2LjMzMjggNjUuNDk4NSAzNi41MDExIDY1LjQzNzcgMzYuNjE1NyA2NS4zMTU5QzM2LjczMzkgNjUuMTk3OCAzNi43OTMgNjQuOTkwMSAzNi43OTMgNjQuNjkyOVY1OC4xODg1SDM3Ljc4NjZaTTM2LjY5MDkgNTYuNjQ3QzM2LjY5MDkgNTYuNDg5NCAzNi43MzkzIDU2LjM1NTEgMzYuODM1OSA1Ni4yNDQxQzM2LjkzNjIgNTYuMTI5NiAzNy4wODEyIDU2LjA3MjMgMzcuMjcxIDU2LjA3MjNDMzcuNDY0NCA1Ni4wNzIzIDM3LjYxMTIgNTYuMTI3OCAzNy43MTE0IDU2LjIzODhDMzcuODExNyA1Ni4zNDk4IDM3Ljg2MTggNTYuNDg1OCAzNy44NjE4IDU2LjY0N0MzNy44NjE4IDU2LjgwODEgMzcuODExNyA1Ni45NDI0IDM3LjcxMTQgNTcuMDQ5OEMzNy42MTEyIDU3LjE1NzIgMzcuNDY0NCA1Ny4yMTA5IDM3LjI3MSA1Ny4yMTA5QzM3LjA3NzYgNTcuMjEwOSAzNi45MzI2IDU3LjE1NzIgMzYuODM1OSA1Ny4wNDk4QzM2LjczOTMgNTYuOTQyNCAzNi42OTA5IDU2LjgwODEgMzYuNjkwOSA1Ni42NDdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik00Mi45ODA1IDYzLjQyNTNDNDIuNTkzOCA2My44OCA0Mi4wMjYyIDY0LjEwNzQgNDEuMjc3OCA2NC4xMDc0QzQwLjY1ODQgNjQuMTA3NCA0MC4xODU3IDYzLjkyODQgMzkuODU5OSA2My41NzAzQzM5LjUzNzYgNjMuMjA4NyAzOS4zNzQ3IDYyLjY3NTEgMzkuMzcxMSA2MS45Njk3VjU4LjE4ODVINDAuMzY0N1Y2MS45NDI5QzQwLjM2NDcgNjIuODIzNyA0MC43MjI4IDYzLjI2NDIgNDEuNDM5IDYzLjI2NDJDNDIuMTk4MSA2My4yNjQyIDQyLjcwMyA2Mi45ODEzIDQyLjk1MzYgNjIuNDE1NVY1OC4xODg1SDQzLjk0NzNWNjRINDMuMDAyTDQyLjk4MDUgNjMuNDI1M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTY1LjMxMSA1Ny4wMjgzSDYyLjc5NzRWNjRINjEuNzcxNVY1Ny4wMjgzSDU5LjI2MzJWNTYuMTc5N0g2NS4zMTFWNTcuMDI4M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTcxLjE0OTQgNjIuNjMwNEw3Mi4yNjY2IDU4LjE4ODVINzMuMjYwM0w3MS41Njg0IDY0SDcwLjc2MjdMNjkuMzUwMSA1OS41OTU3TDY3Ljk3NTEgNjRINjcuMTY5NEw2NS40ODI5IDU4LjE4ODVINjYuNDcxMkw2Ny42MTUyIDYyLjUzOTFMNjguOTY4OCA1OC4xODg1SDY5Ljc2OUw3MS4xNDk0IDYyLjYzMDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03NC4wMTIyIDYxLjA0MDVDNzQuMDEyMiA2MC40NzEyIDc0LjEyMzIgNTkuOTU5MSA3NC4zNDUyIDU5LjUwNDRDNzQuNTcwOCA1OS4wNDk2IDc0Ljg4MjMgNTguNjk4NyA3NS4yNzk4IDU4LjQ1MTdDNzUuNjgwOCA1OC4yMDQ2IDc2LjEzNzQgNTguMDgxMSA3Ni42NDk0IDU4LjA4MTFDNzcuNDQwOCA1OC4wODExIDc4LjA3OTkgNTguMzU1IDc4LjU2NjkgNTguOTAyOEM3OS4wNTc1IDU5LjQ1MDcgNzkuMzAyNyA2MC4xNzk0IDc5LjMwMjcgNjEuMDg4OVY2MS4xNTg3Qzc5LjMwMjcgNjEuNzI0NCA3OS4xOTM1IDYyLjIzMjkgNzguOTc1MSA2Mi42ODQxQzc4Ljc2MDMgNjMuMTMxNyA3OC40NTA1IDYzLjQ4MDggNzguMDQ1OSA2My43MzE0Qzc3LjY0NDkgNjMuOTgyMSA3Ny4xODI5IDY0LjEwNzQgNzYuNjYwMiA2NC4xMDc0Qzc1Ljg3MjQgNjQuMTA3NCA3NS4yMzMyIDYzLjgzMzUgNzQuNzQyNyA2My4yODU2Qzc0LjI1NTcgNjIuNzM3OCA3NC4wMTIyIDYyLjAxMjcgNzQuMDEyMiA2MS4xMTA0VjYxLjA0MDVaTTc1LjAxMTIgNjEuMTU4N0M3NS4wMTEyIDYxLjgwMzIgNzUuMTU5OCA2Mi4zMjA2IDc1LjQ1NyA2Mi43MTA5Qzc1Ljc1NzggNjMuMTAxMiA3Ni4xNTg5IDYzLjI5NjQgNzYuNjYwMiA2My4yOTY0Qzc3LjE2NSA2My4yOTY0IDc3LjU2NjEgNjMuMDk5NCA3Ny44NjMzIDYyLjcwNTZDNzguMTYwNSA2Mi4zMDgxIDc4LjMwOTEgNjEuNzUzMSA3OC4zMDkxIDYxLjA0MDVDNzguMzA5MSA2MC40MDMyIDc4LjE1NjkgNTkuODg3NSA3Ny44NTI1IDU5LjQ5MzdDNzcuNTUxOCA1OS4wOTYyIDc3LjE1MDcgNTguODk3NSA3Ni42NDk0IDU4Ljg5NzVDNzYuMTU4OSA1OC44OTc1IDc1Ljc2MzIgNTkuMDkyNiA3NS40NjI0IDU5LjQ4MjlDNzUuMTYxNiA1OS44NzMyIDc1LjAxMTIgNjAuNDMxOCA3NS4wMTEyIDYxLjE1ODdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04Mi42MTY3IDYxLjA4MzVINzkuOTk1NlY2MC4yNzI1SDgyLjYxNjdWNjEuMDgzNVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTg4LjczNDQgNjIuNjMwNEw4OS44NTE2IDU4LjE4ODVIOTAuODQ1Mkw4OS4xNTMzIDY0SDg4LjM0NzdMODYuOTM1MSA1OS41OTU3TDg1LjU2MDEgNjRIODQuNzU0NEw4My4wNjc5IDU4LjE4ODVIODQuMDU2Mkw4NS4yMDAyIDYyLjUzOTFMODYuNTUzNyA1OC4xODg1SDg3LjM1NEw4OC43MzQ0IDYyLjYzMDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05NS40NDgyIDY0Qzk1LjM5MSA2My44ODU0IDk1LjM0NDQgNjMuNjgxMyA5NS4zMDg2IDYzLjM4NzdDOTQuODQ2NyA2My44Njc1IDk0LjI5NTIgNjQuMTA3NCA5My42NTQzIDY0LjEwNzRDOTMuMDgxNCA2NC4xMDc0IDkyLjYxMDUgNjMuOTQ2MyA5Mi4yNDE3IDYzLjYyNEM5MS44NzY1IDYzLjI5ODIgOTEuNjkzOCA2Mi44ODY0IDkxLjY5MzggNjIuMzg4N0M5MS42OTM4IDYxLjc4MzUgOTEuOTIzIDYxLjMxNDUgOTIuMzgxMyA2MC45ODE0QzkyLjg0MzMgNjAuNjQ0OSA5My40OTE0IDYwLjQ3NjYgOTQuMzI1NyA2MC40NzY2SDk1LjI5MjVWNjAuMDJDOTUuMjkyNSA1OS42NzI3IDk1LjE4ODYgNTkuMzk3IDk0Ljk4MSA1OS4xOTI5Qzk0Ljc3MzMgNTguOTg1MiA5NC40NjcxIDU4Ljg4MTMgOTQuMDYyNSA1OC44ODEzQzkzLjcwOCA1OC44ODEzIDkzLjQxMDggNTguOTcwOSA5My4xNzA5IDU5LjE0OTlDOTIuOTMxIDU5LjMyODkgOTIuODExIDU5LjU0NTYgOTIuODExIDU5Ljc5OThIOTEuODEyQzkxLjgxMiA1OS41MDk4IDkxLjkxNDEgNTkuMjMwNSA5Mi4xMTgyIDU4Ljk2MTlDOTIuMzI1OCA1OC42ODk4IDkyLjYwNTEgNTguNDc0OSA5Mi45NTYxIDU4LjMxNzRDOTMuMzEwNSA1OC4xNTk4IDkzLjY5OTEgNTguMDgxMSA5NC4xMjE2IDU4LjA4MTFDOTQuNzkxMiA1OC4wODExIDk1LjMxNTggNTguMjQ5MyA5NS42OTUzIDU4LjU4NTlDOTYuMDc0OSA1OC45MTg5IDk2LjI3MTggNTkuMzc5MSA5Ni4yODYxIDU5Ljk2NjNWNjIuNjQxMUM5Ni4yODYxIDYzLjE3NDYgOTYuMzU0MiA2My41OTkgOTYuNDkwMiA2My45MTQxVjY0SDk1LjQ0ODJaTTkzLjc5OTMgNjMuMjQyN0M5NC4xMTA4IDYzLjI0MjcgOTQuNDA2MiA2My4xNjIxIDk0LjY4NTUgNjMuMDAxQzk0Ljk2NDggNjIuODM5OCA5NS4xNjcyIDYyLjYzMDQgOTUuMjkyNSA2Mi4zNzI2VjYxLjE4MDJIOTQuNTEzN0M5My4yOTYyIDYxLjE4MDIgOTIuNjg3NSA2MS41MzY1IDkyLjY4NzUgNjIuMjQ5QzkyLjY4NzUgNjIuNTYwNSA5Mi43OTEzIDYyLjgwNCA5Mi45OTkgNjIuOTc5NUM5My4yMDY3IDYzLjE1NDkgOTMuNDczNSA2My4yNDI3IDkzLjc5OTMgNjMuMjQyN1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTk5LjY1OTIgNjIuNTQ0NEwxMDEuMDEzIDU4LjE4ODVIMTAyLjA3Nkw5OS43Mzk3IDY0Ljg5N0M5OS4zNzgxIDY1Ljg2MzggOTguODAzNCA2Ni4zNDcyIDk4LjAxNTYgNjYuMzQ3Mkw5Ny44Mjc2IDY2LjMzMTFMOTcuNDU3IDY2LjI2MTJWNjUuNDU1Nkw5Ny43MjU2IDY1LjQ3NzFDOTguMDYyMiA2NS40NzcxIDk4LjMyMzYgNjUuNDA5IDk4LjUwOTggNjUuMjcyOUM5OC42OTk1IDY1LjEzNjkgOTguODU1MyA2NC44ODggOTguOTc3MSA2NC41MjY0TDk5LjE5NzMgNjMuOTM1NUw5Ny4xMjQgNTguMTg4NUg5OC4yMDlMOTkuNjU5MiA2Mi41NDQ0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNjUuOTIzMyAxMDAuMzQyQzY1LjkyMzMgMTAxLjEwOSA2NS43OTQ0IDEwMS43NzggNjUuNTM2NiAxMDIuMzUxQzY1LjI3ODggMTAyLjkyIDY0LjkxMzYgMTAzLjM1NSA2NC40NDA5IDEwMy42NTZDNjMuOTY4MyAxMDMuOTU3IDYzLjQxNjggMTA0LjEwNyA2Mi43ODY2IDEwNC4xMDdDNjIuMTcwNyAxMDQuMTA3IDYxLjYyNDcgMTAzLjk1NyA2MS4xNDg0IDEwMy42NTZDNjAuNjcyMiAxMDMuMzUyIDYwLjMwMTYgMTAyLjkyIDYwLjAzNjYgMTAyLjM2MkM1OS43NzUyIDEwMS44IDU5LjY0MSAxMDEuMTUgNTkuNjMzOCAxMDAuNDEyVjk5Ljg0ODFDNTkuNjMzOCA5OS4wOTYyIDU5Ljc2NDUgOTguNDMyIDYwLjAyNTkgOTcuODU1NUM2MC4yODczIDk3LjI3OSA2MC42NTYxIDk2LjgzODUgNjEuMTMyMyA5Ni41MzQyQzYxLjYxMjEgOTYuMjI2MiA2Mi4xNiA5Ni4wNzIzIDYyLjc3NTkgOTYuMDcyM0M2My40MDI1IDk2LjA3MjMgNjMuOTUzOSA5Ni4yMjQ0IDY0LjQzMDIgOTYuNTI4OEM2NC45MSA5Ni44Mjk2IDY1LjI3ODggOTcuMjY4MiA2NS41MzY2IDk3Ljg0NDdDNjUuNzk0NCA5OC40MTc2IDY1LjkyMzMgOTkuMDg1NCA2NS45MjMzIDk5Ljg0ODFWMTAwLjM0MlpNNjQuODk3NSA5OS44Mzc0QzY0Ljg5NzUgOTguOTEgNjQuNzExMyA5OC4xOTkyIDY0LjMzODkgOTcuNzA1MUM2My45NjY1IDk3LjIwNzQgNjMuNDQ1NSA5Ni45NTg1IDYyLjc3NTkgOTYuOTU4NUM2Mi4xMjQyIDk2Ljk1ODUgNjEuNjEwNCA5Ny4yMDc0IDYxLjIzNDQgOTcuNzA1MUM2MC44NjIgOTguMTk5MiA2MC42NzA0IDk4Ljg4NjcgNjAuNjU5NyA5OS43Njc2VjEwMC4zNDJDNjAuNjU5NyAxMDEuMjQxIDYwLjg0NzcgMTAxLjk0OCA2MS4yMjM2IDEwMi40NjRDNjEuNjAzMiAxMDIuOTc2IDYyLjEyNDIgMTAzLjIzMiA2Mi43ODY2IDEwMy4yMzJDNjMuNDUyNiAxMDMuMjMyIDYzLjk2ODMgMTAyLjk5IDY0LjMzMzUgMTAyLjUwN0M2NC42OTg3IDEwMi4wMiA2NC44ODY3IDEwMS4zMjMgNjQuODk3NSAxMDAuNDE3Vjk5LjgzNzRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik02OC4yNTQ0IDk4LjE4ODVMNjguMjg2NiA5OC45MTg5QzY4LjczMDYgOTguMzYwNCA2OS4zMTA3IDk4LjA4MTEgNzAuMDI2OSA5OC4wODExQzcxLjI1NSA5OC4wODExIDcxLjg3NDUgOTguNzczOSA3MS44ODUzIDEwMC4xNlYxMDRINzAuODkxNlYxMDAuMTU0QzcwLjg4OCA5OS43MzU0IDcwLjc5MTMgOTkuNDI1NiA3MC42MDE2IDk5LjIyNTFDNzAuNDE1NCA5OS4wMjQ2IDcwLjEyMzUgOTguOTI0MyA2OS43MjYxIDk4LjkyNDNDNjkuNDAzOCA5OC45MjQzIDY5LjEyMDkgOTkuMDEwMyA2OC44Nzc0IDk5LjE4MjFDNjguNjM0IDk5LjM1NCA2OC40NDQyIDk5LjU3OTYgNjguMzA4MSA5OS44NTg5VjEwNEg2Ny4zMTQ1Vjk4LjE4ODVINjguMjU0NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTc1Ljc5NTQgMTA0LjEwN0M3NS4wMDc2IDEwNC4xMDcgNzQuMzY2NyAxMDMuODUgNzMuODcyNiAxMDMuMzM0QzczLjM3ODQgMTAyLjgxNSA3My4xMzEzIDEwMi4xMjIgNzMuMTMxMyAxMDEuMjU1VjEwMS4wNzNDNzMuMTMxMyAxMDAuNDk2IDczLjI0MDYgOTkuOTgyNCA3My40NTkgOTkuNTMxMkM3My42ODEgOTkuMDc2NSA3My45ODg5IDk4LjcyMiA3NC4zODI4IDk4LjQ2NzhDNzQuNzgwMyA5OC4yMSA3NS4yMSA5OC4wODExIDc1LjY3MTkgOTguMDgxMUM3Ni40Mjc0IDk4LjA4MTEgNzcuMDE0NiA5OC4zMjk5IDc3LjQzMzYgOTguODI3NkM3Ny44NTI1IDk5LjMyNTQgNzguMDYyIDEwMC4wMzggNzguMDYyIDEwMC45NjVWMTAxLjM3OUg3NC4xMjVDNzQuMTM5MyAxMDEuOTUyIDc0LjMwNTggMTAyLjQxNiA3NC42MjQ1IDEwMi43N0M3NC45NDY4IDEwMy4xMjEgNzUuMzU1IDEwMy4yOTYgNzUuODQ5MSAxMDMuMjk2Qzc2LjIgMTAzLjI5NiA3Ni40OTcyIDEwMy4yMjUgNzYuNzQwNyAxMDMuMDgyQzc2Ljk4NDIgMTAyLjkzOCA3Ny4xOTczIDEwMi43NDkgNzcuMzc5OSAxMDIuNTEyTDc3Ljk4NjggMTAyLjk4NUM3Ny40OTk4IDEwMy43MzMgNzYuNzY5NCAxMDQuMTA3IDc1Ljc5NTQgMTA0LjEwN1pNNzUuNjcxOSA5OC44OTc1Qzc1LjI3MDggOTguODk3NSA3NC45MzQyIDk5LjA0NDMgNzQuNjYyMSA5OS4zMzc5Qzc0LjM5IDk5LjYyNzkgNzQuMjIxNyAxMDAuMDM2IDc0LjE1NzIgMTAwLjU2Mkg3Ny4wNjg0VjEwMC40ODdDNzcuMDM5NyA5OS45ODI0IDc2LjkwMzYgOTkuNTkyMSA3Ni42NjAyIDk5LjMxNjRDNzYuNDE2NyA5OS4wMzcxIDc2LjA4NzIgOTguODk3NSA3NS42NzE5IDk4Ljg5NzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04MS4yODQ3IDEwMS4wODNINzguNjYzNlYxMDAuMjcySDgxLjI4NDdWMTAxLjA4M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTg3LjQwMjMgMTAyLjYzTDg4LjUxOTUgOTguMTg4NUg4OS41MTMyTDg3LjgyMTMgMTA0SDg3LjAxNTZMODUuNjAzIDk5LjU5NTdMODQuMjI4IDEwNEg4My40MjI0TDgxLjczNTggOTguMTg4NUg4Mi43MjQxTDgzLjg2ODIgMTAyLjUzOUw4NS4yMjE3IDk4LjE4ODVIODYuMDIyTDg3LjQwMjMgMTAyLjYzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNOTQuMTE2MiAxMDRDOTQuMDU4OSAxMDMuODg1IDk0LjAxMjQgMTAzLjY4MSA5My45NzY2IDEwMy4zODhDOTMuNTE0NiAxMDMuODY4IDkyLjk2MzIgMTA0LjEwNyA5Mi4zMjIzIDEwNC4xMDdDOTEuNzQ5MyAxMDQuMTA3IDkxLjI3ODUgMTAzLjk0NiA5MC45MDk3IDEwMy42MjRDOTAuNTQ0NCAxMDMuMjk4IDkwLjM2MTggMTAyLjg4NiA5MC4zNjE4IDEwMi4zODlDOTAuMzYxOCAxMDEuNzg0IDkwLjU5MSAxMDEuMzE0IDkxLjA0OTMgMTAwLjk4MUM5MS41MTEyIDEwMC42NDUgOTIuMTU5MyAxMDAuNDc3IDkyLjk5MzcgMTAwLjQ3N0g5My45NjA0VjEwMC4wMkM5My45NjA0IDk5LjY3MjcgOTMuODU2NiA5OS4zOTcgOTMuNjQ4OSA5OS4xOTI5QzkzLjQ0MTIgOTguOTg1MiA5My4xMzUxIDk4Ljg4MTMgOTIuNzMwNSA5OC44ODEzQzkyLjM3NiA5OC44ODEzIDkyLjA3ODggOTguOTcwOSA5MS44Mzg5IDk5LjE0OTlDOTEuNTk5IDk5LjMyODkgOTEuNDc5IDk5LjU0NTYgOTEuNDc5IDk5Ljc5OThIOTAuNDhDOTAuNDggOTkuNTA5OCA5MC41ODIgOTkuMjMwNSA5MC43ODYxIDk4Ljk2MTlDOTAuOTkzOCA5OC42ODk4IDkxLjI3MzEgOTguNDc0OSA5MS42MjQgOTguMzE3NEM5MS45Nzg1IDk4LjE1OTggOTIuMzY3IDk4LjA4MTEgOTIuNzg5NiA5OC4wODExQzkzLjQ1OTEgOTguMDgxMSA5My45ODM3IDk4LjI0OTMgOTQuMzYzMyA5OC41ODU5Qzk0Ljc0MjggOTguOTE4OSA5NC45Mzk4IDk5LjM3OTEgOTQuOTU0MSA5OS45NjYzVjEwMi42NDFDOTQuOTU0MSAxMDMuMTc1IDk1LjAyMjEgMTAzLjU5OSA5NS4xNTgyIDEwMy45MTRWMTA0SDk0LjExNjJaTTkyLjQ2NzMgMTAzLjI0M0M5Mi43Nzg4IDEwMy4yNDMgOTMuMDc0MiAxMDMuMTYyIDkzLjM1MzUgMTAzLjAwMUM5My42MzI4IDEwMi44NCA5My44MzUxIDEwMi42MyA5My45NjA0IDEwMi4zNzNWMTAxLjE4SDkzLjE4MTZDOTEuOTY0MiAxMDEuMTggOTEuMzU1NSAxMDEuNTM2IDkxLjM1NTUgMTAyLjI0OUM5MS4zNTU1IDEwMi41NjEgOTEuNDU5MyAxMDIuODA0IDkxLjY2NyAxMDIuOTc5QzkxLjg3NDcgMTAzLjE1NSA5Mi4xNDE0IDEwMy4yNDMgOTIuNDY3MyAxMDMuMjQzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNOTguMzI3MSAxMDIuNTQ0TDk5LjY4MDcgOTguMTg4NUgxMDAuNzQ0TDk4LjQwNzcgMTA0Ljg5N0M5OC4wNDYxIDEwNS44NjQgOTcuNDcxNCAxMDYuMzQ3IDk2LjY4MzYgMTA2LjM0N0w5Ni40OTU2IDEwNi4zMzFMOTYuMTI1IDEwNi4yNjFWMTA1LjQ1Nkw5Ni4zOTM2IDEwNS40NzdDOTYuNzMwMSAxMDUuNDc3IDk2Ljk5MTUgMTA1LjQwOSA5Ny4xNzc3IDEwNS4yNzNDOTcuMzY3NSAxMDUuMTM3IDk3LjUyMzMgMTA0Ljg4OCA5Ny42NDUgMTA0LjUyNkw5Ny44NjUyIDEwMy45MzZMOTUuNzkyIDk4LjE4ODVIOTYuODc3TDk4LjMyNzEgMTAyLjU0NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTY1LjkyMzMgMTQwLjM0MkM2NS45MjMzIDE0MS4xMDkgNjUuNzk0NCAxNDEuNzc4IDY1LjUzNjYgMTQyLjM1MUM2NS4yNzg4IDE0Mi45MiA2NC45MTM2IDE0My4zNTUgNjQuNDQwOSAxNDMuNjU2QzYzLjk2ODMgMTQzLjk1NyA2My40MTY4IDE0NC4xMDcgNjIuNzg2NiAxNDQuMTA3QzYyLjE3MDcgMTQ0LjEwNyA2MS42MjQ3IDE0My45NTcgNjEuMTQ4NCAxNDMuNjU2QzYwLjY3MjIgMTQzLjM1MiA2MC4zMDE2IDE0Mi45MiA2MC4wMzY2IDE0Mi4zNjJDNTkuNzc1MiAxNDEuOCA1OS42NDEgMTQxLjE1IDU5LjYzMzggMTQwLjQxMlYxMzkuODQ4QzU5LjYzMzggMTM5LjA5NiA1OS43NjQ1IDEzOC40MzIgNjAuMDI1OSAxMzcuODU1QzYwLjI4NzMgMTM3LjI3OSA2MC42NTYxIDEzNi44MzkgNjEuMTMyMyAxMzYuNTM0QzYxLjYxMjEgMTM2LjIyNiA2Mi4xNiAxMzYuMDcyIDYyLjc3NTkgMTM2LjA3MkM2My40MDI1IDEzNi4wNzIgNjMuOTUzOSAxMzYuMjI0IDY0LjQzMDIgMTM2LjUyOUM2NC45MSAxMzYuODMgNjUuMjc4OCAxMzcuMjY4IDY1LjUzNjYgMTM3Ljg0NUM2NS43OTQ0IDEzOC40MTggNjUuOTIzMyAxMzkuMDg1IDY1LjkyMzMgMTM5Ljg0OFYxNDAuMzQyWk02NC44OTc1IDEzOS44MzdDNjQuODk3NSAxMzguOTEgNjQuNzExMyAxMzguMTk5IDY0LjMzODkgMTM3LjcwNUM2My45NjY1IDEzNy4yMDcgNjMuNDQ1NSAxMzYuOTU4IDYyLjc3NTkgMTM2Ljk1OEM2Mi4xMjQyIDEzNi45NTggNjEuNjEwNCAxMzcuMjA3IDYxLjIzNDQgMTM3LjcwNUM2MC44NjIgMTM4LjE5OSA2MC42NzA0IDEzOC44ODcgNjAuNjU5NyAxMzkuNzY4VjE0MC4zNDJDNjAuNjU5NyAxNDEuMjQxIDYwLjg0NzcgMTQxLjk0OCA2MS4yMjM2IDE0Mi40NjRDNjEuNjAzMiAxNDIuOTc2IDYyLjEyNDIgMTQzLjIzMiA2Mi43ODY2IDE0My4yMzJDNjMuNDUyNiAxNDMuMjMyIDYzLjk2ODMgMTQyLjk5IDY0LjMzMzUgMTQyLjUwN0M2NC42OTg3IDE0Mi4wMiA2NC44ODY3IDE0MS4zMjMgNjQuODk3NSAxNDAuNDE3VjEzOS44MzdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik02OC4yNTQ0IDEzOC4xODhMNjguMjg2NiAxMzguOTE5QzY4LjczMDYgMTM4LjM2IDY5LjMxMDcgMTM4LjA4MSA3MC4wMjY5IDEzOC4wODFDNzEuMjU1IDEzOC4wODEgNzEuODc0NSAxMzguNzc0IDcxLjg4NTMgMTQwLjE2VjE0NEg3MC44OTE2VjE0MC4xNTRDNzAuODg4IDEzOS43MzUgNzAuNzkxMyAxMzkuNDI2IDcwLjYwMTYgMTM5LjIyNUM3MC40MTU0IDEzOS4wMjUgNzAuMTIzNSAxMzguOTI0IDY5LjcyNjEgMTM4LjkyNEM2OS40MDM4IDEzOC45MjQgNjkuMTIwOSAxMzkuMDEgNjguODc3NCAxMzkuMTgyQzY4LjYzNCAxMzkuMzU0IDY4LjQ0NDIgMTM5LjU4IDY4LjMwODEgMTM5Ljg1OVYxNDRINjcuMzE0NVYxMzguMTg4SDY4LjI1NDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03NS43OTU0IDE0NC4xMDdDNzUuMDA3NiAxNDQuMTA3IDc0LjM2NjcgMTQzLjg1IDczLjg3MjYgMTQzLjMzNEM3My4zNzg0IDE0Mi44MTUgNzMuMTMxMyAxNDIuMTIyIDczLjEzMTMgMTQxLjI1NVYxNDEuMDczQzczLjEzMTMgMTQwLjQ5NiA3My4yNDA2IDEzOS45ODIgNzMuNDU5IDEzOS41MzFDNzMuNjgxIDEzOS4wNzYgNzMuOTg4OSAxMzguNzIyIDc0LjM4MjggMTM4LjQ2OEM3NC43ODAzIDEzOC4yMSA3NS4yMSAxMzguMDgxIDc1LjY3MTkgMTM4LjA4MUM3Ni40Mjc0IDEzOC4wODEgNzcuMDE0NiAxMzguMzMgNzcuNDMzNiAxMzguODI4Qzc3Ljg1MjUgMTM5LjMyNSA3OC4wNjIgMTQwLjAzOCA3OC4wNjIgMTQwLjk2NVYxNDEuMzc5SDc0LjEyNUM3NC4xMzkzIDE0MS45NTIgNzQuMzA1OCAxNDIuNDE2IDc0LjYyNDUgMTQyLjc3Qzc0Ljk0NjggMTQzLjEyMSA3NS4zNTUgMTQzLjI5NiA3NS44NDkxIDE0My4yOTZDNzYuMiAxNDMuMjk2IDc2LjQ5NzIgMTQzLjIyNSA3Ni43NDA3IDE0My4wODJDNzYuOTg0MiAxNDIuOTM4IDc3LjE5NzMgMTQyLjc0OSA3Ny4zNzk5IDE0Mi41MTJMNzcuOTg2OCAxNDIuOTg1Qzc3LjQ5OTggMTQzLjczMyA3Ni43Njk0IDE0NC4xMDcgNzUuNzk1NCAxNDQuMTA3Wk03NS42NzE5IDEzOC44OTdDNzUuMjcwOCAxMzguODk3IDc0LjkzNDIgMTM5LjA0NCA3NC42NjIxIDEzOS4zMzhDNzQuMzkgMTM5LjYyOCA3NC4yMjE3IDE0MC4wMzYgNzQuMTU3MiAxNDAuNTYySDc3LjA2ODRWMTQwLjQ4N0M3Ny4wMzk3IDEzOS45ODIgNzYuOTAzNiAxMzkuNTkyIDc2LjY2MDIgMTM5LjMxNkM3Ni40MTY3IDEzOS4wMzcgNzYuMDg3MiAxMzguODk3IDc1LjY3MTkgMTM4Ljg5N1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTgxLjI4NDcgMTQxLjA4M0g3OC42NjM2VjE0MC4yNzJIODEuMjg0N1YxNDEuMDgzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNODcuNDAyMyAxNDIuNjNMODguNTE5NSAxMzguMTg4SDg5LjUxMzJMODcuODIxMyAxNDRIODcuMDE1Nkw4NS42MDMgMTM5LjU5Nkw4NC4yMjggMTQ0SDgzLjQyMjRMODEuNzM1OCAxMzguMTg4SDgyLjcyNDFMODMuODY4MiAxNDIuNTM5TDg1LjIyMTcgMTM4LjE4OEg4Ni4wMjJMODcuNDAyMyAxNDIuNjNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05NC4xMTYyIDE0NEM5NC4wNTg5IDE0My44ODUgOTQuMDEyNCAxNDMuNjgxIDkzLjk3NjYgMTQzLjM4OEM5My41MTQ2IDE0My44NjggOTIuOTYzMiAxNDQuMTA3IDkyLjMyMjMgMTQ0LjEwN0M5MS43NDkzIDE0NC4xMDcgOTEuMjc4NSAxNDMuOTQ2IDkwLjkwOTcgMTQzLjYyNEM5MC41NDQ0IDE0My4yOTggOTAuMzYxOCAxNDIuODg2IDkwLjM2MTggMTQyLjM4OUM5MC4zNjE4IDE0MS43ODQgOTAuNTkxIDE0MS4zMTQgOTEuMDQ5MyAxNDAuOTgxQzkxLjUxMTIgMTQwLjY0NSA5Mi4xNTkzIDE0MC40NzcgOTIuOTkzNyAxNDAuNDc3SDkzLjk2MDRWMTQwLjAyQzkzLjk2MDQgMTM5LjY3MyA5My44NTY2IDEzOS4zOTcgOTMuNjQ4OSAxMzkuMTkzQzkzLjQ0MTIgMTM4Ljk4NSA5My4xMzUxIDEzOC44ODEgOTIuNzMwNSAxMzguODgxQzkyLjM3NiAxMzguODgxIDkyLjA3ODggMTM4Ljk3MSA5MS44Mzg5IDEzOS4xNUM5MS41OTkgMTM5LjMyOSA5MS40NzkgMTM5LjU0NiA5MS40NzkgMTM5LjhIOTAuNDhDOTAuNDggMTM5LjUxIDkwLjU4MiAxMzkuMjMgOTAuNzg2MSAxMzguOTYyQzkwLjk5MzggMTM4LjY5IDkxLjI3MzEgMTM4LjQ3NSA5MS42MjQgMTM4LjMxN0M5MS45Nzg1IDEzOC4xNiA5Mi4zNjcgMTM4LjA4MSA5Mi43ODk2IDEzOC4wODFDOTMuNDU5MSAxMzguMDgxIDkzLjk4MzcgMTM4LjI0OSA5NC4zNjMzIDEzOC41ODZDOTQuNzQyOCAxMzguOTE5IDk0LjkzOTggMTM5LjM3OSA5NC45NTQxIDEzOS45NjZWMTQyLjY0MUM5NC45NTQxIDE0My4xNzUgOTUuMDIyMSAxNDMuNTk5IDk1LjE1ODIgMTQzLjkxNFYxNDRIOTQuMTE2MlpNOTIuNDY3MyAxNDMuMjQzQzkyLjc3ODggMTQzLjI0MyA5My4wNzQyIDE0My4xNjIgOTMuMzUzNSAxNDMuMDAxQzkzLjYzMjggMTQyLjg0IDkzLjgzNTEgMTQyLjYzIDkzLjk2MDQgMTQyLjM3M1YxNDEuMThIOTMuMTgxNkM5MS45NjQyIDE0MS4xOCA5MS4zNTU1IDE0MS41MzYgOTEuMzU1NSAxNDIuMjQ5QzkxLjM1NTUgMTQyLjU2MSA5MS40NTkzIDE0Mi44MDQgOTEuNjY3IDE0Mi45NzlDOTEuODc0NyAxNDMuMTU1IDkyLjE0MTQgMTQzLjI0MyA5Mi40NjczIDE0My4yNDNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05OC4zMjcxIDE0Mi41NDRMOTkuNjgwNyAxMzguMTg4SDEwMC43NDRMOTguNDA3NyAxNDQuODk3Qzk4LjA0NjEgMTQ1Ljg2NCA5Ny40NzE0IDE0Ni4zNDcgOTYuNjgzNiAxNDYuMzQ3TDk2LjQ5NTYgMTQ2LjMzMUw5Ni4xMjUgMTQ2LjI2MVYxNDUuNDU2TDk2LjM5MzYgMTQ1LjQ3N0M5Ni43MzAxIDE0NS40NzcgOTYuOTkxNSAxNDUuNDA5IDk3LjE3NzcgMTQ1LjI3M0M5Ny4zNjc1IDE0NS4xMzcgOTcuNTIzMyAxNDQuODg4IDk3LjY0NSAxNDQuNTI2TDk3Ljg2NTIgMTQzLjkzNkw5NS43OTIgMTM4LjE4OEg5Ni44NzdMOTguMzI3MSAxNDIuNTQ0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIuNzczNCAxMDMuNDc5QzEyLjc3MzQgMTAzLjMwNyAxMi44MjM2IDEwMy4xNjQgMTIuOTIzOCAxMDMuMDQ5QzEzLjAyNzcgMTAyLjkzNSAxMy4xODE2IDEwMi44NzcgMTMuMzg1NyAxMDIuODc3QzEzLjU4OTggMTAyLjg3NyAxMy43NDM4IDEwMi45MzUgMTMuODQ3NyAxMDMuMDQ5QzEzLjk1NTEgMTAzLjE2NCAxNC4wMDg4IDEwMy4zMDcgMTQuMDA4OCAxMDMuNDc5QzE0LjAwODggMTAzLjY0NCAxMy45NTUxIDEwMy43ODIgMTMuODQ3NyAxMDMuODkzQzEzLjc0MzggMTA0LjAwNCAxMy41ODk4IDEwNC4wNTkgMTMuMzg1NyAxMDQuMDU5QzEzLjE4MTYgMTA0LjA1OSAxMy4wMjc3IDEwNC4wMDQgMTIuOTIzOCAxMDMuODkzQzEyLjgyMzYgMTAzLjc4MiAxMi43NzM0IDEwMy42NDQgMTIuNzczNCAxMDMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTUuNjczOCAxMDMuNDc5QzE1LjY3MzggMTAzLjMwNyAxNS43MjQgMTAzLjE2NCAxNS44MjQyIDEwMy4wNDlDMTUuOTI4MSAxMDIuOTM1IDE2LjA4MiAxMDIuODc3IDE2LjI4NjEgMTAyLjg3N0MxNi40OTAyIDEwMi44NzcgMTYuNjQ0MiAxMDIuOTM1IDE2Ljc0OCAxMDMuMDQ5QzE2Ljg1NTUgMTAzLjE2NCAxNi45MDkyIDEwMy4zMDcgMTYuOTA5MiAxMDMuNDc5QzE2LjkwOTIgMTAzLjY0NCAxNi44NTU1IDEwMy43ODIgMTYuNzQ4IDEwMy44OTNDMTYuNjQ0MiAxMDQuMDA0IDE2LjQ5MDIgMTA0LjA1OSAxNi4yODYxIDEwNC4wNTlDMTYuMDgyIDEwNC4wNTkgMTUuOTI4MSAxMDQuMDA0IDE1LjgyNDIgMTAzLjg5M0MxNS43MjQgMTAzLjc4MiAxNS42NzM4IDEwMy42NDQgMTUuNjczOCAxMDMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTguMjg5NiAxMDEuMDQxQzE4LjI4OTYgMTAwLjQ3MSAxOC40MDA2IDk5Ljk1OTEgMTguNjIyNiA5OS41MDQ0QzE4Ljg0ODEgOTkuMDQ5NiAxOS4xNTk3IDk4LjY5ODcgMTkuNTU3MSA5OC40NTE3QzE5Ljk1ODIgOTguMjA0NiAyMC40MTQ3IDk4LjA4MTEgMjAuOTI2OCA5OC4wODExQzIxLjcxODEgOTguMDgxMSAyMi4zNTczIDk4LjM1NSAyMi44NDQyIDk4LjkwMjhDMjMuMzM0OCA5OS40NTA3IDIzLjU4MDEgMTAwLjE3OSAyMy41ODAxIDEwMS4wODlWMTAxLjE1OUMyMy41ODAxIDEwMS43MjQgMjMuNDcwOSAxMDIuMjMzIDIzLjI1MjQgMTAyLjY4NEMyMy4wMzc2IDEwMy4xMzIgMjIuNzI3OSAxMDMuNDgxIDIyLjMyMzIgMTAzLjczMUMyMS45MjIyIDEwMy45ODIgMjEuNDYwMyAxMDQuMTA3IDIwLjkzNzUgMTA0LjEwN0MyMC4xNDk3IDEwNC4xMDcgMTkuNTEwNiAxMDMuODMzIDE5LjAyIDEwMy4yODZDMTguNTMzIDEwMi43MzggMTguMjg5NiAxMDIuMDEzIDE4LjI4OTYgMTAxLjExVjEwMS4wNDFaTTE5LjI4ODYgMTAxLjE1OUMxOS4yODg2IDEwMS44MDMgMTkuNDM3MiAxMDIuMzIxIDE5LjczNDQgMTAyLjcxMUMyMC4wMzUyIDEwMy4xMDEgMjAuNDM2MiAxMDMuMjk2IDIwLjkzNzUgMTAzLjI5NkMyMS40NDI0IDEwMy4yOTYgMjEuODQzNCAxMDMuMDk5IDIyLjE0MDYgMTAyLjcwNkMyMi40Mzc4IDEwMi4zMDggMjIuNTg2NCAxMDEuNzUzIDIyLjU4NjQgMTAxLjA0MUMyMi41ODY0IDEwMC40MDMgMjIuNDM0MiA5OS44ODc1IDIyLjEyOTkgOTkuNDkzN0MyMS44MjkxIDk5LjA5NjIgMjEuNDI4MSA5OC44OTc1IDIwLjkyNjggOTguODk3NUMyMC40MzYyIDk4Ljg5NzUgMjAuMDQwNSA5OS4wOTI2IDE5LjczOTcgOTkuNDgyOUMxOS40MzkgOTkuODczMiAxOS4yODg2IDEwMC40MzIgMTkuMjg4NiAxMDEuMTU5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjYuMTY4OSA5OS42MDY0SDI2LjkxNTVDMjcuMzg0NiA5OS41OTkzIDI3Ljc1MzQgOTkuNDc1NyAyOC4wMjIgOTkuMjM1OEMyOC4yOTA1IDk4Ljk5NTkgMjguNDI0OCA5OC42NzE5IDI4LjQyNDggOTguMjYzN0MyOC40MjQ4IDk3LjM0NyAyNy45NjgzIDk2Ljg4ODcgMjcuMDU1MiA5Ni44ODg3QzI2LjYyNTUgOTYuODg4NyAyNi4yODE3IDk3LjAxMjIgMjYuMDIzOSA5Ny4yNTkzQzI1Ljc2OTcgOTcuNTAyOCAyNS42NDI2IDk3LjgyNjggMjUuNjQyNiA5OC4yMzE0SDI0LjY0ODlDMjQuNjQ4OSA5Ny42MTIgMjQuODc0NSA5Ny4wOTgxIDI1LjMyNTcgOTYuNjg5OUMyNS43ODA0IDk2LjI3ODIgMjYuMzU2OSA5Ni4wNzIzIDI3LjA1NTIgOTYuMDcyM0MyNy43OTI4IDk2LjA3MjMgMjguMzcxMSA5Ni4yNjc0IDI4Ljc5IDk2LjY1NzdDMjkuMjA5IDk3LjA0OCAyOS40MTg1IDk3LjU5MDUgMjkuNDE4NSA5OC4yODUyQzI5LjQxODUgOTguNjI1MyAyOS4zMDc1IDk4Ljk1NDggMjkuMDg1NCA5OS4yNzM0QzI4Ljg2NyA5OS41OTIxIDI4LjU2OCA5OS44MzAyIDI4LjE4ODUgOTkuOTg3OEMyOC42MTgyIDEwMC4xMjQgMjguOTQ5NCAxMDAuMzQ5IDI5LjE4MjEgMTAwLjY2NUMyOS40MTg1IDEwMC45OCAyOS41MzY2IDEwMS4zNjUgMjkuNTM2NiAxMDEuODE5QzI5LjUzNjYgMTAyLjUyMSAyOS4zMDc1IDEwMy4wNzggMjguODQ5MSAxMDMuNDlDMjguMzkwOCAxMDMuOTAyIDI3Ljc5NDYgMTA0LjEwNyAyNy4wNjA1IDEwNC4xMDdDMjYuMzI2NSAxMDQuMTA3IDI1LjcyODUgMTAzLjkwOSAyNS4yNjY2IDEwMy41MTFDMjQuODA4MyAxMDMuMTE0IDI0LjU3OTEgMTAyLjU4OSAyNC41NzkxIDEwMS45MzhIMjUuNTc4MUMyNS41NzgxIDEwMi4zNDkgMjUuNzEyNCAxMDIuNjc5IDI1Ljk4MSAxMDIuOTI2QzI2LjI0OTUgMTAzLjE3MyAyNi42MDk0IDEwMy4yOTYgMjcuMDYwNSAxMDMuMjk2QzI3LjU0MDQgMTAzLjI5NiAyNy45MDc0IDEwMy4xNzEgMjguMTYxNiAxMDIuOTJDMjguNDE1OSAxMDIuNjcgMjguNTQzIDEwMi4zMSAyOC41NDMgMTAxLjg0MUMyOC41NDMgMTAxLjM4NiAyOC40MDMzIDEwMS4wMzcgMjguMTI0IDEwMC43OTNDMjcuODQ0NyAxMDAuNTUgMjcuNDQxOSAxMDAuNDI1IDI2LjkxNTUgMTAwLjQxN0gyNi4xNjg5Vjk5LjYwNjRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zMC43NjEyIDEwMS4wNDZDMzAuNzYxMiAxMDAuMTU0IDMwLjk3MjUgOTkuNDM4MiAzMS4zOTUgOTguODk3NUMzMS44MTc1IDk4LjM1MzIgMzIuMzcwOCA5OC4wODExIDMzLjA1NDcgOTguMDgxMUMzMy43MzUgOTguMDgxMSAzNC4yNzM5IDk4LjMxMzggMzQuNjcxNCA5OC43NzkzVjk1Ljc1SDM1LjY2NVYxMDRIMzQuNzUyTDM0LjcwMzYgMTAzLjM3N0MzNC4zMDYyIDEwMy44NjQgMzMuNzUyOSAxMDQuMTA3IDMzLjA0MzkgMTA0LjEwN0MzMi4zNzA4IDEwNC4xMDcgMzEuODIxMSAxMDMuODMyIDMxLjM5NSAxMDMuMjhDMzAuOTcyNSAxMDIuNzI5IDMwLjc2MTIgMTAyLjAwOSAzMC43NjEyIDEwMS4xMjFWMTAxLjA0NlpNMzEuNzU0OSAxMDEuMTU5QzMxLjc1NDkgMTAxLjgxOCAzMS44OTEgMTAyLjMzMyAzMi4xNjMxIDEwMi43MDZDMzIuNDM1MiAxMDMuMDc4IDMyLjgxMTIgMTAzLjI2NCAzMy4yOTEgMTAzLjI2NEMzMy45MjEyIDEwMy4yNjQgMzQuMzgxMyAxMDIuOTgxIDM0LjY3MTQgMTAyLjQxNlY5OS43NDYxQzM0LjM3NDIgOTkuMTk4MiAzMy45MTc2IDk4LjkyNDMgMzMuMzAxOCA5OC45MjQzQzMyLjgxNDggOTguOTI0MyAzMi40MzUyIDk5LjExMjMgMzIuMTYzMSA5OS40ODgzQzMxLjg5MSA5OS44NjQzIDMxLjc1NDkgMTAwLjQyMSAzMS43NTQ5IDEwMS4xNTlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik00MC4wMjEgOTkuMDgwMUMzOS44NzA2IDk5LjA1NSAzOS43MDc3IDk5LjA0MjUgMzkuNTMyMiA5OS4wNDI1QzM4Ljg4MDUgOTkuMDQyNSAzOC40MzgzIDk5LjMyIDM4LjIwNTYgOTkuODc1VjEwNEgzNy4yMTE5Vjk4LjE4ODVIMzguMTc4N0wzOC4xOTQ4IDk4Ljg1OTlDMzguNTIwNyA5OC4zNDA3IDM4Ljk4MjYgOTguMDgxMSAzOS41ODA2IDk4LjA4MTFDMzkuNzczOSA5OC4wODExIDM5LjkyMDcgOTguMTA2MSA0MC4wMjEgOTguMTU2MlY5OS4wODAxWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDEuODc5NCA5OC4xODg1TDQxLjkxMTYgOTguOTE4OUM0Mi4zNTU2IDk4LjM2MDQgNDIuOTM1NyA5OC4wODExIDQzLjY1MTkgOTguMDgxMUM0NC44OCA5OC4wODExIDQ1LjQ5OTUgOTguNzczOSA0NS41MTAzIDEwMC4xNlYxMDRINDQuNTE2NlYxMDAuMTU0QzQ0LjUxMyA5OS43MzU0IDQ0LjQxNjMgOTkuNDI1NiA0NC4yMjY2IDk5LjIyNTFDNDQuMDQwNCA5OS4wMjQ2IDQzLjc0ODUgOTguOTI0MyA0My4zNTExIDk4LjkyNDNDNDMuMDI4OCA5OC45MjQzIDQyLjc0NTkgOTkuMDEwMyA0Mi41MDI0IDk5LjE4MjFDNDIuMjU5IDk5LjM1NCA0Mi4wNjkyIDk5LjU3OTYgNDEuOTMzMSA5OS44NTg5VjEwNEg0MC45Mzk1Vjk4LjE4ODVINDEuODc5NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTEyLjc3MzQgMTQzLjQ3OUMxMi43NzM0IDE0My4zMDcgMTIuODIzNiAxNDMuMTY0IDEyLjkyMzggMTQzLjA0OUMxMy4wMjc3IDE0Mi45MzUgMTMuMTgxNiAxNDIuODc3IDEzLjM4NTcgMTQyLjg3N0MxMy41ODk4IDE0Mi44NzcgMTMuNzQzOCAxNDIuOTM1IDEzLjg0NzcgMTQzLjA0OUMxMy45NTUxIDE0My4xNjQgMTQuMDA4OCAxNDMuMzA3IDE0LjAwODggMTQzLjQ3OUMxNC4wMDg4IDE0My42NDQgMTMuOTU1MSAxNDMuNzgyIDEzLjg0NzcgMTQzLjg5M0MxMy43NDM4IDE0NC4wMDQgMTMuNTg5OCAxNDQuMDU5IDEzLjM4NTcgMTQ0LjA1OUMxMy4xODE2IDE0NC4wNTkgMTMuMDI3NyAxNDQuMDA0IDEyLjkyMzggMTQzLjg5M0MxMi44MjM2IDE0My43ODIgMTIuNzczNCAxNDMuNjQ0IDEyLjc3MzQgMTQzLjQ3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTE1LjY3MzggMTQzLjQ3OUMxNS42NzM4IDE0My4zMDcgMTUuNzI0IDE0My4xNjQgMTUuODI0MiAxNDMuMDQ5QzE1LjkyODEgMTQyLjkzNSAxNi4wODIgMTQyLjg3NyAxNi4yODYxIDE0Mi44NzdDMTYuNDkwMiAxNDIuODc3IDE2LjY0NDIgMTQyLjkzNSAxNi43NDggMTQzLjA0OUMxNi44NTU1IDE0My4xNjQgMTYuOTA5MiAxNDMuMzA3IDE2LjkwOTIgMTQzLjQ3OUMxNi45MDkyIDE0My42NDQgMTYuODU1NSAxNDMuNzgyIDE2Ljc0OCAxNDMuODkzQzE2LjY0NDIgMTQ0LjAwNCAxNi40OTAyIDE0NC4wNTkgMTYuMjg2MSAxNDQuMDU5QzE2LjA4MiAxNDQuMDU5IDE1LjkyODEgMTQ0LjAwNCAxNS44MjQyIDE0My44OTNDMTUuNzI0IDE0My43ODIgMTUuNjczOCAxNDMuNjQ0IDE1LjY3MzggMTQzLjQ3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTIxLjM2MTggMTM5LjA4QzIxLjIxMTQgMTM5LjA1NSAyMS4wNDg1IDEzOS4wNDIgMjAuODczIDEzOS4wNDJDMjAuMjIxNCAxMzkuMDQyIDE5Ljc3OTEgMTM5LjMyIDE5LjU0NjQgMTM5Ljg3NVYxNDRIMTguNTUyN1YxMzguMTg4SDE5LjUxOTVMMTkuNTM1NiAxMzguODZDMTkuODYxNSAxMzguMzQxIDIwLjMyMzQgMTM4LjA4MSAyMC45MjE0IDEzOC4wODFDMjEuMTE0NyAxMzguMDgxIDIxLjI2MTYgMTM4LjEwNiAyMS4zNjE4IDEzOC4xNTZWMTM5LjA4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjQuMjk0NCAxNDIuNjUyTDI1LjczMzkgMTM4LjE4OEgyNi43NDlMMjQuNjY1IDE0NEgyMy45MDc3TDIxLjgwMjIgMTM4LjE4OEgyMi44MTc0TDI0LjI5NDQgMTQyLjY1MloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTMxLjQxMTEgMTQwLjU2MkMzMS4yMDM1IDE0MC44MSAzMC45NTQ2IDE0MS4wMDggMzAuNjY0NiAxNDEuMTU5QzMwLjM3ODEgMTQxLjMwOSAzMC4wNjMgMTQxLjM4NCAyOS43MTkyIDE0MS4zODRDMjkuMjY4MSAxNDEuMzg0IDI4Ljg3NDIgMTQxLjI3MyAyOC41Mzc2IDE0MS4wNTFDMjguMjA0NiAxNDAuODI5IDI3Ljk0NjggMTQwLjUxOCAyNy43NjQyIDE0MC4xMTdDMjcuNTgxNSAxMzkuNzEyIDI3LjQ5MDIgMTM5LjI2NiAyNy40OTAyIDEzOC43NzlDMjcuNDkwMiAxMzguMjU3IDI3LjU4ODcgMTM3Ljc4NiAyNy43ODU2IDEzNy4zNjdDMjcuOTg2MiAxMzYuOTQ4IDI4LjI2OSAxMzYuNjI3IDI4LjYzNDMgMTM2LjQwNUMyOC45OTk1IDEzNi4xODMgMjkuNDI1NiAxMzYuMDcyIDI5LjkxMjYgMTM2LjA3MkMzMC42ODYgMTM2LjA3MiAzMS4yOTQ4IDEzNi4zNjIgMzEuNzM4OCAxMzYuOTQyQzMyLjE4NjQgMTM3LjUxOSAzMi40MTAyIDEzOC4zMDcgMzIuNDEwMiAxMzkuMzA2VjEzOS41OTZDMzIuNDEwMiAxNDEuMTE4IDMyLjEwOTQgMTQyLjIyOSAzMS41MDc4IDE0Mi45MzFDMzAuOTA2MiAxNDMuNjI5IDI5Ljk5ODUgMTQzLjk4NyAyOC43ODQ3IDE0NC4wMDVIMjguNTkxM1YxNDMuMTY3SDI4LjgwMDhDMjkuNjIwOCAxNDMuMTUzIDMwLjI1MSAxNDIuOTQgMzAuNjkxNCAxNDIuNTI4QzMxLjEzMTggMTQyLjExMyAzMS4zNzE3IDE0MS40NTggMzEuNDExMSAxNDAuNTYyWk0yOS44ODA0IDE0MC41NjJDMzAuMjEzNCAxNDAuNTYyIDMwLjUxOTUgMTQwLjQ2IDMwLjc5ODggMTQwLjI1NkMzMS4wODE3IDE0MC4wNTIgMzEuMjg3NiAxMzkuOCAzMS40MTY1IDEzOS40OTlWMTM5LjEwMkMzMS40MTY1IDEzOC40NSAzMS4yNzUxIDEzNy45MiAzMC45OTIyIDEzNy41MTJDMzAuNzA5MyAxMzcuMTA0IDMwLjM1MTIgMTM2Ljg5OSAyOS45MTggMTM2Ljg5OUMyOS40ODExIDEzNi44OTkgMjkuMTMwMiAxMzcuMDY4IDI4Ljg2NTIgMTM3LjQwNEMyOC42MDAzIDEzNy43MzcgMjguNDY3OCAxMzguMTc4IDI4LjQ2NzggMTM4LjcyNkMyOC40Njc4IDEzOS4yNTkgMjguNTk0OSAxMzkuNyAyOC44NDkxIDE0MC4wNDdDMjkuMTA2OSAxNDAuMzkxIDI5LjQ1MDcgMTQwLjU2MiAyOS44ODA0IDE0MC41NjJaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNS41MDM5IDE0MS4zMDlMMzQuODgwOSAxNDEuOTU5VjE0NEgzMy44ODcyVjEzNS43NUgzNC44ODA5VjE0MC43NEwzNS40MTI2IDE0MC4xMDFMMzcuMjIyNyAxMzguMTg4SDM4LjQzMTJMMzYuMTY5OSAxNDAuNjE2TDM4LjY5NDMgMTQ0SDM3LjUyODhMMzUuNTAzOSAxNDEuMzA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDEuNzYxMiAxNDQuMTA3QzQwLjk3MzUgMTQ0LjEwNyA0MC4zMzI1IDE0My44NSAzOS44Mzg0IDE0My4zMzRDMzkuMzQ0MiAxNDIuODE1IDM5LjA5NzIgMTQyLjEyMiAzOS4wOTcyIDE0MS4yNTVWMTQxLjA3M0MzOS4wOTcyIDE0MC40OTYgMzkuMjA2NCAxMzkuOTgyIDM5LjQyNDggMTM5LjUzMUMzOS42NDY4IDEzOS4wNzYgMzkuOTU0OCAxMzguNzIyIDQwLjM0ODYgMTM4LjQ2OEM0MC43NDYxIDEzOC4yMSA0MS4xNzU4IDEzOC4wODEgNDEuNjM3NyAxMzguMDgxQzQyLjM5MzIgMTM4LjA4MSA0Mi45ODA1IDEzOC4zMyA0My4zOTk0IDEzOC44MjhDNDMuODE4NCAxMzkuMzI1IDQ0LjAyNzggMTQwLjAzOCA0NC4wMjc4IDE0MC45NjVWMTQxLjM3OUg0MC4wOTA4QzQwLjEwNTEgMTQxLjk1MiA0MC4yNzE2IDE0Mi40MTYgNDAuNTkwMyAxNDIuNzdDNDAuOTEyNiAxNDMuMTIxIDQxLjMyMDggMTQzLjI5NiA0MS44MTQ5IDE0My4yOTZDNDIuMTY1OSAxNDMuMjk2IDQyLjQ2MzEgMTQzLjIyNSA0Mi43MDY1IDE0My4wODJDNDIuOTUgMTQyLjkzOCA0My4xNjMxIDE0Mi43NDkgNDMuMzQ1NyAxNDIuNTEyTDQzLjk1MjYgMTQyLjk4NUM0My40NjU3IDE0My43MzMgNDIuNzM1MiAxNDQuMTA3IDQxLjc2MTIgMTQ0LjEwN1pNNDEuNjM3NyAxMzguODk3QzQxLjIzNjcgMTM4Ljg5NyA0MC45MDAxIDEzOS4wNDQgNDAuNjI3OSAxMzkuMzM4QzQwLjM1NTggMTM5LjYyOCA0MC4xODc1IDE0MC4wMzYgNDAuMTIzIDE0MC41NjJINDMuMDM0MlYxNDAuNDg3QzQzLjAwNTUgMTM5Ljk4MiA0Mi44Njk1IDEzOS41OTIgNDIuNjI2IDEzOS4zMTZDNDIuMzgyNSAxMzkuMDM3IDQyLjA1MzEgMTM4Ljg5NyA0MS42Mzc3IDEzOC44OTdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNDYuNDQ1IDU3LjI3NTRIMTQ0LjAwN1Y2NEgxNDIuNjU5VjU3LjI3NTRIMTQwLjI0MlY1Ni4xNzk3SDE0Ni40NDVWNTcuMjc1NFoiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE0OC43MzkgNjRIMTQ3LjQzNFY1OC4xODg1SDE0OC43MzlWNjRaTTE0Ny4zNTMgNTYuNjc5MkMxNDcuMzUzIDU2LjQ3ODcgMTQ3LjQxNiA1Ni4zMTIyIDE0Ny41NDEgNTYuMTc5N0MxNDcuNjcgNTYuMDQ3MiAxNDcuODUzIDU1Ljk4MSAxNDguMDg5IDU1Ljk4MUMxNDguMzI1IDU1Ljk4MSAxNDguNTA4IDU2LjA0NzIgMTQ4LjYzNyA1Ni4xNzk3QzE0OC43NjYgNTYuMzEyMiAxNDguODMgNTYuNDc4NyAxNDguODMgNTYuNjc5MkMxNDguODMgNTYuODc2MSAxNDguNzY2IDU3LjA0MDkgMTQ4LjYzNyA1Ny4xNzMzQzE0OC41MDggNTcuMzAyMiAxNDguMzI1IDU3LjM2NjcgMTQ4LjA4OSA1Ny4zNjY3QzE0Ny44NTMgNTcuMzY2NyAxNDcuNjcgNTcuMzAyMiAxNDcuNTQxIDU3LjE3MzNDMTQ3LjQxNiA1Ny4wNDA5IDE0Ny4zNTMgNTYuODc2MSAxNDcuMzUzIDU2LjY3OTJaIiBmaWxsPSIjRkZBNTAwIi8+CjxwYXRoIGQ9Ik0xNTEuMzkyIDU4LjE4ODVMMTUxLjQzIDU4Ljc5NTRDMTUxLjgzOCA1OC4zMTkyIDE1Mi4zOTYgNTguMDgxMSAxNTMuMTA1IDU4LjA4MTFDMTUzLjg4MiA1OC4wODExIDE1NC40MTQgNTguMzc4MyAxNTQuNzAxIDU4Ljk3MjdDMTU1LjEyMyA1OC4zNzgzIDE1NS43MTggNTguMDgxMSAxNTYuNDg0IDU4LjA4MTFDMTU3LjEyNSA1OC4wODExIDE1Ny42MDEgNTguMjU4MyAxNTcuOTEzIDU4LjYxMjhDMTU4LjIyOCA1OC45NjczIDE1OC4zODkgNTkuNDkwMSAxNTguMzk2IDYwLjE4MTJWNjRIMTU3LjA5MVY2MC4yMTg4QzE1Ny4wOTEgNTkuODQ5OSAxNTcuMDEgNTkuNTc5NiAxNTYuODQ5IDU5LjQwNzdDMTU2LjY4OCA1OS4yMzU4IDE1Ni40MjEgNTkuMTQ5OSAxNTYuMDQ5IDU5LjE0OTlDMTU1Ljc1MiA1OS4xNDk5IDE1NS41MDggNTkuMjMwNSAxNTUuMzE4IDU5LjM5MTZDMTU1LjEzMiA1OS41NDkyIDE1NS4wMDEgNTkuNzU2OCAxNTQuOTI2IDYwLjAxNDZMMTU0LjkzMiA2NEgxNTMuNjI2VjYwLjE3NThDMTUzLjYwOSA1OS40OTE5IDE1My4yNTkgNTkuMTQ5OSAxNTIuNTc5IDU5LjE0OTlDMTUyLjA1NiA1OS4xNDk5IDE1MS42ODYgNTkuMzYzIDE1MS40NjcgNTkuNzg5MVY2NEgxNTAuMTYyVjU4LjE4ODVIMTUxLjM5MloiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE2Mi4yOTUgNjQuMTA3NEMxNjEuNDY4IDY0LjEwNzQgMTYwLjc5NyA2My44NDc4IDE2MC4yODEgNjMuMzI4NkMxNTkuNzY5IDYyLjgwNTggMTU5LjUxMyA2Mi4xMTEyIDE1OS41MTMgNjEuMjQ0NlY2MS4wODM1QzE1OS41MTMgNjAuNTAzNCAxNTkuNjI0IDU5Ljk4NiAxNTkuODQ2IDU5LjUzMTJDMTYwLjA3MiA1OS4wNzI5IDE2MC4zODcgNTguNzE2NiAxNjAuNzkyIDU4LjQ2MjRDMTYxLjE5NiA1OC4yMDgyIDE2MS42NDcgNTguMDgxMSAxNjIuMTQ1IDU4LjA4MTFDMTYyLjkzNiA1OC4wODExIDE2My41NDcgNTguMzMzNSAxNjMuOTc3IDU4LjgzODRDMTY0LjQxIDU5LjM0MzMgMTY0LjYyNiA2MC4wNTc2IDE2NC42MjYgNjAuOTgxNFY2MS41MDc4SDE2MC44MjlDMTYwLjg2OCA2MS45ODc2IDE2MS4wMjggNjIuMzY3MiAxNjEuMzA3IDYyLjY0NjVDMTYxLjU5IDYyLjkyNTggMTYxLjk0NCA2My4wNjU0IDE2Mi4zNzEgNjMuMDY1NEMxNjIuOTY5IDYzLjA2NTQgMTYzLjQ1NiA2Mi44MjM3IDE2My44MzIgNjIuMzQwM0wxNjQuNTM1IDYzLjAxMTdDMTY0LjMwMiA2My4zNTkgMTYzLjk5MSA2My42Mjk0IDE2My42MDEgNjMuODIyOEMxNjMuMjE0IDY0LjAxMjUgMTYyLjc3OSA2NC4xMDc0IDE2Mi4yOTUgNjQuMTA3NFpNMTYyLjE0IDU5LjEyODRDMTYxLjc4MiA1OS4xMjg0IDE2MS40OTIgNTkuMjUzNyAxNjEuMjcgNTkuNTA0NEMxNjEuMDUxIDU5Ljc1NSAxNjAuOTExIDYwLjEwNDIgMTYwLjg1MSA2MC41NTE4SDE2My4zMzdWNjAuNDU1MUMxNjMuMzA5IDYwLjAxODIgMTYzLjE5MiA1OS42ODg4IDE2Mi45ODggNTkuNDY2OEMxNjIuNzg0IDU5LjI0MTIgMTYyLjUwMSA1OS4xMjg0IDE2Mi4xNCA1OS4xMjg0WiIgZmlsbD0iI0ZGQTUwMCIvPgo8cGF0aCBkPSJNMTY1LjQgNjEuMDQwNUMxNjUuNCA2MC40NzEyIDE2NS41MTMgNTkuOTU5MSAxNjUuNzM4IDU5LjUwNDRDMTY1Ljk2NCA1OS4wNDYxIDE2Ni4yODEgNTguNjk1MSAxNjYuNjg5IDU4LjQ1MTdDMTY3LjA5NyA1OC4yMDQ2IDE2Ny41NjYgNTguMDgxMSAxNjguMDk2IDU4LjA4MTFDMTY4Ljg4IDU4LjA4MTEgMTY5LjUxNiA1OC4zMzM1IDE3MC4wMDMgNTguODM4NEMxNzAuNDkzIDU5LjM0MzMgMTcwLjc1OCA2MC4wMTI5IDE3MC43OTggNjAuODQ3MkwxNzAuODAzIDYxLjE1MzNDMTcwLjgwMyA2MS43MjYyIDE3MC42OTIgNjIuMjM4MyAxNzAuNDcgNjIuNjg5NUMxNzAuMjUyIDYzLjE0MDYgMTY5LjkzNyA2My40ODk3IDE2OS41MjUgNjMuNzM2OEMxNjkuMTE3IDYzLjk4MzkgMTY4LjY0NCA2NC4xMDc0IDE2OC4xMDcgNjQuMTA3NEMxNjcuMjg3IDY0LjEwNzQgMTY2LjYzIDYzLjgzNTMgMTY2LjEzNiA2My4yOTFDMTY1LjY0NSA2Mi43NDMyIDE2NS40IDYyLjAxNDUgMTY1LjQgNjEuMTA1VjYxLjA0MDVaTTE2Ni43MDUgNjEuMTUzM0MxNjYuNzA1IDYxLjc1MTMgMTY2LjgyOSA2Mi4yMjA0IDE2Ny4wNzYgNjIuNTYwNUMxNjcuMzIzIDYyLjg5NzEgMTY3LjY2NyA2My4wNjU0IDE2OC4xMDcgNjMuMDY1NEMxNjguNTQ3IDYzLjA2NTQgMTY4Ljg4OSA2Mi44OTM2IDE2OS4xMzMgNjIuNTQ5OEMxNjkuMzggNjIuMjA2MSAxNjkuNTAzIDYxLjcwMyAxNjkuNTAzIDYxLjA0MDVDMTY5LjUwMyA2MC40NTMzIDE2OS4zNzYgNTkuOTg3OCAxNjkuMTIyIDU5LjY0NEMxNjguODcxIDU5LjMwMDMgMTY4LjUyOSA1OS4xMjg0IDE2OC4wOTYgNTkuMTI4NEMxNjcuNjcgNTkuMTI4NCAxNjcuMzMyIDU5LjI5ODUgMTY3LjA4MSA1OS42Mzg3QzE2Ni44MyA1OS45NzUzIDE2Ni43MDUgNjAuNDgwMSAxNjYuNzA1IDYxLjE1MzNaIiBmaWxsPSIjRkZBNTAwIi8+CjxwYXRoIGQ9Ik0xNzUuNDI4IDYzLjQzMDdDMTc1LjA0NSA2My44ODE4IDE3NC41IDY0LjEwNzQgMTczLjc5NSA2NC4xMDc0QzE3My4xNjUgNjQuMTA3NCAxNzIuNjg3IDYzLjkyMyAxNzIuMzYxIDYzLjU1NDJDMTcyLjAzOSA2My4xODU0IDE3MS44NzcgNjIuNjUxOSAxNzEuODc3IDYxLjk1MzZWNTguMTg4NUgxNzMuMTgzVjYxLjkzNzVDMTczLjE4MyA2Mi42NzUxIDE3My40ODkgNjMuMDQzOSAxNzQuMTAxIDYzLjA0MzlDMTc0LjczNSA2My4wNDM5IDE3NS4xNjMgNjIuODE2NiAxNzUuMzg1IDYyLjM2MThWNTguMTg4NUgxNzYuNjlWNjRIMTc1LjQ2TDE3NS40MjggNjMuNDMwN1oiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE3OS42NTUgNTYuNzc1OVY1OC4xODg1SDE4MC42ODFWNTkuMTU1M0gxNzkuNjU1VjYyLjM5OTRDMTc5LjY1NSA2Mi42MjE0IDE3OS42OTggNjIuNzgyNiAxNzkuNzg0IDYyLjg4MjhDMTc5Ljg3MyA2Mi45Nzk1IDE4MC4wMzEgNjMuMDI3OCAxODAuMjU2IDYzLjAyNzhDMTgwLjQwNyA2My4wMjc4IDE4MC41NTkgNjMuMDA5OSAxODAuNzEzIDYyLjk3NDFWNjMuOTgzOUMxODAuNDE2IDY0LjA2NjIgMTgwLjEyOSA2NC4xMDc0IDE3OS44NTQgNjQuMTA3NEMxNzguODUxIDY0LjEwNzQgMTc4LjM1IDYzLjU1NDIgMTc4LjM1IDYyLjQ0NzhWNTkuMTU1M0gxNzcuMzk0VjU4LjE4ODVIMTc4LjM1VjU2Ljc3NTlIMTc5LjY1NVoiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE0NS4zMDEgMTAwLjY4NkgxNDIuMTU0VjEwNEgxNDAuNzk1Vjk2LjE3OTdIMTQ1Ljc2M1Y5Ny4yNzU0SDE0Mi4xNTRWOTkuNjAxMUgxNDUuMzAxVjEwMC42ODZaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTAuMDA2IDEwNEMxNDkuOTQ5IDEwMy44ODkgMTQ5Ljg5OSAxMDMuNzA4IDE0OS44NTYgMTAzLjQ1OEMxNDkuNDQxIDEwMy44OTEgMTQ4LjkzMiAxMDQuMTA3IDE0OC4zMzEgMTA0LjEwN0MxNDcuNzQ3IDEwNC4xMDcgMTQ3LjI3MSAxMDMuOTQxIDE0Ni45MDIgMTAzLjYwOEMxNDYuNTMzIDEwMy4yNzUgMTQ2LjM0OSAxMDIuODYzIDE0Ni4zNDkgMTAyLjM3M0MxNDYuMzQ5IDEwMS43NTMgMTQ2LjU3OCAxMDEuMjc5IDE0Ny4wMzYgMTAwLjk0OUMxNDcuNDk4IDEwMC42MTYgMTQ4LjE1NyAxMDAuNDUgMTQ5LjAxMyAxMDAuNDVIMTQ5LjgxM1YxMDAuMDY4QzE0OS44MTMgOTkuNzY3NiAxNDkuNzI5IDk5LjUyNzcgMTQ5LjU2MSA5OS4zNDg2QzE0OS4zOTIgOTkuMTY2IDE0OS4xMzYgOTkuMDc0NyAxNDguNzkyIDk5LjA3NDdDMTQ4LjQ5NSA5OS4wNzQ3IDE0OC4yNTIgOTkuMTQ5OSAxNDguMDYyIDk5LjMwMDNDMTQ3Ljg3MiA5OS40NDcxIDE0Ny43NzcgOTkuNjM1MSAxNDcuNzc3IDk5Ljg2NDNIMTQ2LjQ3MkMxNDYuNDcyIDk5LjU0NTYgMTQ2LjU3OCA5OS4yNDg0IDE0Ni43ODkgOTguOTcyN0MxNDcgOTguNjkzNCAxNDcuMjg3IDk4LjQ3NDkgMTQ3LjY0OCA5OC4zMTc0QzE0OC4wMTQgOTguMTU5OCAxNDguNDIgOTguMDgxMSAxNDguODY4IDk4LjA4MTFDMTQ5LjU0OCA5OC4wODExIDE1MC4wOSA5OC4yNTI5IDE1MC40OTUgOTguNTk2N0MxNTAuOSA5OC45MzY4IDE1MS4xMDcgOTkuNDE2NyAxNTEuMTE4IDEwMC4wMzZWMTAyLjY1N0MxNTEuMTE4IDEwMy4xOCAxNTEuMTkyIDEwMy41OTcgMTUxLjMzOCAxMDMuOTA5VjEwNEgxNTAuMDA2Wk0xNDguNTcyIDEwMy4wNkMxNDguODMgMTAzLjA2IDE0OS4wNzIgMTAyLjk5NyAxNDkuMjk3IDEwMi44NzJDMTQ5LjUyNyAxMDIuNzQ3IDE0OS42OTggMTAyLjU3OCAxNDkuODEzIDEwMi4zNjdWMTAxLjI3MUgxNDkuMTA5QzE0OC42MjYgMTAxLjI3MSAxNDguMjYzIDEwMS4zNTYgMTQ4LjAxOSAxMDEuNTI0QzE0Ny43NzYgMTAxLjY5MiAxNDcuNjU0IDEwMS45MyAxNDcuNjU0IDEwMi4yMzhDMTQ3LjY1NCAxMDIuNDg5IDE0Ny43MzYgMTAyLjY4OSAxNDcuOTAxIDEwMi44NEMxNDguMDY5IDEwMi45ODcgMTQ4LjI5MyAxMDMuMDYgMTQ4LjU3MiAxMDMuMDZaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTMuODc0IDEwNEgxNTIuNTY4Vjk4LjE4ODVIMTUzLjg3NFYxMDRaTTE1Mi40ODggOTYuNjc5MkMxNTIuNDg4IDk2LjQ3ODcgMTUyLjU1IDk2LjMxMjIgMTUyLjY3NiA5Ni4xNzk3QzE1Mi44MDUgOTYuMDQ3MiAxNTIuOTg3IDk1Ljk4MSAxNTMuMjI0IDk1Ljk4MUMxNTMuNDYgOTUuOTgxIDE1My42NDMgOTYuMDQ3MiAxNTMuNzcxIDk2LjE3OTdDMTUzLjkgOTYuMzEyMiAxNTMuOTY1IDk2LjQ3ODcgMTUzLjk2NSA5Ni42NzkyQzE1My45NjUgOTYuODc2MSAxNTMuOSA5Ny4wNDA5IDE1My43NzEgOTcuMTczM0MxNTMuNjQzIDk3LjMwMjIgMTUzLjQ2IDk3LjM2NjcgMTUzLjIyNCA5Ny4zNjY3QzE1Mi45ODcgOTcuMzY2NyAxNTIuODA1IDk3LjMwMjIgMTUyLjY3NiA5Ny4xNzMzQzE1Mi41NSA5Ny4wNDA5IDE1Mi40ODggOTYuODc2MSAxNTIuNDg4IDk2LjY3OTJaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTYuNjg4IDEwNEgxNTUuMzgzVjk1Ljc1SDE1Ni42ODhWMTA0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTYwLjY3MyAxMDQuMTA3QzE1OS44NDYgMTA0LjEwNyAxNTkuMTc1IDEwMy44NDggMTU4LjY1OSAxMDMuMzI5QzE1OC4xNDcgMTAyLjgwNiAxNTcuODkxIDEwMi4xMTEgMTU3Ljg5MSAxMDEuMjQ1VjEwMS4wODNDMTU3Ljg5MSAxMDAuNTAzIDE1OC4wMDIgOTkuOTg2IDE1OC4yMjQgOTkuNTMxMkMxNTguNDUgOTkuMDcyOSAxNTguNzY1IDk4LjcxNjYgMTU5LjE2OSA5OC40NjI0QzE1OS41NzQgOTguMjA4MiAxNjAuMDI1IDk4LjA4MTEgMTYwLjUyMyA5OC4wODExQzE2MS4zMTQgOTguMDgxMSAxNjEuOTI1IDk4LjMzMzUgMTYyLjM1NCA5OC44Mzg0QzE2Mi43ODggOTkuMzQzMyAxNjMuMDA0IDEwMC4wNTggMTYzLjAwNCAxMDAuOTgxVjEwMS41MDhIMTU5LjIwN0MxNTkuMjQ2IDEwMS45ODggMTU5LjQwNiAxMDIuMzY3IDE1OS42ODUgMTAyLjY0NkMxNTkuOTY4IDEwMi45MjYgMTYwLjMyMiAxMDMuMDY1IDE2MC43NDkgMTAzLjA2NUMxNjEuMzQ3IDEwMy4wNjUgMTYxLjgzMyAxMDIuODI0IDE2Mi4yMDkgMTAyLjM0TDE2Mi45MTMgMTAzLjAxMkMxNjIuNjggMTAzLjM1OSAxNjIuMzY5IDEwMy42MjkgMTYxLjk3OSAxMDMuODIzQzE2MS41OTIgMTA0LjAxMyAxNjEuMTU3IDEwNC4xMDcgMTYwLjY3MyAxMDQuMTA3Wk0xNjAuNTE4IDk5LjEyODRDMTYwLjE2IDk5LjEyODQgMTU5Ljg2OSA5OS4yNTM3IDE1OS42NDcgOTkuNTA0NEMxNTkuNDI5IDk5Ljc1NSAxNTkuMjg5IDEwMC4xMDQgMTU5LjIyOSAxMDAuNTUySDE2MS43MTVWMTAwLjQ1NUMxNjEuNjg3IDEwMC4wMTggMTYxLjU3IDk5LjY4ODggMTYxLjM2NiA5OS40NjY4QzE2MS4xNjIgOTkuMjQxMiAxNjAuODc5IDk5LjEyODQgMTYwLjUxOCA5OS4xMjg0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTYzLjc3OCAxMDEuMDUxQzE2My43NzggMTAwLjE1NiAxNjMuOTg2IDk5LjQzODIgMTY0LjQwMSA5OC44OTc1QzE2NC44MTYgOTguMzUzMiAxNjUuMzczIDk4LjA4MTEgMTY2LjA3MSA5OC4wODExQzE2Ni42ODcgOTguMDgxMSAxNjcuMTg1IDk4LjI5NTkgMTY3LjU2NCA5OC43MjU2Vjk1Ljc1SDE2OC44N1YxMDRIMTY3LjY4OEwxNjcuNjI0IDEwMy4zOThDMTY3LjIzMyAxMDMuODcxIDE2Ni43MTIgMTA0LjEwNyAxNjYuMDYxIDEwNC4xMDdDMTY1LjM4IDEwNC4xMDcgMTY0LjgyOSAxMDMuODMzIDE2NC40MDYgMTAzLjI4NkMxNjMuOTg3IDEwMi43MzggMTYzLjc3OCAxMDEuOTkzIDE2My43NzggMTAxLjA1MVpNMTY1LjA4MyAxMDEuMTY0QzE2NS4wODMgMTAxLjc1NSAxNjUuMTk2IDEwMi4yMTcgMTY1LjQyMSAxMDIuNTVDMTY1LjY1MSAxMDIuODc5IDE2NS45NzUgMTAzLjA0NCAxNjYuMzk0IDEwMy4wNDRDMTY2LjkyNyAxMDMuMDQ0IDE2Ny4zMTcgMTAyLjgwNiAxNjcuNTY0IDEwMi4zM1Y5OS44NDgxQzE2Ny4zMjUgOTkuMzgyNiAxNjYuOTM4IDk5LjE0OTkgMTY2LjQwNCA5OS4xNDk5QzE2NS45ODIgOTkuMTQ5OSAxNjUuNjU2IDk5LjMxODIgMTY1LjQyNyA5OS42NTQ4QzE2NS4xOTggOTkuOTg3OCAxNjUuMDgzIDEwMC40OTEgMTY1LjA4MyAxMDEuMTY0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTQwLjc5NSAxNDRWMTM2LjE4SDE0My4xMDRDMTQzLjc5NiAxMzYuMTggMTQ0LjQwOCAxMzYuMzM0IDE0NC45NDEgMTM2LjY0MkMxNDUuNDc5IDEzNi45NSAxNDUuODk0IDEzNy4zODYgMTQ2LjE4OCAxMzcuOTUyQzE0Ni40ODEgMTM4LjUxOCAxNDYuNjI4IDEzOS4xNjYgMTQ2LjYyOCAxMzkuODk2VjE0MC4yODlDMTQ2LjYyOCAxNDEuMDMgMTQ2LjQ3OSAxNDEuNjgxIDE0Ni4xODIgMTQyLjI0NEMxNDUuODg5IDE0Mi44MDYgMTQ1LjQ2OCAxNDMuMjM5IDE0NC45MiAxNDMuNTQzQzE0NC4zNzYgMTQzLjg0OCAxNDMuNzUxIDE0NCAxNDMuMDQ1IDE0NEgxNDAuNzk1Wk0xNDIuMTU0IDEzNy4yNzVWMTQyLjkxNUgxNDMuMDRDMTQzLjc1MyAxNDIuOTE1IDE0NC4yOTkgMTQyLjY5MyAxNDQuNjc4IDE0Mi4yNDlDMTQ1LjA2MSAxNDEuODAxIDE0NS4yNTcgMTQxLjE2IDE0NS4yNjQgMTQwLjMyNlYxMzkuODkxQzE0NS4yNjQgMTM5LjA0MiAxNDUuMDc5IDEzOC4zOTQgMTQ0LjcxIDEzNy45NDdDMTQ0LjM0MiAxMzcuNDk5IDE0My44MDYgMTM3LjI3NSAxNDMuMTA0IDEzNy4yNzVIMTQyLjE1NFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE1MC40MTUgMTQ0LjEwN0MxNDkuNTg3IDE0NC4xMDcgMTQ4LjkxNiAxNDMuODQ4IDE0OC40IDE0My4zMjlDMTQ3Ljg4OCAxNDIuODA2IDE0Ny42MzIgMTQyLjExMSAxNDcuNjMyIDE0MS4yNDVWMTQxLjA4M0MxNDcuNjMyIDE0MC41MDMgMTQ3Ljc0MyAxMzkuOTg2IDE0Ny45NjUgMTM5LjUzMUMxNDguMTkxIDEzOS4wNzMgMTQ4LjUwNiAxMzguNzE3IDE0OC45MTEgMTM4LjQ2MkMxNDkuMzE1IDEzOC4yMDggMTQ5Ljc2NiAxMzguMDgxIDE1MC4yNjQgMTM4LjA4MUMxNTEuMDU2IDEzOC4wODEgMTUxLjY2NiAxMzguMzMzIDE1Mi4wOTYgMTM4LjgzOEMxNTIuNTI5IDEzOS4zNDMgMTUyLjc0NiAxNDAuMDU4IDE1Mi43NDYgMTQwLjk4MVYxNDEuNTA4SDE0OC45NDhDMTQ4Ljk4OCAxNDEuOTg4IDE0OS4xNDcgMTQyLjM2NyAxNDkuNDI2IDE0Mi42NDZDMTQ5LjcwOSAxNDIuOTI2IDE1MC4wNjQgMTQzLjA2NSAxNTAuNDkgMTQzLjA2NUMxNTEuMDg4IDE0My4wNjUgMTUxLjU3NSAxNDIuODI0IDE1MS45NTEgMTQyLjM0TDE1Mi42NTQgMTQzLjAxMkMxNTIuNDIyIDE0My4zNTkgMTUyLjExIDE0My42MjkgMTUxLjcyIDE0My44MjNDMTUxLjMzMyAxNDQuMDEzIDE1MC44OTggMTQ0LjEwNyAxNTAuNDE1IDE0NC4xMDdaTTE1MC4yNTkgMTM5LjEyOEMxNDkuOTAxIDEzOS4xMjggMTQ5LjYxMSAxMzkuMjU0IDE0OS4zODkgMTM5LjUwNEMxNDkuMTcgMTM5Ljc1NSAxNDkuMDMxIDE0MC4xMDQgMTQ4Ljk3IDE0MC41NTJIMTUxLjQ1N1YxNDAuNDU1QzE1MS40MjggMTQwLjAxOCAxNTEuMzEyIDEzOS42ODkgMTUxLjEwNyAxMzkuNDY3QzE1MC45MDMgMTM5LjI0MSAxNTAuNjIgMTM5LjEyOCAxNTAuMjU5IDEzOS4xMjhaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNTUuMTUyIDE0NEgxNTMuODQ3VjEzNS43NUgxNTUuMTUyVjE0NFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE1Ny45NjYgMTQ0SDE1Ni42NjFWMTM4LjE4OEgxNTcuOTY2VjE0NFpNMTU2LjU4MSAxMzYuNjc5QzE1Ni41ODEgMTM2LjQ3OSAxNTYuNjQzIDEzNi4zMTIgMTU2Ljc2OSAxMzYuMThDMTU2Ljg5NyAxMzYuMDQ3IDE1Ny4wOCAxMzUuOTgxIDE1Ny4zMTYgMTM1Ljk4MUMxNTcuNTUzIDEzNS45ODEgMTU3LjczNSAxMzYuMDQ3IDE1Ny44NjQgMTM2LjE4QzE1Ny45OTMgMTM2LjMxMiAxNTguMDU4IDEzNi40NzkgMTU4LjA1OCAxMzYuNjc5QzE1OC4wNTggMTM2Ljg3NiAxNTcuOTkzIDEzNy4wNDEgMTU3Ljg2NCAxMzcuMTczQzE1Ny43MzUgMTM3LjMwMiAxNTcuNTUzIDEzNy4zNjcgMTU3LjMxNiAxMzcuMzY3QzE1Ny4wOCAxMzcuMzY3IDE1Ni44OTcgMTM3LjMwMiAxNTYuNzY5IDEzNy4xNzNDMTU2LjY0MyAxMzcuMDQxIDE1Ni41ODEgMTM2Ljg3NiAxNTYuNTgxIDEzNi42NzlaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNjEuNDQxIDE0Mi4zNDZMMTYyLjY3MSAxMzguMTg4SDE2NC4wMkwxNjIuMDA1IDE0NEgxNjAuODcyTDE1OC44NDIgMTM4LjE4OEgxNjAuMTk1TDE2MS40NDEgMTQyLjM0NloiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE2Ny4zMjMgMTQ0LjEwN0MxNjYuNDk2IDE0NC4xMDcgMTY1LjgyNCAxNDMuODQ4IDE2NS4zMDkgMTQzLjMyOUMxNjQuNzk3IDE0Mi44MDYgMTY0LjU0MSAxNDIuMTExIDE2NC41NDEgMTQxLjI0NVYxNDEuMDgzQzE2NC41NDEgMTQwLjUwMyAxNjQuNjUyIDEzOS45ODYgMTY0Ljg3NCAxMzkuNTMxQzE2NS4wOTkgMTM5LjA3MyAxNjUuNDE0IDEzOC43MTcgMTY1LjgxOSAxMzguNDYyQzE2Ni4yMjMgMTM4LjIwOCAxNjYuNjc1IDEzOC4wODEgMTY3LjE3MiAxMzguMDgxQzE2Ny45NjQgMTM4LjA4MSAxNjguNTc0IDEzOC4zMzMgMTY5LjAwNCAxMzguODM4QzE2OS40MzcgMTM5LjM0MyAxNjkuNjU0IDE0MC4wNTggMTY5LjY1NCAxNDAuOTgxVjE0MS41MDhIMTY1Ljg1NkMxNjUuODk2IDE0MS45ODggMTY2LjA1NSAxNDIuMzY3IDE2Ni4zMzQgMTQyLjY0NkMxNjYuNjE3IDE0Mi45MjYgMTY2Ljk3MiAxNDMuMDY1IDE2Ny4zOTggMTQzLjA2NUMxNjcuOTk2IDE0My4wNjUgMTY4LjQ4MyAxNDIuODI0IDE2OC44NTkgMTQyLjM0TDE2OS41NjIgMTQzLjAxMkMxNjkuMzMgMTQzLjM1OSAxNjkuMDE4IDE0My42MjkgMTY4LjYyOCAxNDMuODIzQzE2OC4yNDEgMTQ0LjAxMyAxNjcuODA2IDE0NC4xMDcgMTY3LjMyMyAxNDQuMTA3Wk0xNjcuMTY3IDEzOS4xMjhDMTY2LjgwOSAxMzkuMTI4IDE2Ni41MTkgMTM5LjI1NCAxNjYuMjk3IDEzOS41MDRDMTY2LjA3OCAxMzkuNzU1IDE2NS45MzkgMTQwLjEwNCAxNjUuODc4IDE0MC41NTJIMTY4LjM2NVYxNDAuNDU1QzE2OC4zMzYgMTQwLjAxOCAxNjguMjIgMTM5LjY4OSAxNjguMDE2IDEzOS40NjdDMTY3LjgxMiAxMzkuMjQxIDE2Ny41MjkgMTM5LjEyOCAxNjcuMTY3IDEzOS4xMjhaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNzMuNzE0IDEzOS4zODFDMTczLjU0MiAxMzkuMzUyIDE3My4zNjUgMTM5LjMzOCAxNzMuMTgzIDEzOS4zMzhDMTcyLjU4NSAxMzkuMzM4IDE3Mi4xODIgMTM5LjU2NyAxNzEuOTc0IDE0MC4wMjVWMTQ0SDE3MC42NjlWMTM4LjE4OEgxNzEuOTE1TDE3MS45NDcgMTM4LjgzOEMxNzIuMjYyIDEzOC4zMzMgMTcyLjY5OSAxMzguMDgxIDE3My4yNTggMTM4LjA4MUMxNzMuNDQ0IDEzOC4wODEgMTczLjU5OCAxMzguMTA2IDE3My43MiAxMzguMTU2TDE3My43MTQgMTM5LjM4MVoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE3Ni45OTEgMTQ0LjEwN0MxNzYuMTY0IDE0NC4xMDcgMTc1LjQ5MiAxNDMuODQ4IDE3NC45NzcgMTQzLjMyOUMxNzQuNDY1IDE0Mi44MDYgMTc0LjIwOCAxNDIuMTExIDE3NC4yMDggMTQxLjI0NVYxNDEuMDgzQzE3NC4yMDggMTQwLjUwMyAxNzQuMzE5IDEzOS45ODYgMTc0LjU0MiAxMzkuNTMxQzE3NC43NjcgMTM5LjA3MyAxNzUuMDgyIDEzOC43MTcgMTc1LjQ4NyAxMzguNDYyQzE3NS44OTEgMTM4LjIwOCAxNzYuMzQzIDEzOC4wODEgMTc2Ljg0IDEzOC4wODFDMTc3LjYzMiAxMzguMDgxIDE3OC4yNDIgMTM4LjMzMyAxNzguNjcyIDEzOC44MzhDMTc5LjEwNSAxMzkuMzQzIDE3OS4zMjIgMTQwLjA1OCAxNzkuMzIyIDE0MC45ODFWMTQxLjUwOEgxNzUuNTI0QzE3NS41NjQgMTQxLjk4OCAxNzUuNzIzIDE0Mi4zNjcgMTc2LjAwMiAxNDIuNjQ2QzE3Ni4yODUgMTQyLjkyNiAxNzYuNjQgMTQzLjA2NSAxNzcuMDY2IDE0My4wNjVDMTc3LjY2NCAxNDMuMDY1IDE3OC4xNTEgMTQyLjgyNCAxNzguNTI3IDE0Mi4zNEwxNzkuMjMgMTQzLjAxMkMxNzguOTk4IDE0My4zNTkgMTc4LjY4NiAxNDMuNjI5IDE3OC4yOTYgMTQzLjgyM0MxNzcuOTA5IDE0NC4wMTMgMTc3LjQ3NCAxNDQuMTA3IDE3Ni45OTEgMTQ0LjEwN1pNMTc2LjgzNSAxMzkuMTI4QzE3Ni40NzcgMTM5LjEyOCAxNzYuMTg3IDEzOS4yNTQgMTc1Ljk2NSAxMzkuNTA0QzE3NS43NDYgMTM5Ljc1NSAxNzUuNjA3IDE0MC4xMDQgMTc1LjU0NiAxNDAuNTUySDE3OC4wMzNWMTQwLjQ1NUMxNzguMDA0IDE0MC4wMTggMTc3Ljg4OCAxMzkuNjg5IDE3Ny42ODQgMTM5LjQ2N0MxNzcuNDc5IDEzOS4yNDEgMTc3LjE5NyAxMzkuMTI4IDE3Ni44MzUgMTM5LjEyOFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE4MC4wOTUgMTQxLjA1MUMxODAuMDk1IDE0MC4xNTYgMTgwLjMwMyAxMzkuNDM4IDE4MC43MTggMTM4Ljg5N0MxODEuMTM0IDEzOC4zNTMgMTgxLjY5IDEzOC4wODEgMTgyLjM4OSAxMzguMDgxQzE4My4wMDUgMTM4LjA4MSAxODMuNTAyIDEzOC4yOTYgMTgzLjg4MiAxMzguNzI2VjEzNS43NUgxODUuMTg3VjE0NEgxODQuMDA1TDE4My45NDEgMTQzLjM5OEMxODMuNTUxIDE0My44NzEgMTgzLjAzIDE0NC4xMDcgMTgyLjM3OCAxNDQuMTA3QzE4MS42OTggMTQ0LjEwNyAxODEuMTQ2IDE0My44MzMgMTgwLjcyNCAxNDMuMjg2QzE4MC4zMDUgMTQyLjczOCAxODAuMDk1IDE0MS45OTMgMTgwLjA5NSAxNDEuMDUxWk0xODEuNCAxNDEuMTY0QzE4MS40IDE0MS43NTUgMTgxLjUxMyAxNDIuMjE3IDE4MS43MzkgMTQyLjU1QzE4MS45NjggMTQyLjg3OSAxODIuMjkyIDE0My4wNDQgMTgyLjcxMSAxNDMuMDQ0QzE4My4yNDQgMTQzLjA0NCAxODMuNjM1IDE0Mi44MDYgMTgzLjg4MiAxNDIuMzNWMTM5Ljg0OEMxODMuNjQyIDEzOS4zODMgMTgzLjI1NSAxMzkuMTUgMTgyLjcyMiAxMzkuMTVDMTgyLjI5OSAxMzkuMTUgMTgxLjk3MyAxMzkuMzE4IDE4MS43NDQgMTM5LjY1NUMxODEuNTE1IDEzOS45ODggMTgxLjQgMTQwLjQ5MSAxODEuNCAxNDEuMTY0WiIgZmlsbD0iIzRDQUY1MCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE5NiAxSDRDMi4zNDMxNCAxIDEgMi4zNDMxNCAxIDRWMTU2QzEgMTU3LjY1NyAyLjM0MzE0IDE1OSA0IDE1OUgxOTZDMTk3LjY1NyAxNTkgMTk5IDE1Ny42NTcgMTk5IDE1NlY0QzE5OSAyLjM0MzE1IDE5Ny42NTcgMSAxOTYgMVpNNCAwSDE5NkMxOTguMjA5IDAgMjAwIDEuNzkwODYgMjAwIDRWMTU2QzIwMCAxNTguMjA5IDE5OC4yMDkgMTYwIDE5NiAxNjBINEMxLjc5MDg2IDE2MCAwIDE1OC4yMDkgMCAxNTZWNEMwIDEuNzkwODYgMS43OTA4NiAwIDQgMFoiIGZpbGw9IiNFMEUwRTAiLz4KPC9zdmc+Cg==",
+ "description": "Displays Persistent RPC requests that match selected alias and filter with the ability of pagination and sending persistent RPC requests.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 7.5,
+ "sizeY": 4,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-persistent-table-widget-settings",
+ "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableStickyAction\":true,\"enableFilter\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"enableStickyHeader\":true,\"displayColumns\":[\"rpcId\",\"messageType\",\"status\",\"method\",\"createdTime\",\"expirationTime\"],\"displayDetails\":true,\"defaultSortOrder\":\"-createdTime\",\"allowSendRequest\":true,\"allowDelete\":true},\"title\":\"Persistent RPC table\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px\"},\"targetDeviceAliasIds\":[]}"
+ }
+ },
+ {
+ "alias": "slide_toggle_control",
+ "name": "Slide Toggle Control",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to send the RPC call to device when user toggle the slider. Advanced widget settings allow you to configure how to fetch the initial value of the slider.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 3,
+ "sizeY": 1,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-slide-toggle-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Slide toggle control\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\",\"requestPersistent\":false,\"labelPosition\":\"after\",\"sliderColor\":\"accent\"},\"title\":\"Slide Toggle Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2,\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\"}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/date.json b/application/src/main/data/json/system/widget_bundles/date.json
new file mode 100644
index 0000000000000000000000000000000000000000..dc868235df257a5a10f3df1d98ef9e56532ec942
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/date.json
@@ -0,0 +1,29 @@
+{
+ "widgetsBundle": {
+ "alias": "date",
+ "title": "Date",
+ "image": "data:image/png;base64,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",
+ "description": "Contains widgets to change the data range for other widgets on the dashboard."
+ },
+ "widgetTypes": [
+ {
+ "alias": "date_range_navigator",
+ "name": "Date-range-navigator",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to change the data range for other widgets on the dashboard.",
+ "descriptor": {
+ "type": "static",
+ "sizeX": 5,
+ "sizeY": 5.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-date-range-navigator-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/digital_gauges.json b/application/src/main/data/json/system/widget_bundles/digital_gauges.json
new file mode 100644
index 0000000000000000000000000000000000000000..7974ae2ea9882e9c9ca22bb2c5b9461c87c2e9a6
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/digital_gauges.json
@@ -0,0 +1,257 @@
+{
+ "widgetsBundle": {
+ "alias": "digital_gauges",
+ "title": "Digital gauges",
+ "image": "data:image/png;base64,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",
+ "description": "Display temperature, humidity, speed, and other latest values on various digital gauge widgets."
+ },
+ "widgetTypes": [
+ {
+ "alias": "gauge_justgage",
+ "name": "Gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 4,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":36,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"arc\"},\"title\":\"Gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "digital_thermometer",
+ "name": "Digital thermometer",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display temperature. Allows to configure temperature range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 3,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -60) {\\n\\tvalue = 60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[\"#304ffe\",\"#7e57c2\",\"#ff4081\",\"#d32f2f\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"dashThickness\":1.5,\"minValue\":-60,\"gaugeColor\":\"#333333\",\"neonGlowBrightness\":35,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital thermometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "digital_speedometer",
+ "name": "Digital speedometer",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display speed. Allows to configure speed range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 45) {\\n\\tvalue = 45;\\n} else if (value > 130) {\\n\\tvalue = 130;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital speedometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "neon_gauge_justgage",
+ "name": "Neon gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as an arc. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":70,\"dashThickness\":1,\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "lcd_gauge",
+ "name": "LCD gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as an arc. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 180) {\\n\\tvalue = 180;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"arc\"},\"title\":\"LCD gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "lcd_bar_gauge",
+ "name": "LCD bar gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 2,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"400\",\"size\":16},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"verticalBar\",\"units\":\"%\"},\"title\":\"LCD bar gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "simple_neon_gauge_justgage",
+ "name": "Simple neon gauge",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAolElEQVR42u1daZgsVXnumu7p6WWWO3fuMnMb2TdBAVFWURZFEFkuywVEFgUEERBEBNkX2XfZZVPMozH7/mgSE7M92XcTg9kDmoQsJjH5T96vvnPe89Wp6p6+3Blmurvq6ec+0zU1datOvef93u893zlVee21115++eVTTz11amqqUm7ltg0bILR58+aXXnoJoKoAVWvXri0bpdyWapudnQWoKuCqsi3KbWm3LVu2VMoIWG5Lvk1PT5eNUG7ltpxbMpZUJ6q19vj45Dh3NtY1W5va7U672qzpnv1PePtTrz772CtPffInr+ZhR174nsM/csReR+7dmmmVLTnq21htDDAaGx/Tr0BVe7tJfFoL7QCs9U3dWfPAOuDkA1/4vy/hc+3Xr+dhD/7153Tnnu96s+7Zft8d3nXuuzft2QFey6YeoW1ibUMRU5+uO8aqJroHnwCsOXdYreWAddCWQxRDl33lCgfQ6thz//0F3Tm9YUZ3nnzTqbrnohcuKVt7aLdqvVqfmWjOt4Ae3YN4p4hBsONhEvXSnTxsYnYiAtahZx2miPnIExfonjULs7rn8e89zVMhUOrO937saB52z5/ff/qdZ+528G5JUtLYUGyAlMNH2+knRMB84GtucIFvrO7iY32mHv0hApwi5vQ7ztQ9O719Z91zw6/czFM9/HeP6c7t9n6T+8PzDtc9937rgRJYA6rDK8BBY12DO4iPiblGP/xEqT4+FQPriAuOVHx84OoTKOcdhz15YcRh0PjUWJd++XLdecrNpzGGfuJHP/nOs9813hgvH9oAoAok5PDRqMX8tKldSWL9xMMIIyaGwFO056iL3qv4AMKiPcdeeZzu2ff9++meS7/yCZ8oVBEoIw7b51h32MN/+2i9WS8f3So1C/LC3PJTc6OLhsj+Ipk17vU7EBYpeiSDEbCOvvQYRQPSQ91zyi2n6R7gSfec8JmTnMC65H26Z4/D9tQ9d/7RPbwkwE53XvD5i7hzcq70q1cJpKpJfc1EuzMJ+yDiJ+wk4IAeB5qZenQY8YczuD2zE5EHMT7lgHXsFccpGvY6Ym/dA1jong07b9Q9SBgdOb3FkdNpt5+ue0649iQHoLWTz/znC7pz+3221504w+f/4/kLn7143Y7ryye70sLcUxHRYP0nMg1Bg+MjmdWaD3uARZsqEnyIkrrnuE8d79Cw7w665+qfuxZfgRJoJt1z/7cfigTWbb97p/7Vxl3mdc8xlx+re67++Wv5v1/w9Ed1522/c0f5ZFfaSvDRSvjJkxaDmoVRc0MrUuuUWUSAwpR/RXOL4ZJhbm77dboH0Q1fb/+9Ox00Z9vP/++LVmBBy+uem3/jNl4MobbPMfvqHpzw2f9yftjbjt/fXUC6lU/5jdjAPSAkpmmWnyxpkcnG6tVuKR5lFoXXRAo10fheukWqa/MNp+izb0y6APrEv3weXy/50qX6FQM7kcB694ePcOr+Cqfudz5gF91z15/cS0yf99j5uvMzv3gD7+Lw84+87pdvXNhjU/ncl3cTNd1x+R0fSUZUeTZiQke0kX6CqMpFurqXYuQJ/e8ILJXqoBY9YKLdUDQAcHrA+z/5Ad3zprdub0U6SIskd+7nPqLHADe6Z7az9pnvv6CHwRijDnv05SexE8Lr7ScdUD795c3+6D9ZfgpDNGsmgu+gEspaDKkFavfoMfhzB8eWi6FMBfQASn74onjSj/z9Y/p1frcFhQiGenTPxV/8uBVY1fHqE/8sRgOIx2G3WVfrAcfUW+60H3rwHDcE9PzHeFPnPerwB3jNbJwpn/4yhD8fzuyzt2EO6kqpxZIW6YcDMvnYpzKr6fU7OYwHqGtPvJ55z1nWNXjz4XtFmd1df3qfHUnEsLQeALtL9xz6wXdGJAfQPP3vz0kG8P0X1u+0QXfu+LYdn//Bi87aOOWgwqYot20yPNUQpxtOJSSA2BC0Oe11DAsGhaRZ3vpmrJk8AxFqLvYllQiLSnIE1lkPCLVc/42b9OshZx4q8esHLyr3IDIqGo7++DF6wJbPnoGvz/3PFzk+fc3XrtMskiR0xt1nKYDgSvA6b/zmLW60+4evCJlKo+r4tRT027gxxomo8lQkAdHb61TiIUp2gvziKA0TRlX6zPvyFKVnpurC0JANu+c+8mE87Ct+7CrnPlwl7sN9f/mQft390D0igfXZ378LXz/1s9foVxhUmiFy/AdC6qlXn9HIiIxSd3Lg6HP/+MT0+mlqxCAD1jZKbGwbYdXG2JokHusyWBVP+sm7oMFM96KeMNXz0/GCiWUpSpFNYH34ccndLnzmIktgLPoDUVmBBUmuMMIItB7Achoi79Rbt0R1EIDXo//0hO60mp3JLy6YErDcthpPAUPe/rZEYsksqPjESSKLNgUKqxgYH0l1KrN4EvHxTebovvrfqs/+wXs/ZDM+xDL7WwYvJH2aQioV4b++/68eFgL7GUdgzemWCnmUPNTqtch3+OhzQchz5CAd3KwWNlS5LbIJ8XQyzYcRlQLBbgIid4bRvTACWI2eh4tuHjrKcwyOSmkoobFPlKEHWRse+YnXbdavqoRQl2xtT4wn6lcgDF9RwqBf33r0PooY/KB7Trr+ZMdMJ74j0uwYmWZkRBmPS00Ms7ou18l0tnLrjirfNYXwx0N3pFoCmMhGwWE3Kl5DhiUtNUuJJM0raTowXOpXJUgynMKOwIIRiqf+notd2HrwO4/gK6SVHNkYV+tcM0QaDQef7pyIS37oMmeKpolCc6r52Hef0kIu3WM1O6pxQv/x9WRWWuGyKRLYi8qtu7Ng2ksw5JWQhDlfiGfLFkKRJ4v46tUobjpdxTHpBNGwnRHs6f+o9X3MHC0K+T9q7FOs4EgtStaShJ3esbMVWOpEPP1vzyFVrJhRZxjxeqrjP32iOqK7HrSb0+wXHhWVdtmIL5zKxqgGtpYeWC8DYn+uVavTZmvSBLetmUkGF9oRRenDkD3eRHC5no8jalhQ46vM4jkVZ4ppjaQND6xP/MiVjGXwC9S9tKkcBRaqkO1Qj+p6ZHlaejXRnsDP2ANDNdLsmJrRXtOOUo1ufSxFVels9YyANvCl6sEng6YWVNjIGaEhM6KKCqM33i9luYuapUFIpVKMX12883+uwVRpQPmP9Q5azA5yUj0kQ3u/dIMdqKHA+uwf3G3F062/fQe+QlTpVy2/gSO6wTuimm+CwKjAcIOug3UCuTId0Uaw+9GAZUzMqfVc51Nlmh+9YSe2oYEBkWeATeqkUpJJBl3USDK0pDKLgt1ZsmllqdbbEFhwpFh6td8H9sfPSOL0Vzf9+q0sp1m73Vr8jPFp5acd99/JmqJQYw/9jcwYA6s5zb7/TqrZ4ZMFWtoYl+ozS3VoM/kN7sv5JlNloXM0GgMcZLugUFGnwGhg49JtQshThUsVT780hL8ULpFBlQl/HaffNQnVX6nkooWmvrnma5inip9RWZXir4qhYngHWpWlaomloWc/fB6+gpP0KywrUWPffYo2xE2/dqsarSyayN9jxRRoRKhCo1GY2vKhUd+idsk0mS9nsMMsePx0C1mxTrsrqoohaSktMRmMxJPKLA3H+it6qhZYGEsGtSh6MC1C9Nb7JHKhWBQ/X/7VKzNSLP0V+EmzPy0oRbaolYAMmgpQnJbzXRncMaBEVqaTYifQutbzskF6ZqOUXFEyuGCwZRquZkr5qtbEohfKUee0lwcV7/3S4IWm6sQBNI2GeBIZlkp/laThj/EXhzFKwg5A3ufs0LTgc90OUgyDyTb4+X2XCXvB6nzyX5/BYep5IoUUd/6nrrYaXxzRiZpmi6rZz7r/7ChHscZKEJe2g2VJHcdbnTqyIVCCkR2aQIOyyBONZTslKxrsaIYwULaSmEgiJhSUJC3HUuuzWioNvspnjDs4M0+LczLC3vybt2PeKfUWqhL02QMWFFha6wctr4eh+Bhf937PW5Su7vuLB/H1HZvdWA0mvirO1JUIQzedSaLEjmtZvSU2nkeVpM/VMDSNVhrRsWp9qGivDHUnidri+a4ZEm9MaPb9OPjsXoHRxKKK1/IEPg9VxKpCNHqyGgIXQ8zhMOaMQBV/RjU69BDzPuR6DJGwQzVEarXCnu+WuAY+Q4zDULQaH4edI/Ndb/hV54iqZsdHLVaronjvuFlOuLW2e82IBPQBO0+J0mLkxqr5+KM5MHHik+2gYV68UR6uWqZjpy9PWBWv/xcrrhSg1E8gM4IGMotFyYjFDMe26hwinfoaWaEWp+MABD7oKt2Pai0MyCjIdNAG86cr6QxV2O74iln2VrMzN2S8o8EmatJbVjZ9sUVpNmWOhr9sBxsZwd6oBlGVqzEK6U+ELd9q1nxXkgtM5hN1/qHaoUFaLbQ5YqMg1iAi/77e2AFbYWZ+jbqmqL5SwSSjzt9+6JF/eFznOqvYwsCO/slRHxXNfucf36uWBNCpzGSXk2DlWQZV7QCdujWukmC7+GhQG0mVVU2C15dTCZbqbQio52YISrDIjgYqS1HFq6MR7ND0DCqt8D8u+UpDU+umFGR7H/UWjlgDNwiIcER1BpjWs2NESL1W2h9NE+hD6aJBD2lbZKiRCqlCDY2JmG4bc/g3wCW6YUtOIJKq9UjrwYbI+Fgz9fy0QT2Sba3HRMUwmhDgyeG3b0C7wyZFBSlwhp+h1gEyVDY7zf7khbKOwy2n2dBmU5N8bZltK2GjqKFMTh3VO+BObSY0jNIqZQ40iu1q6hpTn0b8BLLhr2wTU4cFH0u9Hw71+ICoIkP9Uhtl3uDtqp/+NNwsEFUlXaAGgv2OP7xbQyR7RXwvWXeUVI0Gsdm0RMZOcc/Unqze7zDXBtLV1LBlYxB+5jC+Y5ok/Ip/yAwu5JWdYN5ojBPZngQ/IlQho8VXLjigKF6jnmp2BEGkhK4DpCGMw3xi6aWGp1XlJGloedtu3L9ok9r626GLg+3x4LArn2cpGkxGE1la0IznU8baTuyqGMzIf1S2UDdyapVsWPQBI80cjdYclgMAdEetU0B6FhFZ1N8k/E1mMuuqEpVpapsDDaNgH0usm5DvZ2BsDr6KlWwqjcIyV7kZhSL8VacnTsi7sJJUqqsv34bdAKeUIS9cfC4LqXQZNBRW8woBf2IN94iotLlGZSlUNIRNYUQZWOpKMmP4hTap7dDKUkL1SfCp7XjIqu1jYnwgdfWiUHPkhqEl2gcZa7QV8mVBW9LVxBHMDbGPJZnwxlY+MZHAZ+g6oi5pI/9bK9u5P8SFxJexe7TpwtoDoQ3CshEphiS9SDJkLEVXxo4ibUeDFkJUWRMrCo7aLBCgw2ND8IbBUlFgiiKjUJdpRJER3uiyoxYcsZYaQGJrfXNwq7817UhJ13m8Wm1hxYC0hheakU2Vd5sjtsZJCpdOGWxRRd4Ot12Nbrsahp9VE5hRFJ2049JsjstWnRwJXRzjjBuatvpvcNpInJF0nCqxMdE6xlZ6Wr8UDWWJCs0Y1b/jDJHksuvRDbx9lb83sSizt2dzRijTTNGtN7pa5iURTIvY0bFnQJssXHkSbspMRqpyjbioyJYSPp/3qQ+c79XDZmhxTceMDoBHnHSNjJa6pGtySRkjuZw0ybo7g9pEHlV25JRue0aDJlkHK4p9SezsoNmHC1JJEbyy3QhUFOl6mzNG1EWjK1XuYWa9HWIbaNmAeBdeloG+pEqrkxmrEOVgvYasbBWFvrGVgRT6Z16wJ4OMKihKNE0+403hNcF5OMr80WG2z4nhzr5ac7ATCcIumAxbV2TZo8gmc5tqqBbGPjSgHdtA8+LgPKTQbwE1Fj8O3marOxCq8naDBL7pumVsZHmZmlITGZtZ6lJLXVLuiSH0aVipZ20qIaqNrcLYFyl0MBz+MA8pnDZTRzmgXry9hxD1cDNJDC/ICDuZInKKRblvaAZ1lWRy7KEcqRC2XjA2VZIqrU5B7IsUukAqlxWp5LLD/PkJm4PW8xo1UQnZxAQYAt9E/Ql6AlLJThuPdD3d1LRlg7VTGdLNmnZKVLHnmVXorlWzDSKSIxsTXNedawxDDSDuVnrMxlZ8e8hTolklEbwWMqLVRkZLXUO8kaii2AdYNI3RkIcUFEWUg2ufxAmHsDeCaXC3UQeC/Ip7T1Kx8MIBlvwpNarDXs1drVfzsU8UOlPmhZTDkl5CSpEHXA6DGJWCxvkWSKV4mlsi1WeZ/CWVX3EbKdVzBZ+5jK4fSs1eqOItCbF2qJVXq4mohSgsIKNMDyugKDwaPCBxcwaofzIl1g4Hoi605qAARLYbUSmsnisaBgod7XdGqQIkSp+9Qs9DymU/ZqwQP0uv7tbmkxlZYmsnV/uWT0C092COTeHQOvqlZMudXvKL3XHUFu4Z84X/UiESlXRHQkom2TYKy9vTPjxuB2RtrBicHlYVtT6RTtPL3wkkAjpNHmF5jZ+XXxJDTWGJ3VA9hzWM+/lwiYQeG15x0+fZdthvxx7nwfvG+zwPFzzKKwfccgSXSEh1U+XKT40NzfxTEOE1h5nU44NaRSOCfbpe2FccwsaS4vDvQVkgv/KNnyQoKsdbaHRFvB4fzF3GkrK933yE32Jqjb59qfcHUySwQEO3s8kLlsYSrFG76Hmw2BpeiLL4+5iyQkrAMTuRl5vSRVs1QV4nhydo3zXpnwzPWMWYTEJCjMvTmLx0qVUrsPWaNfbLQvnFDbPasUI/VvXUucXdPvd86wFMKMV6eSffeGqPS8V6CjgVTqgTl7t98H9hwQUAAu/PKTzP8decuLD7AiZA68ql3T6Ym4/VubFGCCeHFbaeFVIykpGzmqUOu8g4xNiOhMjBJSfdAB0QTK9lTxJhcqCkwNmaSyVCBLCacJ62qa1rsNu1X78ei23M7zrfmmnd8lu3Fz4/zEjGZFHABa+AA9P0uAWsU4VXwGHlfkwM1CWH8p+7/+w+HIB36WB9W7z0pvA8WJMNU752O2R3sNqVP35V4XmwSCkOAN1iiRHcQvd8qO5Uec5b1h6Yx5OkTTP13uQkiwBOjg/AEg/hRVzp8AKoCP2sx9KrosZatViN0RqOHJpGtVufA7B0OdBdDtq1x+XNvWkOgNAQ1htYugIM31tZuHXe3MEaDbKoX3dg9fPuLoTm675xkyy81h1YuPF8yJOkZ3Yiajpp88le5ISOmlfAq339IzvwnAntfYAsr8acs9fH4IMCC59P/8JnehymKwr1CSx8zn/qwh6HYT0jPaw3sERCfeeRHuc555Hz9LAewIrwFPnMiyqnQjDZz2ofchXdPV1HFmNtlXw+gtrtHuFSk0pZ+6UTxnYKh+u3FljXp8SwVMCC4l4SYPGNvb2BpZWSYbyrI83YQzlpmGt0B5M2LB5Wfbo+SCu2QW+iD2kIj+VU30zm1FhKYz2s0ZEAVroaKshJMsGcTuiTmVIkNYAknGFIlip1OJuqC86KHNTeTNY7oxkFYHVrhN7MhKaWdGpKVPygDloodPrMacXiwahDqwZ6FwevU8xkfTouIwIsC6ZuzITgoIl5/0jSzGD1wo7rFqerwbZw26BcZMJ9xe9ExLs01uxEM4szZbKSsSpuRbE4g0ZzodHQdCInkn5hJBJldgK9l6Jt9Y7rd0sJ9f4hDiS6z9R1+u+iLrN0SuUz3PymRQq0R4WxErdQKpoFjbN4j03SZmzWnAKBVO1MdntGqzcxtEVU/XxSqGGJxQkHtW2g4lELhT2kCBoTTSqJ+XxrK57FQsGU/NUottBR5CZbaV8B5a6T7rIo7FwMTcVmvzHUAAsLMXKZ68JN37TbJ7DwwhIuZly46fsmFgUWFi069srjepzngJMP1PfRbRWwNKIJjNZkItoiQQNxc07iBmCEFpb+PCSTDRMp9iDm0Ci4T9yttEunuDmgRquLrXQIYOFNNXzHX48NK/1j0bNFgYWhmF0O3HXRsylSewALqOKLx3tsWBoe7xzoB1hoCvFuurSVuAkKnTUT0jlbAh0pzBrxl5OjFwrVNWroTxD+KdXJcgyLmu94+/ceh+2JHw4+41AM82GZ/8IPUIVjsIa2fTV8fsObKTt7bQcViPfkdDsVRqBRWlNJX5sD67ybpY7rwQ9Y8x1ru3c71QnXngSOx/qRXEF+UWChWdA4aCLJ/hpC7cO8lC3uFpyMDE7ueSbtMW3Ht3rny5fQosoF1QH6ZrbeH8gmHMlXw3c7Gwaz8crd3qcCG+FV4cBft7PhPPgtFu/DAt2Llt/ghU29r2pbOy22qpcozRoejUSMmQnpvQgaeHDrVutakj2GDvKMreurKGlrzxPebgQUvo6Gw0sA9ZWn3T54wAheOBK1LovkpLXqGXd9UN9B3+2D950cetZhOLjb2TDmjX8PPPWgp159pjewzn7oXFRALHpVhdKCcEHricAIZN/Q5Xe6yoycwF2lwOrn6rfqI1pe3mPTrCT94mz3d+6BFfq7fbD+J47Z59j9EJj6ORuqErqd6mMvXqrvfn7XeYdf9pXiwLr5hlN0rdGFPTZd/IVLup3qkDMlXKISFSU6/XQgeQXLfKvfbtz/pzO5Wn0sEKx+WsK0+kEHAuXqBz0J+Yh+0KXCB9HTf4ST/UdaMP0sofbEU0QKhtfNL8nZIMzBanzZSR5Y8kaTO85c9Dyolka5Hwps+qEoNottK9uGtm3Z4BIW/IOQ+OAfkDws/+Aq5fb6NrwZUKPbkgDrtNtO10DWG1jyyrgHzulxHrzWUN9D3hewym0Vbpd++XJ90ksCLKSffQIL2q7HeZAB6GElsAY1FA4DsEYwFC6beG/1L95HAFijJ96X2G5YBs9rCEPhKNgNixukyQq7w6OssQbYIF2Cm+82pLNE4X8IgDWKQzp9UvdyDEKPGrBGdBD6jS+b6XPT109aYGHG8+HnH6kfHaLuf+NkskWBBZuqx3lYzLNVoXCEymZWsNCvXxU42bjma9dZYG3YeSMH7B7/3tNbdTbM1sfbeBcF1lOvPgsLFIOPmNqf/wDZ+NWFz1687RpraAv9lqU0eaYuVd5Lt3Y0Shsu/+qVSwIscVjWtG/85i09gCVT7A/eTabY/8SnikskfvAiihPRGng/+eLA0tLktMhdp0ss0izDUZq8jJMp1jWWUJziMfM9lNsIrEo6Qb7boiAYn5ZFQWpVlH/1KG0AnlCqCmydcvNpi4a/UZxMsYLTv173hmU/WHCHKLkcbXLRC5csWiUGYkNpYf+nHa3pX/3AbpkmrK7abd2O65kc9P6gmKcfBBSCbMgnrBYjaQmn2I+2VTPSU+xXalGQkQDWKC8Ksi3LGFly2tpljEZqK1zGqAeNVYZhGaPayiy8Ngp0tbwLr63+sLgiS0UO/TbqS0X21p4F5LREi9uOgnLfpsVtF6OxwdvemOW4RwZfo70c94q8QGCIwVS+QMBt5StPlpTyy1eeBKW5dC9pUvIvX9K0jC9pGpx8qHyt3BJaVpafRv21cr2E/Na/CLNZvggzuv2RfRHmoqq8fHVvn1v56t4ufa582fg29EbGrPJl45ktImQVUnlbz2lP3nxOoQNGJH9LVBjhEUprjw8fqgQxC23cYNAGnrqiyGh1vQ6jSfaTt5oRMXKpOh7QoLaOFVL5NFgaJdufcKtWodvYh3axKgF/qN10KFU8bkqbBbfPjkRxmY+MYCbxa8wobWExSCS/IvNikJw9dDvJ5nIqWygalkSnbZEXHWZ5Xt5b7FsJQ4dKYGjlUFE0PK91DE2kthP+HTO3SSsHjROxtX3BvY7k4OA8vBBbZeh66aYRrGQzZSFVt6pL4mOWzERVMPHJEpUorRSOwCuzaDFm5ltDoLdwC2r7UXeqv4Bbtuwi1OXjWhQZlZYyeZJKi7wvPTxdMa1Ti2ZJRArdxj5BD1rEo0da2bO99fRwjBLeMAArSdTek3KDxCSG3skL95i4eofCyBjp+pCDD8nK29bZW9uIEpO8xszkOFmiEv2e9lH0Xc6Kxp/rY8C/yl7YM6DwCleehJvivUiakrYMmsXyk0gxYzpEkTHS9WSvsaF56VdkN0j3yt5bavq1ColKI51LiNCs3vcSCZISfhiTxiNJF1cZPJLHklfp2jsuuKejzk5E+hAGNDDAMVY6Ip+dsFZ7VFaZFPXqIbFpeOdQTpFdHsW+NMfOOKLaxAoga+2o4yUJs0NVRTv6+PRAVgKOp0kuqRe349TVQtuadg0/pCN8VrV+adWOFcaRMaV8jqHhcQyPuipc2CPSAZF9Rf6PRBWQZ/R7xXZxNpnONB8IR4YR34nFQMAVKnfb2SitxGppZAwtS135yFjxC4oMc7GkzfscUVnYQZmSxjoZu4XemK2m1QeQdndH/mhWnHOVEz4uDxeJS3XCwHePxlxQ7sSK9dzRIFQX1ojJU1c+ZxzaLepYed62YsIGApqiEZkrqqQjpieB5nBD1NrLk8oqHKuWS0ocDjIXP+YuXsg4JyQsbYsYsI7D+Fj/LTyEWxT7hMyz8ZE2VSwjfFxwZjQbfa0TIjxSD5N+bLC4qtx5XeGCAlwNz4ZHEm5EKcdSMvl7wpAZk2Ll9WitGDRs1NRDOfblNluDlXFltJ+Z5EU4ybagHSuMunJnkmyvEpirLKMf67ptzD1XMmFMKrxOlQGOjBP3lWmHsJGKSEPMZOtm1rHLuFn5Jp1r2BkrwyutavKki8YixuwQqe18EhnnC9JsjmyQ8/CDjmOMecmiYUUfpwgaWbVmxRpXZodvcrIP96shzHWAmkMSRyAQx/P0TKdU3Czj14ju7Bi1Go2MaVVIZ3LY3NE4GjZrUVbCcft8u1TrITEUjz7XTQnBsVSw24FVPYadXqOJPjl1Dt+A5EjHrxRMtRT3BIrSLQlY9LiW8/vHL+5djqQDtrKF/9JQC6E8xLaVXsZovc4kXe2paVMY+7DlSXQKOIxBgS0umnc+o3k1CJIhklomJlrhtRwvuxMbPb0R1eaMcbp2qEJHGZSEGvSiGfQkK1uqJuAkXzZwSQetmzbwjegcTLGjNmU8PSuAQvNlUWUHzmyUUV/ej4q4IMg/VKnhyCytuZCxfcNk+gy2aXH5pMKTWGZy/1cSaIlXrvfIgChQm/djCdnsJCJsW4+UIaekkime2dQeuWUvxupVmw9jZlKhUx8tVkG0pS+ay4ZFI+G1o1vNbic56VMhB+irDBxA04mQVGxhUDKJi13zx+gSOmSmANyUHXkXyqyRimcOK5fayYY/mFueh2IryyY6GWmRWZdlpKbKBfRE9rGM93WpRGM3tcFChYvtuIQsG1QfTHi0KZmpJ+kyf/9cBRw+dcJhBJy8YoQ/b0xfwZI7RtZc9KBUZLtQWxuzKNe7kI7hs5ZomUZSMu+dgT7GlpEKdryrYuoEB3tRhtcdO6JCDpa25QUEO6iVtyqkMrEjcZTATqzIYzBS0UP0KLj1ocrZTAhLJ382STMBTOl7bEII9u+f0ZCqRqXildegJovrPwjE6RXy7lwHQ2/xROhMmc5kGHH3qUlE4ZI8dooVqlaUlOsShHFlbVNrnJKWZL/nISoSUei+QfXpUrMTZ3wYqsaC2JI83GFOI4j+SmmGyEjX6m0yeJGZFBNKfgp9xyg4c0fGaqy0IkyVtHDxQVqxCjnrkdpRKUbJiMilq5icerWvn/bGm9F2MoUNjlVD+KFBk+C1Up9yzWbCSLFCulJO4vNWr4jCSAsHHOU0Mua4JTn9fzPZZSMQXnS2wFKaAI6PMXuwcY3akTeuF6AFjGSd0MG2y9C5tF4nLPOXkRajvKkXENrFDL/Y7mjTHzN8NkFxpuTEmEUmCLmhWkd0ktIEiihUFzHDXumvdOXPCFjOmkq5h5mHGhyWlihxlEpJgYqkwKxphI0CYphBWeSRxt3PYEvKrWolb2UbOiqPJKps4kMVb2v6CLUoNyRdsd6QAykKOw2jyjeMd057pVyoaxQykGkwVZ2nnMdrcwM1KS2p6dDycdaJ9I5fn9eTlskHx+I79aZJpKts3YfFFs7gOtLUeIkog63s+qopqtr56W9BanSCimcQ5FMhC7KVNWyF7CwNK/yqyCY3KC0pBDXNjIGVwlfREKUCJEiFBWOWfmV4daaAYRf2DcKFQz2CIUPkwVXOErwszzdVL7HUy+UisUuyw/kUXtXaTqxVTVFkcSVy2JMkhbLGF0HUrSTKhEUf71wiWSSeHC1lwx+PVMqM7FAqPMTuyBFg7LaaPZgspoKD49YjuKjTtqWHm9q9W7OwXImwoJXF7usej6/1BtqUz9jd/ejKGEUVfYTILs+Y+P5txXZoOVisyqP8T30sNrlFPQrfoYxxdiJvvtvQb/tYiq1SVPURE0N7meBIAFmDlFUA1OwMZBlpnK2ecyO+nUy2SOWugKB1rkwTAYvMp0/XATRxOAvZXyczLu5qEufj9MIWtNDJY7ZLYyUyPG0PHNBK/5XQ8lllaocpxqyV5Xtt6PT04n1bM3XnCdXcb+TH7IrMcQU6n6jyRxhXLrL1CTuNm9SI+SvhgDqlGFV81sQaM9nxeJTflLpq6zyITPMVjbkyRmQyKbqmYxkjmwyUD0n6+KMCG1KU+5oFVvAs5hr5YhhCNgrBZDimAiSt5nxBhmv5iURucRk1VLltHcJI+DZkVJthkIePLV/D5MKcSceoY6jeIrtLc67I7iLs9KlHBhWBEhS6V0KR/iNFMcrna8uItsgIpSdsx7XK7XVupCU7dGODYCY91OfRMekh/S3/JPTxhNqH8XgY2OWPnho1bkbAYnRTAd7ysonkSty4WbW+S/DNHcEZwWV34qw2JIP2rqsBcCM5urzElOXtStN3WcptNTu7Pl1HRj0+V1IIKY0kR2MiGj5ylS1rMoGy4RUYR8edDZskEf3wqogPjjGHIc6Zgnk4TIEbJgVWlVaOLi+lsxWcd1OKFIQ8x4XsA5uOS1DCqJznp0Y2U8sTmIq2CFjNHhSlxXoeDTyAUOP1h1VlqgkvPtRu1McKy4dK12p5+Mvoj0KzJ4ys+UGb4En62iy7J3Ip+dT5gF3pwUzdMhBVFENb8BTWNuwBvIwgEDMXFo8MZibizk7kA2K5LcvG6jarSEI9qqGrvNnIOJgpjd+uYOQnDW2JtUAjYLHAoeJjnxmrieU5A3eY/JgzdQNpRX6KU42TozUbYmXCYk3Wt7DjsoXLXXAEN8w6ZHFme7xbZORYUFBpWdwQecFB2NS2YC2IfR7ihC97gn0rBNFmk195Iej6ZpkDrsBWNWWANHWCI7+xlYda0NHkkqxHz79KfKSjI0o/iSeJ/2Qs5kUypU01eDHBYTekVS35acW3sN6roStymJ14mIcaVzPjnqjW2cTKrpHO1Rr4sUU5rTdpC+iT0wb9WILlp7De08ZW+WRXWs77FSgztfC5FcbGc7MReViQXDmTKcS1qfEIENHyQxY0FFUFLuh0PbJC7GE6Uzy/MF25rWS2mM8QbRFECD2cD+MFlhlVjI2AMPKYKyOmmqbqYlAjiPlXBGhGVHn82UxwBF9BNTCGKk15PmmSU8Z68M81ACKn7oPuzgMrlwEwXBKgFtl0SchPPGzAV8YeJWxJMmXqThmJCp903gWgdu4FNcLIs1re17AyKyzDZOx1eb1gu3x57OAK/Pm4eDxw2IKR2H4UhXVzHGzJAytoNW+Rs9KmUgmsSX6yE27LJzIslkS9CojI4g7VuA7CElsPDqOxTmu+Nz+xLD3ULFRlHhEuo1qOyQzxFoaA7ICdH5sL+PDxKw8sOzbMP8wPU5ZlCCO3IbdHeApS2iduNjjSD2MOyDzR+k8hXfApJ05rT15uI21Y6CLeduENXYavbV6Hkc7MbkeLA+JPdGHt0iwot3Jb5m1qaqpshHJb2m1mZqayefPmsiHKbWm3008/vfLSSy/Nzs6WbVFuS7XNzc298sorlddee+3ll1/esmXL9PR02Sjlti0bIASuAqoAqv8Hq3jne/3ojC4AAAAASUVORK5CYII=",
+ "description": "Preconfigured gauge to display any value reading as a doughnut. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 3,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#388e3c\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Simple neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "vertical_bar_justgage",
+ "name": "Vertical bar",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABQVBMVEVqampra2tsbGxtbW1ubm5vb29wcHBycnJzc3N0dHR2dnZ3d3d5eXl6enp7e3t8fHx+fn6BgYGDg4OFhYWHh4eIiIiKioqNjY2Ojo6RkZGTk5OWlpaZmZmbm5uenp6fn5+goKCkpKSlpaWmpqanp6eoqKiqqqqrq6usrKytra2xsbGysrK0tLS1tbW2tra4uLi6urq7u7u9vb2+vr7AwMDBwcHExMTFxcXHx8fIyMjJycnKysrMzMzNzc3Ozs7Pz8/Q0NDR0dHS0tLT09PU1NTV1dXX19fY2NjZ2dnb29vc3Nzd3d3f39/g4ODh4eHj4+Pk5OTl5eXm5ubn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT1fAD19fX29vb39/f6+vr7+/v8/Pz9/f3+/v7///8uFkw3AAAAAWJLR0RqJWKVDgAAAkBJREFUeNrt3ElTE3EQhvGebKMIiUExGkSNuCEiKAqKRKJsIuISN0giwQFC5v3+H8BD5uDBE2WV9tTTp1x/lTyd/6lNKRkDAgQIECBA/jtIHEXx4FOv3fMMWTSLJOn9eN4+OoZ0zg4gjZxduvXDMWQim7FIaoe5hutGXtn90CJp2mZcx340cv44tEgas9kLufKyW8ht21BokVSwTPWKBdtOIdvBDSm0SH2zFWnaxn1CeqPB03o9b88+9C2IpV0r+YTsWDJ3NWQdqWNDPiGH9Xp98I3ous1Ji1b1/NYKLZI+FzKTd3LBG/cQbZXNii9T8frttGKe8UCAAJGaS5J6j2/O96TuzOTz2CfkxZhVJF0tTY1MqF++eK/w0Cdkar5akdrBa60EnTXb04NzXn9a1yrSln1V094+yUvLduQYsmp7+m5rs2ekhrUdQ9ZtV99sYy6UGrbvGPLJdvTOmkvBsRay8T+CtE45v0NOirVurdRvZR/tX67KMUSbwza8KS2ENvrFKySZA0lSfMgTBUjqIH+pESBAiB0IjQBhawEBQuxAaAQIWwsIEGIHQiNA2FpAgBA7EBoBwtYCAoTYgdAIELYWECDEDoRGgKQK8vOUAwQIsQOhESBsLWIHQuxAaAQIW4vYgRA7EBoBwtYidiDEDoRGgLC1iB0IsQOhESBsrXTHnlws9g8ZXCz230hysdg/JLlY7B+SXCz230hysdg/JLlY7B9yUqx1a8V+CtZvcrE4Df/sBzxRgAABAgQIECB/mF+DKtRemhqPQgAAAABJRU5ErkJggg==",
+ "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 2,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":12,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":1.5,\"gaugeColor\":\"#eeeeee\",\"showTitle\":false,\"gaugeType\":\"verticalBar\"},\"title\":\"Vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "simple_gauge_justgage",
+ "name": "Simple gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as a circle. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 2,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "\nself.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#ef6c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"gaugeColor\":\"#eeeeee\",\"gaugeType\":\"donut\"},\"title\":\"Simple gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "digital_bar",
+ "name": "Digital horizontal bar",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as a horizontal bar. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 6,
+ "sizeY": 2.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 80) {\\n\\tvalue = 80;\\n} else if (value > 160) {\\n\\tvalue = 160;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"horizontalBar\",\"showTitle\":false,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "mini_gauge_justgage",
+ "name": "Mini gauge",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as a circle. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 2,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#7cb342\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"roundedLineCap\":true,\"gaugeType\":\"donut\"},\"title\":\"Mini gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "horizontal_bar_justgage",
+ "name": "Horizontal bar",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as a horizontal bar. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"horizontalBar\"},\"title\":\"Horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ }
+ },
+ {
+ "alias": "digital_vertical_bar",
+ "name": "Digital vertical bar",
+ "image": "data:image/png;base64,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",
+ "description": "Preconfigured gauge to display any value reading as a vertical bar. Allows to configure value range, gradient colors and other settings.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 2.5,
+ "sizeY": 4.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-digital-gauge-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#3d5afe\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":14},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":8,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#cccccc\"},\"neonGlowBrightness\":20,\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"verticalBar\",\"showTitle\":false,\"minValue\":-60,\"dashThickness\":1.2,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/edge_widgets.json b/application/src/main/data/json/system/widget_bundles/edge_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..5d9c9526fa9dea9d541007fbb6d7a0a018dabba0
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/edge_widgets.json
@@ -0,0 +1,29 @@
+{
+ "widgetsBundle": {
+ "alias": "edge_widgets",
+ "title": "Edge widgets",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYcAAAFCCAYAAAAaOxF5AAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AACAASURBVHic7N15XE35/8Dx161IK0pCshTSRlEihSyDbGHsmrHWYAZfxjZjmRhjZ/piphlm7NNXGNvXWIYaY+qLmpHsMSNLpqhsFZW6vz96dH7u3EqSMPN+Ph738bj3fD7ncz7ndjvv8/l8zjkfVXJyslqtVqNWqwHIy8sD4OllBcuf/iyEEOLNoVKplPc6OjqoVCplmY6OjpKn4KWnVquxtLR8JZUVQgjxeklOTgZAJzc39xVXRQghxOsiNzcXtVqNXk5OzquuixBCiNdEQUyQ4CCEEEKRk5ODjo6OBAchhBD/LycnB11dXXSePHnyqusihBDiNfHkyRPy8vJkQFoIIcT/y83NzQ8OBfc1CCGEEAX3tOnIjW1CCCEKFMQECQ5CCCEUBU/H0HnVFRFCCPH6keAghBBCg7QchBBCFEqCgxBCCC0SHIQQQmiR4CCEEEKLBAchhBBa9F51BdLS0li3bh1Hjhzhxo0bAFhbW9OhQweGDRuGubn5K66hEEL887zS4HDkyBEmT55MnTp16NevH40aNUKtVnP58mV27tzJxo0bWbJkCW+99darrKYQQvzjvNJupePHjzNjxgxCQ0PR0dEhLCyM7du3o6OjQ2hoKDNnzuT48ePPLGfs2LHY2NgU+goJCSlyvUOHDmFjY8PNmzfLcreKFRISwvjx4wE4evQoHTt21Mpz/fp1li5dSocOHUpc7vHjx/H396dp06Y0b96c0aNHc+bMmeeuX2ZmJt7e3uzYsaPE6/zrX/+iV69ez72t5ORkZsyYQatWrXB0dMTPz4/du3c/dzkvS0BAADNmzHjV1RDilXilLYePP/6YtLQ0unbtSoUKFfDw8ECtVrNx40bWrl3Lrl276NevX4nKsrKyIigoSGu5ra1tWVf7hZw+fRp3d3flvYuLi5J27tw5Fi5cSFRUFDo6Oujr65eozIMHD/LBBx/g7u7OjBkzyMnJYfv27QwYMID169fTokWLEtdPX18fLy8v6tWr91z79bySk5N5++23AZTuw8jISCZNmkRCQgITJkx4qdsvCTc3NypXrvyqqyEEkH9iuWvXLuVzz549GTt2LADp6enK/1OBsLAwTE1NS729Vz7mYGZmRlBQEF5eXlSoUAHIn2zi2LFjzzXeYGRkRPv27V9WNctMbGwsI0eOBPKDQ7t27ZS0s2fPUqVKFdatW8evv/7KN99888zyMjIymDVrFm3atGHNmjWoVCoABgwYQN++fZkxYwaHDx9Wlj+Lrq4uCxYseP4de04LFy4kPT2dQ4cOYWFhAcDbb7+NtbU1q1atwtfXl4YNG76Ubefm5qKrq/vMfAEBAS9l+0KUxp07d4iPj8fOzg4dHR10dDQ7fgp+08nJyaSlpfGic/W8Flcr+fj4KIEBoEKFCi/lQP/f//4XHx8f7O3t6d+/PxcvXtTKc+bMGfr06YO9vT3t27fnhx9+wNHRka1btyp50tLSmDRpEi4uLri4uDB9+nQyMjKK3O6pU6eUbq7k5GT69++PjY0NERERzJkzRzmzHzBgACtXrqRNmzZaf/iiHD16lJSUFMaPH68RACpWrMh7773H1atXiY2NBeD8+fPY2NgQGRmpUYa9vb3S/ZaVlYWNjQ0bN25U0h8/fsy8efPw8PCgSZMm+Pv7c/78+SLrFBUVhZ2dHUuWLCk0PSMjgwMHDjBkyBAlMBQYNWoU+vr6SveSr68v//rXvzTybNy4kcaNG/Pw4UMg/2AfHByMp6cnDg4OvPPOOyQkJCj5x48fT58+fZg2bRoODg5s3LiRuXPn0rp1a55+8OSlS5ewsbHh6NGjAPTp00c5Myuwb98+unbtir29Pb6+vvz0009Afndc48aNCQ0NVfKePHkSGxsbfvvtN2XZpk2baNy4MZmZmUV+f0IUZ8uWLezbt4/33ntPWWZsbMy+ffvYt29fqbp4C/NaBIcCBQfQ0kS8vLw8MjIyNF6PHz9W0mNjY5k4cSJ169ZlyZIldOzYkTVr1miUkZ6ezrBhw7h37x5z585l7NixrFixgkePHil5srOzGTJkCLGxscyfP5+ZM2dy5MgRpk2bVmTdHB0dCQ8PZ9GiRdSvX5/w8HDCwsLQ0dHh4MGDfP/998+9vwXi4uKoWLEiTk5OWmnNmjUD8rurXsQHH3zAzp07GT9+PMHBwahUKoYOHUpSUpJW3osXLzJmzBi6du3Khx9+WGh5Fy9eJCsri+bNm2ulmZqaYmtrq4yX9OjRg4iICJ6ezvbw4cN4e3tjYmICwKxZs1izZg2BgYEEBwdz//59/P39Nf5usbGxpKSksHDhQtq0aUP37t35888/NcZljhw5QpUqVfD09Cy03nv27GH8+PG0bt2aL774Ajs7OwIDA7lw4QKGhoa4urpy8uRJJX9B4AgPD1eWRUdH07x5cwwNDQvdhhCvi1ferVRWrly5grOzs8YyDw8P5UwuJCQEKysr1q5di55e/m4bGRkxa9YsJX9YWBj37t1jx44dSp97s2bNNAaNd+zYweXLl/nhhx9o1KgRAHp6ekyePJnExESsrKy06laxYkXq1avHwYMHcXJyol69ely7do369eu/cNdJWloaFhYWhbY0qlevjkqlIjU1tdTlx8XFceTIEb788ks6d+4MQKtWrWjdujX79u1TusgAbt26xYgRI3BwcGDx4sVFdmWlpaUBYGlpWWi6paUlt27dAqBbt24sWbKE48eP4+3tzcOHDzlx4gSLFi0C4I8//uA///kP8+bNY8iQIQA4ODjQtm1b9u/fT58+fYD8Mamvv/5aaXqr1WqsrKw4ePAgTZo0AeDHH3/krbfe0mjFFlCr1SxcuBBfX19mzpwJQNu2bYmLi2P9+vUsWrQIb29vNm/erKwTERFBkyZNCA8PVwJldHQ077777jO/dyH+qn///nh4eGBsbFwm+Z7lbxMcrK2tWbZsmcaygjNLyO/P79KlixIYAK0ujbNnz2JnZ6cxGFutWjWNPJGRkTRo0IC6deuSlZUF5AcQtVrNmTNnCg0OBc6fP4+9vT0AFy5cUN6/iOLm41CpVCUeayjKyZMnUalU+Pj4KMsqVarEnj17NAbMHz9+TGBgILq6uoSEhFCxYsVn1rmouj9d5zp16tCkSRMOHTqEt7c3P/30Ezo6OsqVXFFRUQB06NBB+XtUq1YNa2tr4uLilOBgZmamMc6gUqnw9fXl0KFDTJkyhdu3bxMXF8ekSZMKrdOVK1dISkrirbfeUrYD+YPWcXFxAHh7e7N06VKuXbtGhQoVuHz5Mtu2baNv377cunWLnJwckpOT8fLyKvK7EaIodnZ22NnZlVm+ZymX4JCbm1vsQeyv8vLySjxoWMDAwAA3N7ci01NSUrQO9H+Vlpb2zDypqanEx8cXemC/d+9eoev07t2bixcvkpOTw/79+wkODla6zuzt7Vm1atVzXbb6NDMzM27fvp0/5+tfWg9JSUnk5eVhZmZWqrIhf59MTEy0DvZ/DYKXL19WAu/169e1WnFPK7jQIDk5GUdHR6305ORkjTp369aNtWvXMnfuXA4fPkzbtm2VwF/QKiqsK+j+/fvF7lv37t1Zs2YNV65c4eTJk1StWpVWrVoVmrdgO4VdRVXQAnJ0dKRq1aqcPHmS7OxsmjRpgqurKw0bNiQiIgJ9fX3Mzc0L3WchXjcvNThERkYyZcqUQvumi9O4cWMAatSowZIlS2jduvUL18XCwkIZwCxK9erVCx2kfpqRkRFOTk6FXjZbp06dQtdZuXIl6enpdOvWjS1btmBmZsaoUaMICAigRYsW1KhRo+Q78hdOTk7k5OQQFxencVksoPSnF3SbFJyRP0+gNjU1JT09nZycnEK7WwoYGxuzZcsWpk+fzr/+9S/27t2LgYFBoXnt7OzQ19fn119/1brwICMjgz/++AN/f39lWbdu3Vi4cCEnTpzg6NGjzJ07V0kzNDRET0+P7777TutkomrVqsXum7OzM3Xr1uXQoUPExMTQuXNnjZbl04yMjACYN28eDg4OGmkF34uOjg6enp6cOHGCe/fuKa2t9u3bExERgbm5OV5eXi/cmhP/TBcuXCAhIYEOHToU2zIvab5neakD0tOmTXvuwPC0pKSkYgd6n4ezszO//PKLxrL09HStPBcuXNC4Ke727dsaedzc3EhISKB27dq4uroqL2tr6yIvva1duzYqlQpDQ0Pc3d2pV68eSUlJeHp6Ymtrqxx4SsPHxwczMzNWrVqlcdDPycnhyy+/pEGDBkpwKOhGe/pvkpqaWuwFAM2bNycvL4+ff/5ZWZabm0uvXr00LrWtX78+zs7OfP7559y6davYy2GNjY3p0qULoaGhpKSkaKStX7+ezMxMevbsqSyrVasWrq6ufPbZZ2RnZ2u0stzd3Xny5AkPHz7U+nuU5F4NX19f9uzZw//+9z+6detWZD47OztMTU25ceOGxnZsbW2VsSfI71qKiooiKipKCQ4dOnRQlkmXkiit7du3M27cOK3jVmnzPctLDQ4Fg4qQfw1uwZmdSqVCV1dXOYN6Oq24MoqTkZHB0aNHtV5//PEHAGPGjOHcuXMEBQVx8eJFDh8+zOLFizXK6Nu3L1WrVmXYsGFs376d7du3a13rPnjwYKpUqcLQoUOVg8rcuXNp3759sQO/V65cUW7Iu379OpA/TvKijI2N+fTTTzl69Cjvvvsu27dvJzQ0lEGDBnH58mU+++wz5XuuVq0ajRs3ZuXKlezZs4cdO3bg7+9Pbm5ukeU3b94cLy8vpk+fTlhYGL/88gsffPABCQkJygD10xo2bMhHH33E5s2biYiIKLLcGTNmYGhoSO/evfnmm2/YvXs3M2bMYMWKFQQGBmr1mXbr1o2zZ8/Srl07jWDq4uLCW2+9xaRJk1i3bh3Hjx9n3bp1+Pj4KJekFqd79+7Ex8djYmKCh4dHkfkqVqzIhAkT+Oabb/jss8+Iiopi7969+Pn58fnnnyv5vLy8SEpKwtTUVOk+cnV1xcDAgMTERLy9vZ9ZJyFeB+U2IH358mUg/+Dx3nvvMXnyZJYtW0ZISIiSZmNjU+ryExMTGT58uNbyUaNG8dFHHylntUuXLmXbtm3Y29szcuRIjTNcQ0ND1q9fz6xZs5g5cyY1a9ZkxIgRzJkzRxl8NTU1ZevWrSxYsIDZs2eTk5ODs7Mz69atK/amvStXrihXJl25cgUbG5sS38vwLF26dGHjxo2sXr2aefPmoVKpyMrKom7duloBaMWKFcycOZPp06djbm7OiBEjWLVqVbHlf/nllyxatIjFixeTmZlJs2bNCA0NpXbt2oXmHzp0KEePHmXatGns37+/0O+levXq7NixgxUrVvD111/z8OFDbG1tWbhwodadnpB/hj9//nx8fX210v7973+zfPlyvv76a+7evUvdunWZPXs2bdu2LXa/IH/Mx9bWlpYtWz5zjGv48OEYGxuzZs0aNmzYQLVq1fDz81MehwL5rRxbW1uaNWumcfLTrl07zp8/T/Xq1Z9ZJyGKM2DAAHR1dYu8Q/rOnTtlsh1VdHS0uriB3Bfx9MG+4Ay+NMGhYN3y8ODBA41bzqOiohg6dCi7d+8udpD1dRMXF0dgYCAA33zzjVY/uRDizfL1119rPHusR48eyo1w6enpDBgwQCN/aGhoqR6fERMTg7GxcfkFhxdRXsEhOTmZzp07M3z4cNzd3bl16xbBwcFYWVkRGhr6xg0kJicns3jxYj7++OMXumJJCPHPURAcXmq3kpWVFYmJiS9cRnmxtLQkODiY1atX89VXX1GlShXatWvH1KlT37jAAPn789d7P4QQoiReanBYtGgR06ZNK3WAsLKyUu6ELS9t27YtUV+1EEL8nb3U4ODp6cmxY8de5iaEEEK8BK/Vg/eEEEK8HiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCy0sNDklJSUydOpVz586VWZm//vorq1atKrPynkdMTAzz588vl23l5eVx6NAhFixYwKxZs1i1ahXx8fHlsu3Xycv4DQkhnu2lBofY2Fh0dHSIjY0tszLr1KlDq1atyqy8snL69GmCgoLKrLytW7dy6tQpevXqxdixY3F0dOTbb78lISGhTMpfunQpUVFRZVLWy/QyfkNCiGd7qcHh9OnTeHl5ceHCBbKzs8ukTAsLC5o3b14mZb2ubty4walTpxgyZAgODg7UrFkTHx8fmjRpQnh4+KuuXplTq9VFpr2M35AQ4tn0XlbB169f58GDB3Tq1Im4uDjOnTuHq6urkp6ZmcnOnTuJj49HT08PV1dXfH190dHRKTbtl19+4eTJk0yaNAmAe/fusXXrVq5du0a1atVwcnLixIkTzJo1iwsXLhAWFkbXrl358ccfyc7OxtXVlV69eqFSqQCIj49n3759pKWlUatWLXr37k2NGjWUssPCwrh27Ro1atSgbt26he7rhg0blG6PqVOnMnz4cOzt7blz5w67d+8mISEBAwMDWrduTbt27QBITExk3bp1jBs3jqpVq2qUd+7cOWrWrEnt2rU1lrds2ZKLFy8qecLCwjRaK1988QUNGzakU6dOqNVqDh06RHR0NDk5Odja2uLn50d6ejqff/45ALt27SImJobx48eTlZXF3r17OXPmDABOTk707NkTfX19srKymDVrFt27d+fEiRPcvXsXGxsb+vfvz8GDBzl9+jQGBgZ06tSJFi1aKPU5duwYP//8Mzk5OdjZ2dGrVy8MDQ25cOEC3333HS1atCAmJobBgwdjZ2f33L8hIcTL89JaDqdPn6ZRo0bo6+vj5OTE6dOnNdL37NnDw4cPGTduHIMHD+bUqVMcO3bsmWl/tXXrVrKzswkICKBHjx6cOnVKIz0zM5OzZ88ybNgw+vTpw4kTJ7h06RIAf/75Jxs3bqRt27ZMnDgRKysr1q1bR25uLgBhYWE8fvyY0aNH06VLF86fP19oHYYMGcKAAQMwMjJi3rx52NnZ8fjxY77++mtMTEz44IMP6NmzJxEREZw8eRKAKlWq4OHhgbGxsVZ5d+/exczMTGu5jY0Nvr6+xX3tiujoaE6ePIm/vz9jx44lIyOD7du3U7NmTebNm4eFhQU9evRgzJgxAISGhpKYmMioUaMYOXIkN2/eZOfOnRplnj9/noEDBzJ8+HCSkpJYtmwZlStXZty4cbRo0YKdO3dy//59AKKiooiMjGTQoEGMGTOGBw8esGvXLqWsrKwssrOzCQwMpF69eoXuw7N+Q0KIl+elBAe1Wk1cXBxOTk4ANGnShPj4eB49eqTkuXPnDnXr1qV69erY2toyePBgatas+cy0p925c4fff/+dt99+m3r16tGwYUM6dOiguYM6OgwZMgQrKyuaNm1K7dq1SUxMBPLPbJs1a0azZs0wNzene/fuPHr0iKtXr5KSksKVK1fo27evUnb79u0L3V89PT309PIbYfr6+ujo6HD69Gny8vLo27cvlpaWODs706FDByIiIgAwMjKiU6dOVKhQQau87Oxs9PX1n/dr1/puzMzMqFOnDpaWlrz99ts4OzujUqnQ19dHpVKhq6tLhQoVuHPnDufPn6d///5YW1tTp04d+vXrx6lTp7h7965SZpcuXbC2tqZhw4a4uLhgYmLCW2+9Rc2aNenYsSMqlYqkpCQAfv75Z7p27YqNjQ2WlpZ069aNuLg4JfDq6urSu3dvatWqVei+luQ3JIR4eV5Kt9Iff/xBeno6Dg4OANStWxdDQ0POnDmjdDv4+Pjwn//8h2vXrmFvb4+LiwuVK1d+ZtrTUlNT0dPTU7qBID8YPK3gYFigYsWKSt/1rVu3uH37Nr/99puSnpOTw71793jy5Am6urrUqlWryLKL8+eff1K7dm0laADUr1+fffv2kZOTU2hQKKCnp0dWVlaJt1WYFi1aEBcXx7Jly3B0dMTZ2Rl3d/ci61qxYkWNAFy7dm0qVKjA7du3lTP7p/dfX18fQ0ND5bNKpaJChQpkZ2fz6NEj0tLSCAsLY9u2bUqevLw8pWWhUqmK/T5L8hsSQrw8LyU4xMbGkpuby7x585RleXl5xMbGKv/YTk5OfPTRR5w/f57z589z6NAhBg8ejKOjY7FpT9PR0UGlUinjB6Xh5eWFh4eHxjIjIyOuXbuGrq5uqcvW1dXVOvjl5eWhVqt58uRJscGhatWqStfX065fv87ly5e1WkeFsbCwYOrUqVy6dIlLly7x1Vdf4enpSdeuXbXy6unpadVVrVYrdS2tgQMHagRXgMqVK5OcnPzMdUvyGxJCvDxlHhxyc3M5e/YsPXr00BhkTE5OZsuWLTx8+BADAwN+/PFHWrZsiZubG25ubuzcuZOoqCjs7OyKTPtrcKhWrRo5OTkkJSUprYe8vLwS17V69eokJydjbm6uLMvKykJfX59q1aqRnZ1NWlpaof3/z1KjRg2lG0VXVxeAhIQEqlSpgoGBQbHrNm7cmJ9++ok///xT42w+KiqKjIwMIP/MPTs7G7VarQSwgi4bgMjISGrUqIGDgwMODg7Y2toSGhpKly5dtAKepaUljx8/5vbt21SvXh3IHzDPycnRaJWVlIGBASYmJty9exdnZ2dleVZWlvJdFKckvyETE5PnrpcQouTKfMzh8uXLPH78GDc3N6pXr668nJycMDU1JS4uDj09PS5evMju3bu5ffs2t27d4vr161hYWBSb9ldmZmY0bNiQ7du3c+3aNS5fvsyRI0dKXNc2bdpw6dIljhw5QmpqKmfPnmXhwoXcv38fc3NzGjZsyO7du0lPTyctLY3IyMgiyzI0NCQzM5PLly+TmZmJi4sLKpWK77//ntu3b3Pu3DkiIiJo06YNkD9QfuTIkULPzG1sbHB0dGTLli1cvnyZP//8k4iICE6fPq2Me1haWqJSqQgPDycpKYmIiAhu3LihlJGSksKOHTtISEggNTWVixcvUq1aNSUwGBoa8vvvv3P79m3Mzc1xdnZm69at3Lhxgxs3brB9+3acnJw0AufzaNeuHUeOHOH06dOkpaURHh7OF198UexlqwVK8hsSQrxcugEBAZ/8ten/Ig4fPoypqalW01+lUnHv3j0uXbpEixYtsLOz48KFCxw4cIBTp05Rv359unfvjp6eXrFp169fJzExUbkRztbWlosXLyoHyQYNGnD79m3atGlDSkoKcXFxGt0wv/32G6ampjRs2BBTU1MsLS2JjIzk8OHD3Lp1i27duimXrNra2nLmzBn++9//cvnyZWxsbJSy/6pKlSpcu3aNo0ePYm1tTY0aNWjUqBGnTp3i4MGDXL16lbZt2+Lt7Q3knwXv3bsXV1dXKlWqpFWeo6Mjd+/e5fDhw5w4cYJHjx7Rt29fGjRoAOS3HExMTPj55585efIkhoaGVKpUCTMzM2xtbbG1tSU1NZVDhw4RGRmJvr4+/fv3V66OMjAwIDIykoSEBOXvcfPmTQ4cOEBsbCwNGzakb9++6OrqkpubS0REBC1atFDGfv744w/S0tI0xjF++uknHBwcsLS0pE6dOuTm5hIeHs7Ro0fJzs7m7bffxtTUtNC/S2l+Q0KIsnfr1i0qVqyIKjo6Wu3m5vaq61NqT5480Rj0/fHHH7l06RLvv//+K6yVEEK8mWJiYjA2Nn7zH7z37bffcvToUdLS0rhw4QJRUVFyo5QQQrygl3aHdHnp3r07+/bt49ChQxgbG+Pl5YWnp+errpYQQrzR3vjgUKtWLUaPHv2qqyGEEH8rb3y3khBCiLInwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNAiwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNAiwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNAiwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILa8sOMTHx7+qTQshhHgGaTkIIYTQIsFBCCGEFgkOQgghtEhwEEIIoUWvPDYSERFR6PLExEStZT4+PiUqc+fOnYSFhRWZHhoaWrLKFWPq1Kns3r2b48ePU7Vq1RcuTwgh3hTlEhygZAf9ooJIYRITEzlx4gRubm7cunWLW7duabx/UY8fP+bgwYPk5OSwb98+hg4d+sJlloanpyddu3Zl1qxZr2T7Qoh/pje6W0lXV5ewsDD69esHwHfffUfv3r3LpOwff/yRzMxM2rZty86dO8ukTCGEeFO8suAQFBT0wmXk5ubSv39/tm3bBsDgwYOVA3n//v2LfH3//ffPLHvXrl20aNGCIUOGcOrUKRISErTyHD16FD8/PxwdHfH29iY4OJgnT54o6VevXmXUqFG4uLjQokULJk+eTEpKikYZ+/bto2vXrtjb2+Pr68tPP/0EwMaNG7GxsSEpKYl169ZhY2Oj1KEk5QohxIt4o1sOkN96UKlUAOjo6CjvdXV1C33FxMQ8s9spNTWVY8eO0aVLF9q0aYOxsTG7du3SyHPp0iUCAwOxs7Pjq6++YujQoaxevZrg4GAAcnJyeOedd7h//z4rVqxgxowZREZGMnbsWKWMPXv2MH78eFq3bs0XX3yBnZ0dgYGBXLhwgZ49e7J//36qVauGn58f+/fvp1atWiUqVwghXlS5jTkUeLrFUPB+zpw5pSpLV1eX0NBQgoODCQ4OZvPmzQQHB7N69eoiB6QbNmz4zHL37t1Lbm4unTt3pmLFivj4+LBr1y4mTpyo5Dlx4gTZ2dkEBQVRqVIlvLy81F0ccwAAIABJREFUePLkiRJ4/vjjDxITE5k3bx7t2rUDoHLlyqxbt46MjAwMDQ1ZuHAhvr6+zJw5E4C2bdsSFxfH+vXrWbRoEVWqVEFPT4+qVatiZ2cH5Ael4so1MjIq1XcphBBPK/fgUBAIgoKCCg0KRT1Wo1GjRlrL8vLy8Pf35/r16wAMGzZMee/v70+vXr3o06cP7777Lr1796ZPnz4lquOuXbto3rw51atXB6Br167s3buXmJgY3NzcAGjatCm6urp8+OGHDB48GDc3N8aNG6eUYW1tjYWFBcuXLycjIwMvLy/at29P+/btAbh8+TJJSUm89dZbZGVlKeu5ubkRFxdXZN2eVa4QQpSF165bqVGjRoW+yssff/xBXFwc7dq1IyMjg4yMDNzd3alUqZJG11LTpk3ZtGkTDx8+ZOTIkTRr1oyPPvqIu3fvAmBoaMi2bdto2LAhM2fOxM3NjWHDhnHp0iUgv+sKYMKECdjb2yuvbdu2ce/evSLr96xyhRCiTERHR6tftvDw8DLNp1ar1StXrlQ3aNBArVar1Z9//rm6fv366pycHPXSpUvV9evXL3K9Bg0aqFeuXFlkesH6hb1cXFzUWVlZWus8evRIfeDAAbWHh4f6nXfe0UrPzc1V//rrr+q3335b3axZM3V6ero6Li5OXb9+ffXmzZvVv/32m8brzJkzyrqtWrVSz507t9C6FlauEEK8iOjoaPWFCxfUr13L4Xnk5uYyaNAgtm/fDsDQoUOVs/tBgwYRFhamlac4arWa3bt34+7uTlhYmMYrKCiI+/fvK/dizJkzRxkErlSpEp07d8bPz4+zZ88C8P3339O5c2fS09PR0dGhWbNmjB07lrt373Lz5k3s7OwwNTXlxo0buLq6Ki9bW1uNlpKOjg5qtVr5/KxyhRCiLJTbmMPz3OD2PHJzc5WDZ15envI+NzeXvLw8rTzFiYmJ4ebNm3z44YfK2EKBZs2aERISws6dO+ncuTOtWrVi7NixfPrpp3To0IHk5GR27tyJp6cnAB4eHsyZM4dx48YxYsQIcnNzWb16NTVr1sTGxoYKFSowYcIEPvvsM/Ly8mjXrh2pqamsWLGCLl26MHXqVABq1apFVFQUBw8exNPT85nlCiFEmSiPbqXCXLp06YXWfxndSjNmzCiy60itVquXL1+utrOzU6elpanVarX6+++/V3fp0kVtZ2en9vDwUM+cOVP94MEDJX9sbKx60KBBagcHB7WLi4t65MiR6suXL2uUGRYWpu7UqZO6UaNGak9PT/XixYvVjx8/VtIjIyPVnp6e6qZNm6qvXr1a4nKFEKI0CrqVVNHR0eq/niWXh/j4+BcaaF61ahXLly8v8vEZRe1TTEwMkyZN4v333y/1toUQ4u8qJiYGY2Pj8r+UtaxYWVnh4eEB5F/eaW1trfW+MB4eHlhZWZVLHYUQ4k31xgaH3r17l9lzlIQQQmh6o69WEkII8XJIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKGlXB6fUdTjuhMTE7WW+fj4lLjckj6K+2kqlQpdXd0i08eOHcuBAweA/Dmqa9SoQfv27Rk7diyWlpbPta3ixMTE8P7777Np06YSzWsthBDlqdyerVSSg35J53zIzs5m1KhR/PLLL89dDw8PD0JDQ4vNY2VlRVBQEHl5eSQmJhIaGsrBgwfZsmULtra2z73NwlSvXh1vb2/MzMzKpDwhhChLb+SD906fPs0vv/xC3759qV279nOtW5InshoZGdG+fXvlc//+/enbty8ffvghO3fufO76FqZOnTosWbKkTMoSQoiy9sqCQ1BQEHPmzCnVutnZ2QD07duXli1blmW1CmVgYMCECRMYM2YMZ86cwdnZGYC0tDQ+/fRTwsPDAejSpQuzZs3CyMgIX19f7OzsWLFihVLOxo0b+eyzz4iOjua3335j+PDh7N+/Hzs7OwBu3LjB/PnzOX78OBUrVqRt27ZMnz4dc3NzpYx9+/axatUqEhISqF+/PlOnTqVdu3ZK+rfffsumTZu4ffs2devWZcyYMfTo0eOlf0dCiL+Xv/2AdFBQEA0bNtR4NW/enGvXrj1XOe7u7kD+WAHkB6ghQ4YQGxvL/PnzmTlzJkeOHGHatGkA9OjRg4iICHJycpQyDh8+jLe3NyYmJlrlP3z4kIEDByoBYtasWcTExPDee+8p053u2bOH8ePH07p1a7744gvs7OwIDAzkwoULAISGhrJgwQIGDx5MSEgIzs7OTJw4sVTdb0KIf7ZybzkEBQVpvS9tC6IkLl68SI0aNRgwYACQf3a+bds2bt68Sd26dUtcjpmZGQYGBqSkpACwY8cOLl++zA8//KDMaKenp8fkyZNJTEykW7duLFmyhOPHj+Pt7c3Dhw85ceIEixYtKrT80NBQ0tLS2LVrFxYWFkB+11Pv3r05f/48jo6OLFy4EF9fX2bOnAlA27ZtiYuLY/369SxatIhjx47h5OTE6NGjAfD29iYzM5OEhAS8vLxK9wUKIf6Ryj04FASCorqV4uPjC13vRaYUrVmzpjIt6PHjx9m2bVupynn6yqjIyEgaNGhA3bp1ycrKAqBZs2ao1WrOnDlDly5daNKkCYcOHcLb25uffvoJHR0dOnToUGjZJ0+exMnJSQkMAE2aNOHHH3/E0tKSK1eukJSUxFtvvaVsD8DNzY24uDgAmjdvzsKFC1myZAm+vr44ODiwcuXKUu2rEOKf7bUbkH6RIHD8+HEGDx4MQOvWrdm0aVNZVYuUlBQeP36sHLxTU1OJj4/H3t5eK++9e/cA6NatG2vXrmXu3LkcPnyYtm3bFtqlBHD//n2NsQXIv+y24Oqo1NRUACZMmKC1bsEltiNGjMDIyIgtW7YQEhKChYUF7777LgEBAcVeviuEEH/1yoLDy+hKunLlCrq6upiYmHDjxg0ePXpUZmUfP34cyD87h/wrmpycnDS6yQrUqVMHyA8OCxcu5MSJExw9epS5c+cWWb6pqSl3794tMt3IyAiAefPm4eDgoJFWoUIFID+YDBw4kIEDB5KamsqePXtYsGABAGPGjCnprgohxN9rQDokJISVK1dy4sQJzMzM+P7778uk3PT0dFasWIGzs7NypZKbmxsJCQnUrl0bV1dX5WVtba20AGrVqoWrqyufffYZ2dnZRXYpQX7QiYuLU1oIAAkJCXh6evLbb79hZ2eHqakpN27c0Niera2t0try8/MjJCQEAHNzc4YPH46DgwNnzpwpk+9BCPHPUW4th5Le4PYiVCoVT548Qa1Wk5eXh45O6WJfZmYmkZGR5ObmkpCQwIYNG3j48CFffvmlkmfw4MF89913DB06lHHjxmFhYcGPP/7I9u3biYiIUAJEt27dmDdvHl26dFHO/gszaNAgNmzYwMiRIxkzZgwqlYrPP/8cc3NzmjZtiq6uLhMmTOCzzz4jLy+Pdu3akZqayooVK+jSpQtTp07F1dWVlStXYmBgQOPGjTl16hTnz5+nX79+pfoehBD/XOUSHAq7Ozo+Pr7U4wsVK1YE8q8YOnHiBAD9+vUjMDCQCRMmYGRkhJmZGX5+fuzZs4dbt24RHBwMwM2bN59Z/s2bN/H390dPTw9LS0t8fHwYM2YMNWvWVPKYmpqydetWFixYwOzZs8nJycHZ2Zl169ZpjB34+voyf/58fH19i91m1apV2bp1K59++imTJ09GT0+PTp06MX36dGW8YPjw4RgbG7NmzRo2bNhAtWrV8PPzY/z48QDMmDEDIyMj1qxZQ2pqKrVq1WLKlCkMGTLkOb5dIYQAVXR0tNrNza3cN/wiwSE7O5sRI0YQFRWlLAsLC+PJkydaA9KzZ89m8+bNGuubmJiwZ8+e57qUVQgh/gliYmIwNjZ+M4NDgacfvKenp4darSY3NxcAHR0dpVvpyZMnGus9nSaEEOL/FQSH1+5S1ufx18szVSoVenrau1TYMiGEEEWT02chhBBaJDgIIYTQIsFBCCGEFgkOQgghtEhwEEIIoUWCgxBCCC0SHIQQQmiR4CCEEEKLBAchhBBaJDgIIYTQUi7PlSjqcd2JiYlaywp7gmt5Gjt2LAcOHCg0berUqbz33nulLnvt2rWEhYVx4MCBMnm2U/v27TE3Ny902tPU1FRatmzJu+++y8yZMwkICMDc3FyZ/EcIIYpTbg8dKslBvzRzPsTExCjvTUxMMDEx4datW0XmNzExwc7OrtgyraysCp3hrWDKzpLYunUrM2bMIDY2FlNTUwAaNGiAt7e3EhgKy/M8evTowapVq0hKSqJGjRoaaQcPHiQ3N5devXoB+ZMTVa5c+bm3IYT4Z3qjn0j35MkT+vfvr3z28PDAzc2N1atXF7mOh4cHoaGhxZZrZGRE+/bty6yeBdq1a0e7du3KrLxevXqxcuVKDhw4wLBhwzTS9u/fT/369ZWZ6wICAspsu0KIv79XNuZQ2Jn5m6JFixYsX76coKAgmjVrhpubG7NnzyY7OxvIn/JzxowZALi4uPCvf/0LgMWLFytzUBeW57vvvsPGxobff/9d2VZaWhoNGjRg7dq1WvWwsbHB0dGR/fv3ayxPS0vj+PHj9OzZU1nWp08fxo4dq3zOzc0lODgYT09PHBwceOedd0hISADg2LFj2NjYcPXqVSX/v//9b5o0aaLsI8C4ceM0gvO3336Lj48Pjo6O+Pr6snfv3uf4VoUQrxMZkC5EXl4eGRkZGq/Hjx9r5FmzZg13795lwYIF9OvXj82bNyt9/2FhYUycOBGA7du3M3XqVK1tFJanW7duVKxYkYMHDyr5IiIiUKvVRc4k17NnT3799Vdu376tLCvoUno6OPzVrFmzWLNmDYGBgQQHB3P//n38/f159OgRLVq0QF9fX5llD+Cnn34iPT2dkydPKsuio6Px8vICIDQ0lAULFjB48GBCQkJwdnZm4sSJ/PLLL0XWQQjx+ir34BAUFKS0Gp5+/zq5cuUKzs7OGq/hw4dr5HFzc2PFihV07tyZadOm4eDgwPHjx4H8sQlLS0sgf5zh6elFCxSWp3LlyrRr145Dhw4p+cLDw2nevDm1atUqtK49evQA0Ago+/fvx9nZmfr16xe6zh9//MF//vMfZsyYwbvvvkunTp344osv+PPPP9m/fz/6+vq4u7srwSE1NZW4uDicnZ0JDw8H4OrVq6SkpODt7Q3ktzacnJwYPXo03t7eLFq0CF9fX6U1IoR4s5T7mMOcOXOA/MBQ8P5p8fHxha5X0lnj3n77bVq2bFlkekkGfq2trVm2bJnGMhMTE43P9vb2qFQq5XONGjVITU0tUR2L06tXL8aNG8etW7ewsLDgl19+YfLkyUXmr1GjBu7u7vzwww/4+/tz9+5djh8/zrRp04pcp2B61Q4dOpCVlQVAtWrVsLa2Ji4ujj59+uDt7c369esBOHr0KA0bNmTo0KF88cUXzJ49m+joaCpXrkyTJk2A/G6yhQsXsmTJEnx9fXFwcGDlypUv/H0IIV6N125A+kWnDt2+fbvGgPSmTZvw9/dXPpdkQNrAwIDSTJ1aMGXpi+jQoQOmpqYcOnSIRo0akZmZWWSXUoGePXsya9YsUlJSOHLkCHl5eXTv3r3I/AVBzNPTUyvt/v37AHh7e7NgwQKuX79OREQEPj4++Pj4MH36dH7//XdOnjyJp6enMhvfiBEjMDIyYsuWLYSEhGBhYcG7775LQECA1ox9QojX3ysLDoW1GgRUrFiRLl26cPDgQW7evEnLli2pVq1asev4+voSFBTEwYMHOXToEB4eHkqXVWEMDQ3R09Pju+++0zpwV61aFYDGjRtjYWFBVFQUx44dY82aNVSrVo0mTZoQHh5OdHQ0Y8aMUdZTqVQMHDiQgQMHkpqayp49e5R7Kp7OJ4R4M8iA9EtScC9Dca2JovL06tWLmJgYfvjhh2JbAAUqV66Mt7c3YWFh/O9//yt2IBrA3d2dJ0+e8PDhQ1xdXZWXtbU19erVU/K1bt2aNWvWAODq6grkt2zCwsK4ceOGMt4A4OfnR0hICADm5uYMHz4cBwcHzpw588z6CyFeP+XWcijNDW6vSkZGBkePHtVabm1tjY2NTYnKKBhA3rhxI15eXsrBtSR5WrZsiaWlJXfu3KFz584l2l7Pnj2ZMGGC0vIojouLC2+99RaTJk1i/Pjx2Nvbc+HCBZYvX86qVato27YtkN+1tGvXLnr06IGeXv5PpUOHDixfvpz69etjZWWllOnq6srKlSsxMDCgcePGnDp1ivPnz9OvX78S1V8I8Xopl+BQ2N3R8fHxLzy+oKOjw4QJE5TPVlZW1KpVSzmQQf4B/a95niUxMVHr6iSAUaNG8dFHH5Wobq1ataJbt26EhISQlJRUaHAoKo9KpaJ58+ZkZGRQpUqVEm2vY8eOGBkZ4enpWaI7of/973+zfPlyvv76a+7evUvdunWZPXu2EhgAvLy8UKlUGjfu2dvbY2VlpdFqAJgxYwZGRkasWbOG1NRUatWqxZQpUxgyZEiJ6i+EeL2ooqOj1aUZfH1RZREc/q4yMzNp3bo1c+bMwc/P71VXRwjxDxITE4OxsfHrd7XSP1lWVhaHDh3i+++/x9DQ8JlXKQkhxMsiA9KvkfT0dKZNm0ZycjIhISFUrFjxVVdJCPEPJS2H14i5uTnnz59/1dUQQghpOQghhNAmwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNBSLndIF/W47sTERK1lhT3B9a/mzZtX6J3EBU9M/eyzz7TSHBwcmDVr1jPLHjt2LAcOHABAV1eXGjVq0L59e8aOHVvsBDqlsWXLFmbNmsWZM2cwMjIqNM/48eO5du0au3fvLtNtv4hDhw7x3nvv8fPPP1O7du1XXR0hxEtQbo/PKMlBv6RzPpw/f54TJ05oLX/w4AFAoWnPw8rKiqCgIPLy8khMTCQ0NJSDBw+yZcsWbG1tX6hsIYR4E8izlQphZGRE+/btlc/9+/enb9++fPjhh+zcufMV1qz8FMxOp1KpXnFNhBCvwisbcwgKCnpVm35uBgYGTJgwgdOnT2tMe/nzzz/Tr18/HBwcaNGiBVOmTOHevXsa63777bf4+Pjg6OiIr68ve/fu1Sr/5MmT9OjRA3t7e7p160ZMTIxWntWrV9OyZUscHBwYPXo0t2/f1kg/c+YM/v7+ODk54e7uzqxZs3j48KGSrlarWbduHZ06dcLe3p42bdqwatUqjSlKW7RowdSpUxk4cCB2dnZcuHABgP/+97/4+Phgb29P//79uXjxolb9SrKfQog3x99uQNrU1BQPDw+tl4ODwwuV6+7uDqAcuGNjYxkxYgSNGjXim2++4ZNPPiE6OpqPP/5YWSc0NJQFCxYwePBgQkJCcHZ2ZuLEifzyyy8aZc+aNYuBAweyaNEisrOzGT9+PDk5OUr6uXPniIqKYvbs2UyZMoWTJ09qzG4XHx/PgAEDAFixYgUTJ05k3759jB49mry8PADWrl3L4sWLGTBgAOvXrycgIIDVq1ezefNmjbrs2LEDJycnVqxYgZWVFbGxsUycOJG6deuyZMkSOnbsqMwr/bz7KYR4c5R7t9LTLYaC93PmzCmz8p2dnQkNDS2z8gqYmZlhYGBASkoKABYWFnz77bd4eXmho5MfY5OTk1m6dCl5eXno6Ohw7NgxnJycGD16NJA/J3NmZiYJCQl4eXkpZS9fvpwWLVoA+a2UwMBArl27RoMGDQCoXr0669atU+Z3MDc3Z+LEiZw5cwZnZ2c+//xzzM3N+fbbb6lQoQIAdevW5Z133iE8PJyOHTvi6emJu7s7Li4uAHh4eHDs2DHCw8Px9/dX6tKzZ09mzpypfA4JCcHKyoq1a9cq068aGRlpDO6XdD+FEG+Ocg8OBYEgKCio0KAQHx9f6HolnVL03LlzLFy4UGt548aNNc7qS+PpLhgrKysePnzIxIkTOXfuHKmpqWRnZ/P48WNycnLQ19enefPmLFy4kCVLluDr64uDgwMrV67UKtfR0VF5X6NGDQBSU1OV4GBhYaEx8U+nTp0AuHjxIs7OzkRFRdGvXz8lMED+/M/VqlXjf//7Hx07dsTR0ZHDhw/j7+9PQkICDx8+5NGjRxrbLtjW086ePUuXLl005uX+a56S7qcQ4s3x2g1IlyQIODk5aRysClSpUoV79+4RGRmplfbkyZMXqldKSgqPHz9WDoxXr16lX79+dOzYkUWLFlG9enX27t3LsmXLlHVGjBiBkZERW7ZsISQkBAsLC959910CAgLQ1dUtdntPB6K/MjAwQF9fn5SUFHJzc3nw4EGhl9laWFhw9+5dAPbs2cPkyZOZMGEC06ZNo0qVKnzyySekpaU9c7+rVatWbJ4X2U8hxOvplQWHF+lKKq4FUFhgKAvHjx8H8s+SAXbu3ImxsTHLly9XruipUqWKxjoqlYqBAwcycOBAUlNT2bNnDwsWLABgzJgxpa5LZmYmWVlZmJubo6uri4mJSaEH+Tt37tCqVSsg/54KX19f3n//fSXd0NDwmcHBwsJCY2C7MC9rP4UQr84bOSA9b948Bg0apPV6+kqispSens6KFStwdnbG2dkZgPv376NSqTTO8OPi4jTW8/PzIyQkBMgfJxg+fDgODg7PXc87d+6QlZWlfD5y5AgA9vb2QP74wcGDB8nNzVXyHD9+nJSUFFq2bKlR3wLZ2dmFXnX0V87OzloDy+np6Rqfy2o/hRCvj3JrOZT0BreSeNZNcC8qMzOTyMhIcnNzSUhIYMOGDTx8+JAvv/xSydOuXTs2bdrElClTaNu2LeHh4crlmxkZGejr6+Pq6srKlSsxMDCgcePGnDp1ivPnz9OvX7/nqk9KSgrDhg3D39+f5ORkgoOD8fT0VALVxIkT6du3LwEBAQwdOpTU1FQWL16Mu7u7cr9G27ZtWbduHTY2NlhZWbF582Z+//13GjZsWOy2x4wZQ+/evQkKCmLAgAHcvHmTxYsXa+Qpq/0UQrw+yiU4FHZ3dHx8fIkHmcvbzZs38ff3R09PD0tLS3x8fBgzZgw1a9ZU8vj4+DB79my++eYb9u/fT+vWrVm2bBkhISEkJiZiZmbGjBkzMDIyYs2aNaSmplKrVi2mTJnCkCFDnqs+bdq0oWnTpgQFBfHgwQO8vb2ZP3++ku7g4MCWLVtYtGgRY8eOxcTEhK5duzJ16lTlSqqJEyeSkZHBhg0byMvLo3fv3nTs2JGDBw+Sk5OjMZj9tIKroZYuXcq2bduwt7dn5MiRSrcRUGb7KYR4faiio6PVbm5u5b7hFwkOgwYNKrTlsGnTJgCNSzMLeHh4vJRLXIUQ4u8kJiYGY2Pj1+9qpZIo6oY2U1NTID8QlHQdIYQQ2t7I4PCsp6tKC0EIIV7MG3m1khBCiJdLgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggt5fL4jKIe152YmKi1rLAnuJanwMBAfvzxR+Wzqakp9erVY9iwYfj5+ZW4nKysLOzt7fnkk0945513XkZVGT9+PNeuXWP37t0vVE5AQADm5uYaT1oVQvyzlduzlUpy0C/LOR9ehJWVFUFBQUD+xDZHjhxh0qRJJCUl8d57773i2pU9Nzc3Kleu/KqrIYR4jbyRD977q08++YTu3btT1KPHY2Ji2LdvX4mnJjUyMlImyQHo2bMnDx48YO3atX/L4BAQEPCqqyCEeM28sjGHgjPzF7Vy5Uo2btxIdHR0kXl+/fVXNmzYwKpVq0q9HTc3N9LS0khPT+f8+fPY2NhozVdtb2+vTJdZmLS0NCZNmoSLiwsuLi5Mnz6djIyMYre7efNmOnTogIODA76+vuzfv18rz5YtW/D29sbZ2Znhw4eTlJSkpKnVatatW0enTp2wt7enTZs2rFq1SmN60z59+jB27FgAfv/9d2xsbNizZw+jRo3CwcEBb29vtmzZorHNo0eP4ufnh6OjI97e3gQHB/PkyZNi90UI8eZ4owekDxw4wOeff06vXr2Kncg+MDCQ3r17s2LFCg4cOFCqbSUkJGBsbIyxsXGp1s/OzmbIkCHExsYyf/58Zs6cyZEjR5g2bVqR66xdu5ZPPvmErl278uWXX9KqVSvGjRvHTz/9pOS5dOkSYWFhTJ06lSlTpvDrr79qzBK3du1aFi9ezIABA1i/fj0BAQGsXr2azZs3F1vfjz76CGdnZ1asWEHjxo2ZPXs28fHxyjYDAwOxs7Pjq6++YujQoaxevZrg4OBSfTdCiNdPuXcrPd1iKHhf0u6ep509e5bJkyfj4uLCwoULn5l/wYIFJCQkMHnyZKytrXF0dCwyb15ennJGn56ezv79+9m1axejRo167noW2LFjB5cvX+aHH35QZsDT09Nj8uTJJCYmYmVlpZE/JyeHVatW8c477/Dhhx8C+fNAX716lS1bttCuXTsATExMWLduHWZmZgBcv36dPXv2KOV4enri7u6Oi4sLkD8R0rFjxwgPDy90xrwCAQEBjB8/XimjadOmREdH06hRI06cOEF2djZBQUFUqlQJLy8vnjx5wq1bt0r9/QghXi/lHhwKAkFQUFChQaHg7PSvnp5S9Pbt2wQGBlK1alVCQkLQ19fn008/RaVS8fHHHyvBYvr06cyfPx+1Ws3MmTP56quv8PPzIyAggJ07d1K9evVCt3XlyhWcnZ2Vzzo6OgwfPlw5SJdGZGQkDRo0oG7dumRlZQHQrFkz1Go1Z86c0QoOFy9e5MGDBxpjHwBLliwhMzNT+VyrVi0lMABYWlqSkpKifHZ0dOTw4cP4+/uTkJDAw4cPefToUbHBEcDJyUl5b2JigqGhoVJu06ZN0dXV5cMPP2Tw4MG4ubkxbtzh0bN0AAAFJElEQVS45/xGhBCvs9duQLqk80qr1WpUKhUqlarM62Btbc2yZcuA/MHsxYsX061bN3R1dUtdZmpqKvHx8djb22ul3bt3T2vZ/fv3ATA3N9dYbm5urrWsOHv27GHy5MlMmDCBadOmUaVKFT755BPS0tKecw/+X9OmTdm0aRNffvklI0eORFdXl549ezJlyhSqVq1a6nKFEK+PVxYcStOVVKB69ep8/fXXDBgwgMDAQEJDQ5k5c6aSPn36dOX9xx9/DOT3+QcGBnL37l22bt1aZKsBwMDAQLnyqVmzZuzcuZOlS5cq/fQFAenpQd1nMTIywsnJqdCB+Dp16mgtK5gP++7duyXeRmG2bNmCr68v77//vrLM0NDwhYIDQMuWLWnZsiWPHz/m6NGjzJkzh8TERDZs2PBC5QohXg9v7IC0k5MTS5cuJTY2tthB3QIzZswgNjaWZcuWaXSZPIuOjg4TJ04kKipKuTrJwsICQOOqoNTU1GKv1nFzcyMhIYHatWvj6uqqvKytrQttCdjZ2WFsbKx178fs2bOfqwvn/v37Gq2r7OxsLl68WOL1CzNnzhzl6qZKlSrRuXNn/Pz8OHv27AuVK4R4fZRby+Fl3ODWtWtXJk6cyIoVK2jUqFGRVyx99dVX7Ny5k/9r5w5aaVvDAI4/S6FTksLITCklIxlgYEDbnppKBgyUndIeyEBGyoS0k5SSSEopYwxMFV/BBzAwNNpx7uDm3PTkdNw697rd3+8DvOttDda/Z72tVa1Wo1wuf/o65XI5+vr6YnNzM0ZGRqKjoyN6e3tjZ2cnmpqaol6vx8HBQby8vHy4xtTUVJyensb09HRUKpXo7OyM6+vrOD8/j5ubmxSI5ubmmJ+fj1qtFi0tLTE4OBi3t7dxenoau7u7v7z30dHRODw8jO7u7ujq6oqTk5N4eHiInp6eT9+HN0NDQ7GwsBDr6+sxNjYWj4+PcXFxEcPDw397TeBr+Ufi8Dt/ibG4uBhPT08ffgAXETEwMBAzMzPvXq18RlEUUa1WY25uLq6urqJUKsX29nasrq7GyspKtLe3x+zs7E+/o2htbY2zs7PY2NiItbW1qNfr0d/fH4eHhx+eIVQqlfj27VscHx/H3t5edHd3R61Wi4mJiV/e+9LSUjw/P8fR0VG8vr7G5ORkjI+Px+XlZdTr9WhsbPz0/SiXy7G1tRX7+/txcnISbW1tUSqVYnl5+dNrAV9TcXd39/1nD1YA/j/u7++jpaXlv3vmAMDvIw4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AvFMUhTgAkIkDAElDURT/9h4A+CLemmByAOCdoihMDgD85cfk0NBgeADgT29NMDkA8ENRFFEURfwBirKKPwVcjBEAAAAASUVORK5CYII=",
+ "description": "Widgets to manage ThingsBoard Edge."
+ },
+ "widgetTypes": [
+ {
+ "alias": "edges_overview",
+ "name": "Edge Quick Overview",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYcAAAFCCAYAAAAaOxF5AAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AACAASURBVHic7N15XE35/8Dx161IK0pCshTSRlEihSyDbGHsmrHWYAZfxjZjmRhjZ/piphlm7NNXGNvXWIYaY+qLmpHsMSNLpqhsFZW6vz96dH7u3EqSMPN+Ph738bj3fD7ncz7ndjvv8/l8zjkfVXJyslqtVqNWqwHIy8sD4OllBcuf/iyEEOLNoVKplPc6OjqoVCplmY6OjpKn4KWnVquxtLR8JZUVQgjxeklOTgZAJzc39xVXRQghxOsiNzcXtVqNXk5OzquuixBCiNdEQUyQ4CCEEEKRk5ODjo6OBAchhBD/LycnB11dXXSePHnyqusihBDiNfHkyRPy8vJkQFoIIcT/y83NzQ8OBfc1CCGEEAX3tOnIjW1CCCEKFMQECQ5CCCEUBU/H0HnVFRFCCPH6keAghBBCg7QchBBCFEqCgxBCCC0SHIQQQmiR4CCEEEKLBAchhBBa9F51BdLS0li3bh1Hjhzhxo0bAFhbW9OhQweGDRuGubn5K66hEEL887zS4HDkyBEmT55MnTp16NevH40aNUKtVnP58mV27tzJxo0bWbJkCW+99darrKYQQvzjvNJupePHjzNjxgxCQ0PR0dEhLCyM7du3o6OjQ2hoKDNnzuT48ePPLGfs2LHY2NgU+goJCSlyvUOHDmFjY8PNmzfLcreKFRISwvjx4wE4evQoHTt21Mpz/fp1li5dSocOHUpc7vHjx/H396dp06Y0b96c0aNHc+bMmeeuX2ZmJt7e3uzYsaPE6/zrX/+iV69ez72t5ORkZsyYQatWrXB0dMTPz4/du3c/dzkvS0BAADNmzHjV1RDilXilLYePP/6YtLQ0unbtSoUKFfDw8ECtVrNx40bWrl3Lrl276NevX4nKsrKyIigoSGu5ra1tWVf7hZw+fRp3d3flvYuLi5J27tw5Fi5cSFRUFDo6Oujr65eozIMHD/LBBx/g7u7OjBkzyMnJYfv27QwYMID169fTokWLEtdPX18fLy8v6tWr91z79bySk5N5++23AZTuw8jISCZNmkRCQgITJkx4qdsvCTc3NypXrvyqqyEEkH9iuWvXLuVzz549GTt2LADp6enK/1OBsLAwTE1NS729Vz7mYGZmRlBQEF5eXlSoUAHIn2zi2LFjzzXeYGRkRPv27V9WNctMbGwsI0eOBPKDQ7t27ZS0s2fPUqVKFdatW8evv/7KN99888zyMjIymDVrFm3atGHNmjWoVCoABgwYQN++fZkxYwaHDx9Wlj+Lrq4uCxYseP4de04LFy4kPT2dQ4cOYWFhAcDbb7+NtbU1q1atwtfXl4YNG76Ubefm5qKrq/vMfAEBAS9l+0KUxp07d4iPj8fOzg4dHR10dDQ7fgp+08nJyaSlpfGic/W8Flcr+fj4KIEBoEKFCi/lQP/f//4XHx8f7O3t6d+/PxcvXtTKc+bMGfr06YO9vT3t27fnhx9+wNHRka1btyp50tLSmDRpEi4uLri4uDB9+nQyMjKK3O6pU6eUbq7k5GT69++PjY0NERERzJkzRzmzHzBgACtXrqRNmzZaf/iiHD16lJSUFMaPH68RACpWrMh7773H1atXiY2NBeD8+fPY2NgQGRmpUYa9vb3S/ZaVlYWNjQ0bN25U0h8/fsy8efPw8PCgSZMm+Pv7c/78+SLrFBUVhZ2dHUuWLCk0PSMjgwMHDjBkyBAlMBQYNWoU+vr6SveSr68v//rXvzTybNy4kcaNG/Pw4UMg/2AfHByMp6cnDg4OvPPOOyQkJCj5x48fT58+fZg2bRoODg5s3LiRuXPn0rp1a55+8OSlS5ewsbHh6NGjAPTp00c5Myuwb98+unbtir29Pb6+vvz0009Afndc48aNCQ0NVfKePHkSGxsbfvvtN2XZpk2baNy4MZmZmUV+f0IUZ8uWLezbt4/33ntPWWZsbMy+ffvYt29fqbp4C/NaBIcCBQfQ0kS8vLw8MjIyNF6PHz9W0mNjY5k4cSJ169ZlyZIldOzYkTVr1miUkZ6ezrBhw7h37x5z585l7NixrFixgkePHil5srOzGTJkCLGxscyfP5+ZM2dy5MgRpk2bVmTdHB0dCQ8PZ9GiRdSvX5/w8HDCwsLQ0dHh4MGDfP/998+9vwXi4uKoWLEiTk5OWmnNmjUD8rurXsQHH3zAzp07GT9+PMHBwahUKoYOHUpSUpJW3osXLzJmzBi6du3Khx9+WGh5Fy9eJCsri+bNm2ulmZqaYmtrq4yX9OjRg4iICJ6ezvbw4cN4e3tjYmICwKxZs1izZg2BgYEEBwdz//59/P39Nf5usbGxpKSksHDhQtq0aUP37t35888/NcZljhw5QpUqVfD09Cy03nv27GH8+PG0bt2aL774Ajs7OwIDA7lw4QKGhoa4urpy8uRJJX9B4AgPD1eWRUdH07x5cwwNDQvdhhCvi1ferVRWrly5grOzs8YyDw8P5UwuJCQEKysr1q5di55e/m4bGRkxa9YsJX9YWBj37t1jx44dSp97s2bNNAaNd+zYweXLl/nhhx9o1KgRAHp6ekyePJnExESsrKy06laxYkXq1avHwYMHcXJyol69ely7do369eu/cNdJWloaFhYWhbY0qlevjkqlIjU1tdTlx8XFceTIEb788ks6d+4MQKtWrWjdujX79u1TusgAbt26xYgRI3BwcGDx4sVFdmWlpaUBYGlpWWi6paUlt27dAqBbt24sWbKE48eP4+3tzcOHDzlx4gSLFi0C4I8//uA///kP8+bNY8iQIQA4ODjQtm1b9u/fT58+fYD8Mamvv/5aaXqr1WqsrKw4ePAgTZo0AeDHH3/krbfe0mjFFlCr1SxcuBBfX19mzpwJQNu2bYmLi2P9+vUsWrQIb29vNm/erKwTERFBkyZNCA8PVwJldHQ077777jO/dyH+qn///nh4eGBsbFwm+Z7lbxMcrK2tWbZsmcaygjNLyO/P79KlixIYAK0ujbNnz2JnZ6cxGFutWjWNPJGRkTRo0IC6deuSlZUF5AcQtVrNmTNnCg0OBc6fP4+9vT0AFy5cUN6/iOLm41CpVCUeayjKyZMnUalU+Pj4KMsqVarEnj17NAbMHz9+TGBgILq6uoSEhFCxYsVn1rmouj9d5zp16tCkSRMOHTqEt7c3P/30Ezo6OsqVXFFRUQB06NBB+XtUq1YNa2tr4uLilOBgZmamMc6gUqnw9fXl0KFDTJkyhdu3bxMXF8ekSZMKrdOVK1dISkrirbfeUrYD+YPWcXFxAHh7e7N06VKuXbtGhQoVuHz5Mtu2baNv377cunWLnJwckpOT8fLyKvK7EaIodnZ22NnZlVm+ZymX4JCbm1vsQeyv8vLySjxoWMDAwAA3N7ci01NSUrQO9H+Vlpb2zDypqanEx8cXemC/d+9eoev07t2bixcvkpOTw/79+wkODla6zuzt7Vm1atVzXbb6NDMzM27fvp0/5+tfWg9JSUnk5eVhZmZWqrIhf59MTEy0DvZ/DYKXL19WAu/169e1WnFPK7jQIDk5GUdHR6305ORkjTp369aNtWvXMnfuXA4fPkzbtm2VwF/QKiqsK+j+/fvF7lv37t1Zs2YNV65c4eTJk1StWpVWrVoVmrdgO4VdRVXQAnJ0dKRq1aqcPHmS7OxsmjRpgqurKw0bNiQiIgJ9fX3Mzc0L3WchXjcvNThERkYyZcqUQvumi9O4cWMAatSowZIlS2jduvUL18XCwkIZwCxK9erVCx2kfpqRkRFOTk6FXjZbp06dQtdZuXIl6enpdOvWjS1btmBmZsaoUaMICAigRYsW1KhRo+Q78hdOTk7k5OQQFxencVksoPSnF3SbFJyRP0+gNjU1JT09nZycnEK7WwoYGxuzZcsWpk+fzr/+9S/27t2LgYFBoXnt7OzQ19fn119/1brwICMjgz/++AN/f39lWbdu3Vi4cCEnTpzg6NGjzJ07V0kzNDRET0+P7777TutkomrVqsXum7OzM3Xr1uXQoUPExMTQuXNnjZbl04yMjACYN28eDg4OGmkF34uOjg6enp6cOHGCe/fuKa2t9u3bExERgbm5OV5eXi/cmhP/TBcuXCAhIYEOHToU2zIvab5neakD0tOmTXvuwPC0pKSkYgd6n4ezszO//PKLxrL09HStPBcuXNC4Ke727dsaedzc3EhISKB27dq4uroqL2tr6yIvva1duzYqlQpDQ0Pc3d2pV68eSUlJeHp6Ymtrqxx4SsPHxwczMzNWrVqlcdDPycnhyy+/pEGDBkpwKOhGe/pvkpqaWuwFAM2bNycvL4+ff/5ZWZabm0uvXr00LrWtX78+zs7OfP7559y6davYy2GNjY3p0qULoaGhpKSkaKStX7+ezMxMevbsqSyrVasWrq6ufPbZZ2RnZ2u0stzd3Xny5AkPHz7U+nuU5F4NX19f9uzZw//+9z+6detWZD47OztMTU25ceOGxnZsbW2VsSfI71qKiooiKipKCQ4dOnRQlkmXkiit7du3M27cOK3jVmnzPctLDQ4Fg4qQfw1uwZmdSqVCV1dXOYN6Oq24MoqTkZHB0aNHtV5//PEHAGPGjOHcuXMEBQVx8eJFDh8+zOLFizXK6Nu3L1WrVmXYsGFs376d7du3a13rPnjwYKpUqcLQoUOVg8rcuXNp3759sQO/V65cUW7Iu379OpA/TvKijI2N+fTTTzl69Cjvvvsu27dvJzQ0lEGDBnH58mU+++wz5XuuVq0ajRs3ZuXKlezZs4cdO3bg7+9Pbm5ukeU3b94cLy8vpk+fTlhYGL/88gsffPABCQkJygD10xo2bMhHH33E5s2biYiIKLLcGTNmYGhoSO/evfnmm2/YvXs3M2bMYMWKFQQGBmr1mXbr1o2zZ8/Srl07jWDq4uLCW2+9xaRJk1i3bh3Hjx9n3bp1+Pj4KJekFqd79+7Ex8djYmKCh4dHkfkqVqzIhAkT+Oabb/jss8+Iiopi7969+Pn58fnnnyv5vLy8SEpKwtTUVOk+cnV1xcDAgMTERLy9vZ9ZJyFeB+U2IH358mUg/+Dx3nvvMXnyZJYtW0ZISIiSZmNjU+ryExMTGT58uNbyUaNG8dFHHylntUuXLmXbtm3Y29szcuRIjTNcQ0ND1q9fz6xZs5g5cyY1a9ZkxIgRzJkzRxl8NTU1ZevWrSxYsIDZs2eTk5ODs7Mz69atK/amvStXrihXJl25cgUbG5sS38vwLF26dGHjxo2sXr2aefPmoVKpyMrKom7duloBaMWKFcycOZPp06djbm7OiBEjWLVqVbHlf/nllyxatIjFixeTmZlJs2bNCA0NpXbt2oXmHzp0KEePHmXatGns37+/0O+levXq7NixgxUrVvD111/z8OFDbG1tWbhwodadnpB/hj9//nx8fX210v7973+zfPlyvv76a+7evUvdunWZPXs2bdu2LXa/IH/Mx9bWlpYtWz5zjGv48OEYGxuzZs0aNmzYQLVq1fDz81MehwL5rRxbW1uaNWumcfLTrl07zp8/T/Xq1Z9ZJyGKM2DAAHR1dYu8Q/rOnTtlsh1VdHS0uriB3Bfx9MG+4Ay+NMGhYN3y8ODBA41bzqOiohg6dCi7d+8udpD1dRMXF0dgYCAA33zzjVY/uRDizfL1119rPHusR48eyo1w6enpDBgwQCN/aGhoqR6fERMTg7GxcfkFhxdRXsEhOTmZzp07M3z4cNzd3bl16xbBwcFYWVkRGhr6xg0kJicns3jxYj7++OMXumJJCPHPURAcXmq3kpWVFYmJiS9cRnmxtLQkODiY1atX89VXX1GlShXatWvH1KlT37jAAPn789d7P4QQoiReanBYtGgR06ZNK3WAsLKyUu6ELS9t27YtUV+1EEL8nb3U4ODp6cmxY8de5iaEEEK8BK/Vg/eEEEK8HiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCy0sNDklJSUydOpVz586VWZm//vorq1atKrPynkdMTAzz588vl23l5eVx6NAhFixYwKxZs1i1ahXx8fHlsu3Xycv4DQkhnu2lBofY2Fh0dHSIjY0tszLr1KlDq1atyqy8snL69GmCgoLKrLytW7dy6tQpevXqxdixY3F0dOTbb78lISGhTMpfunQpUVFRZVLWy/QyfkNCiGd7qcHh9OnTeHl5ceHCBbKzs8ukTAsLC5o3b14mZb2ubty4walTpxgyZAgODg7UrFkTHx8fmjRpQnh4+KuuXplTq9VFpr2M35AQ4tn0XlbB169f58GDB3Tq1Im4uDjOnTuHq6urkp6ZmcnOnTuJj49HT08PV1dXfH190dHRKTbtl19+4eTJk0yaNAmAe/fusXXrVq5du0a1atVwcnLixIkTzJo1iwsXLhAWFkbXrl358ccfyc7OxtXVlV69eqFSqQCIj49n3759pKWlUatWLXr37k2NGjWUssPCwrh27Ro1atSgbt26he7rhg0blG6PqVOnMnz4cOzt7blz5w67d+8mISEBAwMDWrduTbt27QBITExk3bp1jBs3jqpVq2qUd+7cOWrWrEnt2rU1lrds2ZKLFy8qecLCwjRaK1988QUNGzakU6dOqNVqDh06RHR0NDk5Odja2uLn50d6ejqff/45ALt27SImJobx48eTlZXF3r17OXPmDABOTk707NkTfX19srKymDVrFt27d+fEiRPcvXsXGxsb+vfvz8GDBzl9+jQGBgZ06tSJFi1aKPU5duwYP//8Mzk5OdjZ2dGrVy8MDQ25cOEC3333HS1atCAmJobBgwdjZ2f33L8hIcTL89JaDqdPn6ZRo0bo6+vj5OTE6dOnNdL37NnDw4cPGTduHIMHD+bUqVMcO3bsmWl/tXXrVrKzswkICKBHjx6cOnVKIz0zM5OzZ88ybNgw+vTpw4kTJ7h06RIAf/75Jxs3bqRt27ZMnDgRKysr1q1bR25uLgBhYWE8fvyY0aNH06VLF86fP19oHYYMGcKAAQMwMjJi3rx52NnZ8fjxY77++mtMTEz44IMP6NmzJxEREZw8eRKAKlWq4OHhgbGxsVZ5d+/exczMTGu5jY0Nvr6+xX3tiujoaE6ePIm/vz9jx44lIyOD7du3U7NmTebNm4eFhQU9evRgzJgxAISGhpKYmMioUaMYOXIkN2/eZOfOnRplnj9/noEDBzJ8+HCSkpJYtmwZlStXZty4cbRo0YKdO3dy//59AKKiooiMjGTQoEGMGTOGBw8esGvXLqWsrKwssrOzCQwMpF69eoXuw7N+Q0KIl+elBAe1Wk1cXBxOTk4ANGnShPj4eB49eqTkuXPnDnXr1qV69erY2toyePBgatas+cy0p925c4fff/+dt99+m3r16tGwYUM6dOiguYM6OgwZMgQrKyuaNm1K7dq1SUxMBPLPbJs1a0azZs0wNzene/fuPHr0iKtXr5KSksKVK1fo27evUnb79u0L3V89PT309PIbYfr6+ujo6HD69Gny8vLo27cvlpaWODs706FDByIiIgAwMjKiU6dOVKhQQau87Oxs9PX1n/dr1/puzMzMqFOnDpaWlrz99ts4OzujUqnQ19dHpVKhq6tLhQoVuHPnDufPn6d///5YW1tTp04d+vXrx6lTp7h7965SZpcuXbC2tqZhw4a4uLhgYmLCW2+9Rc2aNenYsSMqlYqkpCQAfv75Z7p27YqNjQ2WlpZ069aNuLg4JfDq6urSu3dvatWqVei+luQ3JIR4eV5Kt9Iff/xBeno6Dg4OANStWxdDQ0POnDmjdDv4+Pjwn//8h2vXrmFvb4+LiwuVK1d+ZtrTUlNT0dPTU7qBID8YPK3gYFigYsWKSt/1rVu3uH37Nr/99puSnpOTw71793jy5Am6urrUqlWryLKL8+eff1K7dm0laADUr1+fffv2kZOTU2hQKKCnp0dWVlaJt1WYFi1aEBcXx7Jly3B0dMTZ2Rl3d/ci61qxYkWNAFy7dm0qVKjA7du3lTP7p/dfX18fQ0ND5bNKpaJChQpkZ2fz6NEj0tLSCAsLY9u2bUqevLw8pWWhUqmK/T5L8hsSQrw8LyU4xMbGkpuby7x585RleXl5xMbGKv/YTk5OfPTRR5w/f57z589z6NAhBg8ejKOjY7FpT9PR0UGlUinjB6Xh5eWFh4eHxjIjIyOuXbuGrq5uqcvW1dXVOvjl5eWhVqt58uRJscGhatWqStfX065fv87ly5e1WkeFsbCwYOrUqVy6dIlLly7x1Vdf4enpSdeuXbXy6unpadVVrVYrdS2tgQMHagRXgMqVK5OcnPzMdUvyGxJCvDxlHhxyc3M5e/YsPXr00BhkTE5OZsuWLTx8+BADAwN+/PFHWrZsiZubG25ubuzcuZOoqCjs7OyKTPtrcKhWrRo5OTkkJSUprYe8vLwS17V69eokJydjbm6uLMvKykJfX59q1aqRnZ1NWlpaof3/z1KjRg2lG0VXVxeAhIQEqlSpgoGBQbHrNm7cmJ9++ok///xT42w+KiqKjIwMIP/MPTs7G7VarQSwgi4bgMjISGrUqIGDgwMODg7Y2toSGhpKly5dtAKepaUljx8/5vbt21SvXh3IHzDPycnRaJWVlIGBASYmJty9exdnZ2dleVZWlvJdFKckvyETE5PnrpcQouTKfMzh8uXLPH78GDc3N6pXr668nJycMDU1JS4uDj09PS5evMju3bu5ffs2t27d4vr161hYWBSb9ldmZmY0bNiQ7du3c+3aNS5fvsyRI0dKXNc2bdpw6dIljhw5QmpqKmfPnmXhwoXcv38fc3NzGjZsyO7du0lPTyctLY3IyMgiyzI0NCQzM5PLly+TmZmJi4sLKpWK77//ntu3b3Pu3DkiIiJo06YNkD9QfuTIkULPzG1sbHB0dGTLli1cvnyZP//8k4iICE6fPq2Me1haWqJSqQgPDycpKYmIiAhu3LihlJGSksKOHTtISEggNTWVixcvUq1aNSUwGBoa8vvvv3P79m3Mzc1xdnZm69at3Lhxgxs3brB9+3acnJw0AufzaNeuHUeOHOH06dOkpaURHh7OF198UexlqwVK8hsSQrxcugEBAZ/8ten/Ig4fPoypqalW01+lUnHv3j0uXbpEixYtsLOz48KFCxw4cIBTp05Rv359unfvjp6eXrFp169fJzExUbkRztbWlosXLyoHyQYNGnD79m3atGlDSkoKcXFxGt0wv/32G6ampjRs2BBTU1MsLS2JjIzk8OHD3Lp1i27duimXrNra2nLmzBn++9//cvnyZWxsbJSy/6pKlSpcu3aNo0ePYm1tTY0aNWjUqBGnTp3i4MGDXL16lbZt2+Lt7Q3knwXv3bsXV1dXKlWqpFWeo6Mjd+/e5fDhw5w4cYJHjx7Rt29fGjRoAOS3HExMTPj55585efIkhoaGVKpUCTMzM2xtbbG1tSU1NZVDhw4RGRmJvr4+/fv3V66OMjAwIDIykoSEBOXvcfPmTQ4cOEBsbCwNGzakb9++6OrqkpubS0REBC1atFDGfv744w/S0tI0xjF++uknHBwcsLS0pE6dOuTm5hIeHs7Ro0fJzs7m7bffxtTUtNC/S2l+Q0KIsnfr1i0qVqyIKjo6Wu3m5vaq61NqT5480Rj0/fHHH7l06RLvv//+K6yVEEK8mWJiYjA2Nn7zH7z37bffcvToUdLS0rhw4QJRUVFyo5QQQrygl3aHdHnp3r07+/bt49ChQxgbG+Pl5YWnp+errpYQQrzR3vjgUKtWLUaPHv2qqyGEEH8rb3y3khBCiLInwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNAiwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNAiwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNAiwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILa8sOMTHx7+qTQshhHgGaTkIIYTQIsFBCCGEFgkOQgghtEhwEEIIoUWvPDYSERFR6PLExEStZT4+PiUqc+fOnYSFhRWZHhoaWrLKFWPq1Kns3r2b48ePU7Vq1RcuTwgh3hTlEhygZAf9ooJIYRITEzlx4gRubm7cunWLW7duabx/UY8fP+bgwYPk5OSwb98+hg4d+sJlloanpyddu3Zl1qxZr2T7Qoh/pje6W0lXV5ewsDD69esHwHfffUfv3r3LpOwff/yRzMxM2rZty86dO8ukTCGEeFO8suAQFBT0wmXk5ubSv39/tm3bBsDgwYOVA3n//v2LfH3//ffPLHvXrl20aNGCIUOGcOrUKRISErTyHD16FD8/PxwdHfH29iY4OJgnT54o6VevXmXUqFG4uLjQokULJk+eTEpKikYZ+/bto2vXrtjb2+Pr68tPP/0EwMaNG7GxsSEpKYl169ZhY2Oj1KEk5QohxIt4o1sOkN96UKlUAOjo6CjvdXV1C33FxMQ8s9spNTWVY8eO0aVLF9q0aYOxsTG7du3SyHPp0iUCAwOxs7Pjq6++YujQoaxevZrg4GAAcnJyeOedd7h//z4rVqxgxowZREZGMnbsWKWMPXv2MH78eFq3bs0XX3yBnZ0dgYGBXLhwgZ49e7J//36qVauGn58f+/fvp1atWiUqVwghXlS5jTkUeLrFUPB+zpw5pSpLV1eX0NBQgoODCQ4OZvPmzQQHB7N69eoiB6QbNmz4zHL37t1Lbm4unTt3pmLFivj4+LBr1y4mTpyo5Dlx4gTZ2dkEBQVRqVIlvLy81F0ccwAAIABJREFUePLkiRJ4/vjjDxITE5k3bx7t2rUDoHLlyqxbt46MjAwMDQ1ZuHAhvr6+zJw5E4C2bdsSFxfH+vXrWbRoEVWqVEFPT4+qVatiZ2cH5Ael4so1MjIq1XcphBBPK/fgUBAIgoKCCg0KRT1Wo1GjRlrL8vLy8Pf35/r16wAMGzZMee/v70+vXr3o06cP7777Lr1796ZPnz4lquOuXbto3rw51atXB6Br167s3buXmJgY3NzcAGjatCm6urp8+OGHDB48GDc3N8aNG6eUYW1tjYWFBcuXLycjIwMvLy/at29P+/btAbh8+TJJSUm89dZbZGVlKeu5ubkRFxdXZN2eVa4QQpSF165bqVGjRoW+yssff/xBXFwc7dq1IyMjg4yMDNzd3alUqZJG11LTpk3ZtGkTDx8+ZOTIkTRr1oyPPvqIu3fvAmBoaMi2bdto2LAhM2fOxM3NjWHDhnHp0iUgv+sKYMKECdjb2yuvbdu2ce/evSLr96xyhRCiTERHR6tftvDw8DLNp1ar1StXrlQ3aNBArVar1Z9//rm6fv366pycHPXSpUvV9evXL3K9Bg0aqFeuXFlkesH6hb1cXFzUWVlZWus8evRIfeDAAbWHh4f6nXfe0UrPzc1V//rrr+q3335b3axZM3V6ero6Li5OXb9+ffXmzZvVv/32m8brzJkzyrqtWrVSz507t9C6FlauEEK8iOjoaPWFCxfUr13L4Xnk5uYyaNAgtm/fDsDQoUOVs/tBgwYRFhamlac4arWa3bt34+7uTlhYmMYrKCiI+/fvK/dizJkzRxkErlSpEp07d8bPz4+zZ88C8P3339O5c2fS09PR0dGhWbNmjB07lrt373Lz5k3s7OwwNTXlxo0buLq6Ki9bW1uNlpKOjg5qtVr5/KxyhRCiLJTbmMPz3OD2PHJzc5WDZ15envI+NzeXvLw8rTzFiYmJ4ebNm3z44YfK2EKBZs2aERISws6dO+ncuTOtWrVi7NixfPrpp3To0IHk5GR27tyJp6cnAB4eHsyZM4dx48YxYsQIcnNzWb16NTVr1sTGxoYKFSowYcIEPvvsM/Ly8mjXrh2pqamsWLGCLl26MHXqVABq1apFVFQUBw8exNPT85nlCiFEmSiPbqXCXLp06YXWfxndSjNmzCiy60itVquXL1+utrOzU6elpanVarX6+++/V3fp0kVtZ2en9vDwUM+cOVP94MEDJX9sbKx60KBBagcHB7WLi4t65MiR6suXL2uUGRYWpu7UqZO6UaNGak9PT/XixYvVjx8/VtIjIyPVnp6e6qZNm6qvXr1a4nKFEKI0CrqVVNHR0eq/niWXh/j4+BcaaF61ahXLly8v8vEZRe1TTEwMkyZN4v333y/1toUQ4u8qJiYGY2Pj8r+UtaxYWVnh4eEB5F/eaW1trfW+MB4eHlhZWZVLHYUQ4k31xgaH3r17l9lzlIQQQmh6o69WEkII8XJIcBBCCKFFgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKGlXB6fUdTjuhMTE7WW+fj4lLjckj6K+2kqlQpdXd0i08eOHcuBAweA/Dmqa9SoQfv27Rk7diyWlpbPta3ixMTE8P7777Np06YSzWsthBDlqdyerVSSg35J53zIzs5m1KhR/PLLL89dDw8PD0JDQ4vNY2VlRVBQEHl5eSQmJhIaGsrBgwfZsmULtra2z73NwlSvXh1vb2/MzMzKpDwhhChLb+SD906fPs0vv/xC3759qV279nOtW5InshoZGdG+fXvlc//+/enbty8ffvghO3fufO76FqZOnTosWbKkTMoSQoiy9sqCQ1BQEHPmzCnVutnZ2QD07duXli1blmW1CmVgYMCECRMYM2YMZ86cwdnZGYC0tDQ+/fRTwsPDAejSpQuzZs3CyMgIX19f7OzsWLFihVLOxo0b+eyzz4iOjua3335j+PDh7N+/Hzs7OwBu3LjB/PnzOX78OBUrVqRt27ZMnz4dc3NzpYx9+/axatUqEhISqF+/PlOnTqVdu3ZK+rfffsumTZu4ffs2devWZcyYMfTo0eOlf0dCiL+Xv/2AdFBQEA0bNtR4NW/enGvXrj1XOe7u7kD+WAHkB6ghQ4YQGxvL/PnzmTlzJkeOHGHatGkA9OjRg4iICHJycpQyDh8+jLe3NyYmJlrlP3z4kIEDByoBYtasWcTExPDee+8p053u2bOH8ePH07p1a7744gvs7OwIDAzkwoULAISGhrJgwQIGDx5MSEgIzs7OTJw4sVTdb0KIf7ZybzkEBQVpvS9tC6IkLl68SI0aNRgwYACQf3a+bds2bt68Sd26dUtcjpmZGQYGBqSkpACwY8cOLl++zA8//KDMaKenp8fkyZNJTEykW7duLFmyhOPHj+Pt7c3Dhw85ceIEixYtKrT80NBQ0tLS2LVrFxYWFkB+11Pv3r05f/48jo6OLFy4EF9fX2bOnAlA27ZtiYuLY/369SxatIhjx47h5OTE6NGjAfD29iYzM5OEhAS8vLxK9wUKIf6Ryj04FASCorqV4uPjC13vRaYUrVmzpjIt6PHjx9m2bVupynn6yqjIyEgaNGhA3bp1ycrKAqBZs2ao1WrOnDlDly5daNKkCYcOHcLb25uffvoJHR0dOnToUGjZJ0+exMnJSQkMAE2aNOHHH3/E0tKSK1eukJSUxFtvvaVsD8DNzY24uDgAmjdvzsKFC1myZAm+vr44ODiwcuXKUu2rEOKf7bUbkH6RIHD8+HEGDx4MQOvWrdm0aVNZVYuUlBQeP36sHLxTU1OJj4/H3t5eK++9e/cA6NatG2vXrmXu3LkcPnyYtm3bFtqlBHD//n2NsQXIv+y24Oqo1NRUACZMmKC1bsEltiNGjMDIyIgtW7YQEhKChYUF7777LgEBAcVeviuEEH/1yoLDy+hKunLlCrq6upiYmHDjxg0ePXpUZmUfP34cyD87h/wrmpycnDS6yQrUqVMHyA8OCxcu5MSJExw9epS5c+cWWb6pqSl3794tMt3IyAiAefPm4eDgoJFWoUIFID+YDBw4kIEDB5KamsqePXtYsGABAGPGjCnprgohxN9rQDokJISVK1dy4sQJzMzM+P7778uk3PT0dFasWIGzs7NypZKbmxsJCQnUrl0bV1dX5WVtba20AGrVqoWrqyufffYZ2dnZRXYpQX7QiYuLU1oIAAkJCXh6evLbb79hZ2eHqakpN27c0Niera2t0try8/MjJCQEAHNzc4YPH46DgwNnzpwpk+9BCPHPUW4th5Le4PYiVCoVT548Qa1Wk5eXh45O6WJfZmYmkZGR5ObmkpCQwIYNG3j48CFffvmlkmfw4MF89913DB06lHHjxmFhYcGPP/7I9u3biYiIUAJEt27dmDdvHl26dFHO/gszaNAgNmzYwMiRIxkzZgwqlYrPP/8cc3NzmjZtiq6uLhMmTOCzzz4jLy+Pdu3akZqayooVK+jSpQtTp07F1dWVlStXYmBgQOPGjTl16hTnz5+nX79+pfoehBD/XOUSHAq7Ozo+Pr7U4wsVK1YE8q8YOnHiBAD9+vUjMDCQCRMmYGRkhJmZGX5+fuzZs4dbt24RHBwMwM2bN59Z/s2bN/H390dPTw9LS0t8fHwYM2YMNWvWVPKYmpqydetWFixYwOzZs8nJycHZ2Zl169ZpjB34+voyf/58fH19i91m1apV2bp1K59++imTJ09GT0+PTp06MX36dGW8YPjw4RgbG7NmzRo2bNhAtWrV8PPzY/z48QDMmDEDIyMj1qxZQ2pqKrVq1WLKlCkMGTLkOb5dIYQAVXR0tNrNza3cN/wiwSE7O5sRI0YQFRWlLAsLC+PJkydaA9KzZ89m8+bNGuubmJiwZ8+e57qUVQgh/gliYmIwNjZ+M4NDgacfvKenp4darSY3NxcAHR0dpVvpyZMnGus9nSaEEOL/FQSH1+5S1ufx18szVSoVenrau1TYMiGEEEWT02chhBBaJDgIIYTQIsFBCCGEFgkOQgghtEhwEEIIoUWCgxBCCC0SHIQQQmiR4CCEEEKLBAchhBBaJDgIIYTQUi7PlSjqcd2JiYlaywp7gmt5Gjt2LAcOHCg0berUqbz33nulLnvt2rWEhYVx4MCBMnm2U/v27TE3Ny902tPU1FRatmzJu+++y8yZMwkICMDc3FyZ/EcIIYpTbg8dKslBvzRzPsTExCjvTUxMMDEx4datW0XmNzExwc7OrtgyraysCp3hrWDKzpLYunUrM2bMIDY2FlNTUwAaNGiAt7e3EhgKy/M8evTowapVq0hKSqJGjRoaaQcPHiQ3N5devXoB+ZMTVa5c+bm3IYT4Z3qjn0j35MkT+vfvr3z28PDAzc2N1atXF7mOh4cHoaGhxZZrZGRE+/bty6yeBdq1a0e7du3KrLxevXqxcuVKDhw4wLBhwzTS9u/fT/369ZWZ6wICAspsu0KIv79XNuZQ2Jn5m6JFixYsX76coKAgmjVrhpubG7NnzyY7OxvIn/JzxowZALi4uPCvf/0LgMWLFytzUBeW57vvvsPGxobff/9d2VZaWhoNGjRg7dq1WvWwsbHB0dGR/fv3ayxPS0vj+PHj9OzZU1nWp08fxo4dq3zOzc0lODgYT09PHBwceOedd0hISADg2LFj2NjYcPXqVSX/v//9b5o0aaLsI8C4ceM0gvO3336Lj48Pjo6O+Pr6snfv3uf4VoUQrxMZkC5EXl4eGRkZGq/Hjx9r5FmzZg13795lwYIF9OvXj82bNyt9/2FhYUycOBGA7du3M3XqVK1tFJanW7duVKxYkYMHDyr5IiIiUKvVRc4k17NnT3799Vdu376tLCvoUno6OPzVrFmzWLNmDYGBgQQHB3P//n38/f159OgRLVq0QF9fX5llD+Cnn34iPT2dkydPKsuio6Px8vICIDQ0lAULFjB48GBCQkJwdnZm4sSJ/PLLL0XWQQjx+ir34BAUFKS0Gp5+/zq5cuUKzs7OGq/hw4dr5HFzc2PFihV07tyZadOm4eDgwPHjx4H8sQlLS0sgf5zh6elFCxSWp3LlyrRr145Dhw4p+cLDw2nevDm1atUqtK49evQA0Ago+/fvx9nZmfr16xe6zh9//MF//vMfZsyYwbvvvkunTp344osv+PPPP9m/fz/6+vq4u7srwSE1NZW4uDicnZ0JDw8H4OrVq6SkpODt7Q3ktzacnJwYPXo03t7eLFq0CF9fX6U1IoR4s5T7mMOcOXOA/MBQ8P5p8fHxha5X0lnj3n77bVq2bFlkekkGfq2trVm2bJnGMhMTE43P9vb2qFQq5XONGjVITU0tUR2L06tXL8aNG8etW7ewsLDgl19+YfLkyUXmr1GjBu7u7vzwww/4+/tz9+5djh8/zrRp04pcp2B61Q4dOpCVlQVAtWrVsLa2Ji4ujj59+uDt7c369esBOHr0KA0bNmTo0KF88cUXzJ49m+joaCpXrkyTJk2A/G6yhQsXsmTJEnx9fXFwcGDlypUv/H0IIV6N125A+kWnDt2+fbvGgPSmTZvw9/dXPpdkQNrAwIDSTJ1aMGXpi+jQoQOmpqYcOnSIRo0akZmZWWSXUoGePXsya9YsUlJSOHLkCHl5eXTv3r3I/AVBzNPTUyvt/v37AHh7e7NgwQKuX79OREQEPj4++Pj4MH36dH7//XdOnjyJp6enMhvfiBEjMDIyYsuWLYSEhGBhYcG7775LQECA1ox9QojX3ysLDoW1GgRUrFiRLl26cPDgQW7evEnLli2pVq1asev4+voSFBTEwYMHOXToEB4eHkqXVWEMDQ3R09Pju+++0zpwV61aFYDGjRtjYWFBVFQUx44dY82aNVSrVo0mTZoQHh5OdHQ0Y8aMUdZTqVQMHDiQgQMHkpqayp49e5R7Kp7OJ4R4M8iA9EtScC9Dca2JovL06tWLmJgYfvjhh2JbAAUqV66Mt7c3YWFh/O9//yt2IBrA3d2dJ0+e8PDhQ1xdXZWXtbU19erVU/K1bt2aNWvWAODq6grkt2zCwsK4ceOGMt4A4OfnR0hICADm5uYMHz4cBwcHzpw588z6CyFeP+XWcijNDW6vSkZGBkePHtVabm1tjY2NTYnKKBhA3rhxI15eXsrBtSR5WrZsiaWlJXfu3KFz584l2l7Pnj2ZMGGC0vIojouLC2+99RaTJk1i/Pjx2Nvbc+HCBZYvX86qVato27YtkN+1tGvXLnr06IGeXv5PpUOHDixfvpz69etjZWWllOnq6srKlSsxMDCgcePGnDp1ivPnz9OvX78S1V8I8Xopl+BQ2N3R8fHxLzy+oKOjw4QJE5TPVlZW1KpVSzmQQf4B/a95niUxMVHr6iSAUaNG8dFHH5Wobq1ataJbt26EhISQlJRUaHAoKo9KpaJ58+ZkZGRQpUqVEm2vY8eOGBkZ4enpWaI7of/973+zfPlyvv76a+7evUvdunWZPXu2EhgAvLy8UKlUGjfu2dvbY2VlpdFqAJgxYwZGRkasWbOG1NRUatWqxZQpUxgyZEiJ6i+EeL2ooqOj1aUZfH1RZREc/q4yMzNp3bo1c+bMwc/P71VXRwjxDxITE4OxsfHrd7XSP1lWVhaHDh3i+++/x9DQ8JlXKQkhxMsiA9KvkfT0dKZNm0ZycjIhISFUrFjxVVdJCPEPJS2H14i5uTnnz59/1dUQQghpOQghhNAmwUEIIYQWCQ5CCCG0SHAQQgihRYKDEEIILRIchBBCaJHgIIQQQosEByGEEFokOAghhNBSLndIF/W47sTERK1lhT3B9a/mzZtX6J3EBU9M/eyzz7TSHBwcmDVr1jPLHjt2LAcOHABAV1eXGjVq0L59e8aOHVvsBDqlsWXLFmbNmsWZM2cwMjIqNM/48eO5du0au3fvLtNtv4hDhw7x3nvv8fPPP1O7du1XXR0hxEtQbo/PKMlBv6RzPpw/f54TJ05oLX/w4AFAoWnPw8rKiqCgIPLy8khMTCQ0NJSDBw+yZcsWbG1tX6hsIYR4E8izlQphZGRE+/btlc/9+/enb9++fPjhh+zcufMV1qz8FMxOp1KpXnFNhBCvwisbcwgKCnpVm35uBgYGTJgwgdOnT2tMe/nzzz/Tr18/HBwcaNGiBVOmTOHevXsa63777bf4+Pjg6OiIr68ve/fu1Sr/5MmT9OjRA3t7e7p160ZMTIxWntWrV9OyZUscHBwYPXo0t2/f1kg/c+YM/v7+ODk54e7uzqxZs3j48KGSrlarWbduHZ06dcLe3p42bdqwatUqjSlKW7RowdSpUxk4cCB2dnZcuHABgP/+97/4+Phgb29P//79uXjxolb9SrKfQog3x99uQNrU1BQPDw+tl4ODwwuV6+7uDqAcuGNjYxkxYgSNGjXim2++4ZNPPiE6OpqPP/5YWSc0NJQFCxYwePBgQkJCcHZ2ZuLEifzyyy8aZc+aNYuBAweyaNEisrOzGT9+PDk5OUr6uXPniIqKYvbs2UyZMoWTJ09qzG4XHx/PgAEDAFixYgUTJ05k3759jB49mry8PADWrl3L4sWLGTBgAOvXrycgIIDVq1ezefNmjbrs2LEDJycnVqxYgZWVFbGxsUycOJG6deuyZMkSOnbsqMwr/bz7KYR4c5R7t9LTLYaC93PmzCmz8p2dnQkNDS2z8gqYmZlhYGBASkoKABYWFnz77bd4eXmho5MfY5OTk1m6dCl5eXno6Ohw7NgxnJycGD16NJA/J3NmZiYJCQl4eXkpZS9fvpwWLVoA+a2UwMBArl27RoMGDQCoXr0669atU+Z3MDc3Z+LEiZw5cwZnZ2c+//xzzM3N+fbbb6lQoQIAdevW5Z133iE8PJyOHTvi6emJu7s7Li4uAHh4eHDs2DHCw8Px9/dX6tKzZ09mzpypfA4JCcHKyoq1a9cq068aGRlpDO6XdD+FEG+Ocg8OBYEgKCio0KAQHx9f6HolnVL03LlzLFy4UGt548aNNc7qS+PpLhgrKysePnzIxIkTOXfuHKmpqWRnZ/P48WNycnLQ19enefPmLFy4kCVLluDr64uDgwMrV67UKtfR0VF5X6NGDQBSU1OV4GBhYaEx8U+nTp0AuHjxIs7OzkRFRdGvXz8lMED+/M/VqlXjf//7Hx07dsTR0ZHDhw/j7+9PQkICDx8+5NGjRxrbLtjW086ePUuXLl005uX+a56S7qcQ4s3x2g1IlyQIODk5aRysClSpUoV79+4RGRmplfbkyZMXqldKSgqPHz9WDoxXr16lX79+dOzYkUWLFlG9enX27t3LsmXLlHVGjBiBkZERW7ZsISQkBAsLC959910CAgLQ1dUtdntPB6K/MjAwQF9fn5SUFHJzc3nw4EGhl9laWFhw9+5dAPbs2cPkyZOZMGEC06ZNo0qVKnzyySekpaU9c7+rVatWbJ4X2U8hxOvplQWHF+lKKq4FUFhgKAvHjx8H8s+SAXbu3ImxsTHLly9XruipUqWKxjoqlYqBAwcycOBAUlNT2bNnDwsWLABgzJgxpa5LZmYmWVlZmJubo6uri4mJSaEH+Tt37tCqVSsg/54KX19f3n//fSXd0NDwmcHBwsJCY2C7MC9rP4UQr84bOSA9b948Bg0apPV6+kqispSens6KFStwdnbG2dkZgPv376NSqTTO8OPi4jTW8/PzIyQkBMgfJxg+fDgODg7PXc87d+6QlZWlfD5y5AgA9vb2QP74wcGDB8nNzVXyHD9+nJSUFFq2bKlR3wLZ2dmFXnX0V87OzloDy+np6Rqfy2o/hRCvj3JrOZT0BreSeNZNcC8qMzOTyMhIcnNzSUhIYMOGDTx8+JAvv/xSydOuXTs2bdrElClTaNu2LeHh4crlmxkZGejr6+Pq6srKlSsxMDCgcePGnDp1ivPnz9OvX7/nqk9KSgrDhg3D39+f5ORkgoOD8fT0VALVxIkT6du3LwEBAQwdOpTU1FQWL16Mu7u7cr9G27ZtWbduHTY2NlhZWbF582Z+//13GjZsWOy2x4wZQ+/evQkKCmLAgAHcvHmTxYsXa+Qpq/0UQrw+yiU4FHZ3dHx8fIkHmcvbzZs38ff3R09PD0tLS3x8fBgzZgw1a9ZU8vj4+DB79my++eYb9u/fT+vWrVm2bBkhISEkJiZiZmbGjBkzMDIyYs2aNaSmplKrVi2mTJnCkCFDnqs+bdq0oWnTpgQFBfHgwQO8vb2ZP3++ku7g4MCWLVtYtGgRY8eOxcTEhK5duzJ16lTlSqqJEyeSkZHBhg0byMvLo3fv3nTs2JGDBw+Sk5OjMZj9tIKroZYuXcq2bduwt7dn5MiRSrcRUGb7KYR4faiio6PVbm5u5b7hFwkOgwYNKrTlsGnTJgCNSzMLeHh4vJRLXIUQ4u8kJiYGY2Pj1+9qpZIo6oY2U1NTID8QlHQdIYQQ2t7I4PCsp6tKC0EIIV7MG3m1khBCiJdLgoMQQggtEhyEEEJokeAghBBCiwQHIYQQWiQ4CCGE0CLBQQghhBYJDkIIIbRIcBBCCKFFgoMQQggt5fL4jKIe152YmKi1rLAnuJanwMBAfvzxR+Wzqakp9erVY9iwYfj5+ZW4nKysLOzt7fnkk0945513XkZVGT9+PNeuXWP37t0vVE5AQADm5uYaT1oVQvyzlduzlUpy0C/LOR9ehJWVFUFBQUD+xDZHjhxh0qRJJCUl8d57773i2pU9Nzc3Kleu/KqrIYR4jbyRD977q08++YTu3btT1KPHY2Ji2LdvX4mnJjUyMlImyQHo2bMnDx48YO3atX/L4BAQEPCqqyCEeM28sjGHgjPzF7Vy5Uo2btxIdHR0kXl+/fVXNmzYwKpVq0q9HTc3N9LS0khPT+f8+fPY2NhozVdtb2+vTJdZmLS0NCZNmoSLiwsuLi5Mnz6djIyMYre7efNmOnTogIODA76+vuzfv18rz5YtW/D29sbZ2Znhw4eTlJSkpKnVatatW0enTp2wt7enTZs2rFq1SmN60z59+jB27FgAfv/9d2xsbNizZw+jRo3CwcEBb29vtmzZorHNo0eP4ufnh6OjI97e3gQHB/PkyZNi90UI8eZ4owekDxw4wOeff06vXr2Kncg+MDCQ3r17s2LFCg4cOFCqbSUkJGBsbIyxsXGp1s/OzmbIkCHExsYyf/58Zs6cyZEjR5g2bVqR66xdu5ZPPvmErl278uWXX9KqVSvGjRvHTz/9pOS5dOkSYWFhTJ06lSlTpvDrr79qzBK3du1aFi9ezIABA1i/fj0BAQGsXr2azZs3F1vfjz76CGdnZ1asWEHjxo2ZPXs28fHxyjYDAwOxs7Pjq6++YujQoaxevZrg4OBSfTdCiNdPuXcrPd1iKHhf0u6ep509e5bJkyfj4uLCwoULn5l/wYIFJCQkMHnyZKytrXF0dCwyb15ennJGn56ezv79+9m1axejRo167noW2LFjB5cvX+aHH35QZsDT09Nj8uTJJCYmYmVlpZE/JyeHVatW8c477/Dhhx8C+fNAX716lS1bttCuXTsATExMWLduHWZmZgBcv36dPXv2KOV4enri7u6Oi4sLkD8R0rFjxwgPDy90xrwCAQEBjB8/XimjadOmREdH06hRI06cOEF2djZBQUFUqlQJLy8vnjx5wq1bt0r9/QghXi/lHhwKAkFQUFChQaHg7PSvnp5S9Pbt2wQGBlK1alVCQkLQ19fn008/RaVS8fHHHyvBYvr06cyfPx+1Ws3MmTP56quv8PPzIyAggJ07d1K9evVCt3XlyhWcnZ2Vzzo6OgwfPlw5SJdGZGQkDRo0oG7dumRlZQHQrFkz1Go1Z86c0QoOFy9e5MGDBxpjHwBLliwhMzNT+VyrVi0lMABYWlqSkpKifHZ0dOTw4cP4+/uTkJDAw4cPefToUbHBEcDJyUl5b2JigqGhoVJu06ZN0dXV5cMPP2Tw4MG4ubkxbtzh0bN0AAAFJElEQVS45/xGhBCvs9duQLqk80qr1WpUKhUqlarM62Btbc2yZcuA/MHsxYsX061bN3R1dUtdZmpqKvHx8djb22ul3bt3T2vZ/fv3ATA3N9dYbm5urrWsOHv27GHy5MlMmDCBadOmUaVKFT755BPS0tKecw/+X9OmTdm0aRNffvklI0eORFdXl549ezJlyhSqVq1a6nKFEK+PVxYcStOVVKB69ep8/fXXDBgwgMDAQEJDQ5k5c6aSPn36dOX9xx9/DOT3+QcGBnL37l22bt1aZKsBwMDAQLnyqVmzZuzcuZOlS5cq/fQFAenpQd1nMTIywsnJqdCB+Dp16mgtK5gP++7duyXeRmG2bNmCr68v77//vrLM0NDwhYIDQMuWLWnZsiWPHz/m6NGjzJkzh8TERDZs2PBC5QohXg9v7IC0k5MTS5cuJTY2tthB3QIzZswgNjaWZcuWaXSZPIuOjg4TJ04kKipKuTrJwsICQOOqoNTU1GKv1nFzcyMhIYHatWvj6uqqvKytrQttCdjZ2WFsbKx178fs2bOfqwvn/v37Gq2r7OxsLl68WOL1CzNnzhzl6qZKlSrRuXNn/Pz8OHv27AuVK4R4fZRby+Fl3ODWtWtXJk6cyIoVK2jUqFGRVyx99dVX7Ny5k/9r5w5aaVvDAI4/S6FTksLITCklIxlgYEDbnppKBgyUndIeyEBGyoS0k5SSSEopYwxMFV/BBzAwNNpx7uDm3PTkdNw697rd3+8DvOttDda/Z72tVa1Wo1wuf/o65XI5+vr6YnNzM0ZGRqKjoyN6e3tjZ2cnmpqaol6vx8HBQby8vHy4xtTUVJyensb09HRUKpXo7OyM6+vrOD8/j5ubmxSI5ubmmJ+fj1qtFi0tLTE4OBi3t7dxenoau7u7v7z30dHRODw8jO7u7ujq6oqTk5N4eHiInp6eT9+HN0NDQ7GwsBDr6+sxNjYWj4+PcXFxEcPDw397TeBr+Ufi8Dt/ibG4uBhPT08ffgAXETEwMBAzMzPvXq18RlEUUa1WY25uLq6urqJUKsX29nasrq7GyspKtLe3x+zs7E+/o2htbY2zs7PY2NiItbW1qNfr0d/fH4eHhx+eIVQqlfj27VscHx/H3t5edHd3R61Wi4mJiV/e+9LSUjw/P8fR0VG8vr7G5ORkjI+Px+XlZdTr9WhsbPz0/SiXy7G1tRX7+/txcnISbW1tUSqVYnl5+dNrAV9TcXd39/1nD1YA/j/u7++jpaXlv3vmAMDvIw4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AJOIAQCIOACTiAEAiDgAk4gBAIg4AvFMUhTgAkIkDAElDURT/9h4A+CLemmByAOCdoihMDgD85cfk0NBgeADgT29NMDkA8ENRFFEURfwBirKKPwVcjBEAAAAASUVORK5CYII=",
+ "description": "Overview of entities related to ThingsBoard Edge.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.onDestroy = function() {\n};\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-edge-quick-overview-widget-settings",
+ "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"showTitleIcon\":true,\"titleIcon\":\"router\",\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edge Quick Overview\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"widgetStyle\":{},\"actions\":{}}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json b/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..3257ab18920b02a949759292fac8f3212ae373b5
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json
@@ -0,0 +1,50 @@
+{
+ "widgetsBundle": {
+ "alias": "entity_admin_widgets",
+ "title": "Entity admin widgets",
+ "image": "data:image/png;base64,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",
+ "description": "Templates of complex widgets that allow to list and create/update/delete devices and assets."
+ },
+ "widgetTypes": [
+ {
+ "alias": "device_admin_table",
+ "name": "Device admin table",
+ "image": "data:image/png;base64,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",
+ "description": "Customized entity table widget with preconfigured actions to create, update and delete devices.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-entities-table-widget-settings",
+ "dataKeySettingsDirective": "tb-entities-table-key-settings",
+ "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Device admin table\",\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityLabel\":false,\"useRowStyleFunction\":false},\"title\":\"Device admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add device\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"\\n\",\"customCss\":\"\",\"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\\nopenAddDeviceDialog();\\n\\nfunction openAddDeviceDialog() {\\n customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\\n}\\n\\nfunction AddDeviceDialogController(instance) {\\n let vm = instance;\\n \\n vm.addDeviceFormGroup = vm.fb.group({\\n deviceName: ['', [vm.validators.required]],\\n deviceType: ['', [vm.validators.required]],\\n deviceLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.addDeviceFormGroup.markAsPristine();\\n let device = {\\n name: vm.addDeviceFormGroup.get('deviceName').value,\\n type: vm.addDeviceFormGroup.get('deviceType').value,\\n label: vm.addDeviceFormGroup.get('deviceLabel').value\\n };\\n deviceService.saveDevice(device).subscribe(\\n function (device) {\\n saveAttributes(device.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n function saveAttributes(entityId) {\\n let attributes = vm.addDeviceFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\n }\\n if (attributesArray.length > 0) {\\n return attributeService.saveEntityAttributes(entityId, \\\"SERVER_SCOPE\\\", attributesArray);\\n } else {\\n return widgetContext.rxjs.of([]);\\n }\\n }\\n}\",\"customResources\":[],\"id\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit device\",\"icon\":\"edit\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"customPretty\",\"customHtml\":\"\\n\",\"customCss\":\"\",\"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\\nopenEditDeviceDialog();\\n\\nfunction openEditDeviceDialog() {\\n customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\\n}\\n\\nfunction EditDeviceDialogController(instance) {\\n let vm = instance;\\n \\n vm.device = null;\\n vm.attributes = {};\\n \\n vm.editDeviceFormGroup = vm.fb.group({\\n deviceName: ['', [vm.validators.required]],\\n deviceType: ['', [vm.validators.required]],\\n deviceLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.editDeviceFormGroup.markAsPristine();\\n if (vm.editDeviceFormGroup.get('deviceType').value !== vm.device.type) {\\n delete vm.device.deviceProfileId;\\n }\\n vm.device.name = vm.editDeviceFormGroup.get('deviceName').value,\\n vm.device.type = vm.editDeviceFormGroup.get('deviceType').value,\\n vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value\\n deviceService.saveDevice(vm.device).subscribe(\\n function () {\\n saveAttributes().subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n getEntityInfo();\\n \\n function getEntityInfo() {\\n deviceService.getDevice(entityId.id).subscribe(\\n function (device) {\\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n ['latitude', 'longitude']).subscribe(\\n function (attributes) {\\n for (let i = 0; i < attributes.length; i++) {\\n vm.attributes[attributes[i].key] = attributes[i].value; \\n }\\n vm.device = device;\\n vm.editDeviceFormGroup.patchValue(\\n {\\n deviceName: vm.device.name,\\n deviceType: vm.device.type,\\n deviceLabel: vm.device.label,\\n attributes: {\\n latitude: vm.attributes.latitude,\\n longitude: vm.attributes.longitude\\n }\\n }, {emitEvent: false}\\n );\\n } \\n );\\n }\\n ); \\n }\\n \\n function saveAttributes() {\\n let attributes = vm.editDeviceFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\n }\\n if (attributesArray.length > 0) {\\n return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\\n } else {\\n return widgetContext.rxjs.of([]);\\n }\\n }\\n}\",\"customResources\":[],\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete device\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\n\\nopenDeleteDeviceDialog();\\n\\nfunction openDeleteDeviceDialog() {\\n let title = \\\"Are you sure you want to delete the device \\\" + entityName + \\\"?\\\";\\n let content = \\\"Be careful, after the confirmation, the device and all related data will become unrecoverable!\\\";\\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n function (result) {\\n if (result) {\\n deleteDevice();\\n }\\n }\\n );\\n}\\n\\nfunction deleteDevice() {\\n deviceService.deleteDevice(entityId.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n }\\n );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}"
+ }
+ },
+ {
+ "alias": "asset_admin_table",
+ "name": "Asset admin table",
+ "image": "data:image/png;base64,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",
+ "description": "Customized entity table widget with preconfigured actions to create, update and delete assets.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-entities-table-widget-settings",
+ "dataKeySettingsDirective": "tb-entities-table-key-settings",
+ "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Asset admin table\",\"enableSelectColumnDisplay\":true},\"title\":\"Asset admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add asset\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenAddAssetDialog();\\n\\nfunction openAddAssetDialog() {\\n customDialog.customDialog(htmlTemplate, AddAssetDialogController).subscribe();\\n}\\n\\nfunction AddAssetDialogController(instance) {\\n let vm = instance;\\n \\n vm.addAssetFormGroup = vm.fb.group({\\n assetName: ['', [vm.validators.required]],\\n assetType: ['', [vm.validators.required]],\\n assetLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.addAssetFormGroup.markAsPristine();\\n let asset = {\\n name: vm.addAssetFormGroup.get('assetName').value,\\n type: vm.addAssetFormGroup.get('assetType').value,\\n label: vm.addAssetFormGroup.get('assetLabel').value\\n };\\n assetService.saveAsset(asset).subscribe(\\n function (asset) {\\n saveAttributes(asset.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n function saveAttributes(entityId) {\\n let attributes = vm.addAssetFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\n }\\n if (attributesArray.length > 0) {\\n return attributeService.saveEntityAttributes(entityId, \\\"SERVER_SCOPE\\\", attributesArray);\\n } else {\\n return widgetContext.rxjs.of([]);\\n }\\n }\\n}\",\"customResources\":[],\"id\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit asset\",\"icon\":\"edit\",\"type\":\"customPretty\",\"customHtml\":\"\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenEditAssetDialog();\\n\\nfunction openEditAssetDialog() {\\n customDialog.customDialog(htmlTemplate, EditAssetDialogController).subscribe();\\n}\\n\\nfunction EditAssetDialogController(instance) {\\n let vm = instance;\\n \\n vm.asset = null;\\n vm.attributes = {};\\n \\n vm.editAssetFormGroup = vm.fb.group({\\n assetName: ['', [vm.validators.required]],\\n assetType: ['', [vm.validators.required]],\\n assetLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.editAssetFormGroup.markAsPristine();\\n vm.asset.name = vm.editAssetFormGroup.get('assetName').value,\\n vm.asset.type = vm.editAssetFormGroup.get('assetType').value,\\n vm.asset.label = vm.editAssetFormGroup.get('assetLabel').value\\n assetService.saveAsset(vm.asset).subscribe(\\n function () {\\n saveAttributes().subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n getEntityInfo();\\n \\n function getEntityInfo() {\\n assetService.getAsset(entityId.id).subscribe(\\n function (asset) {\\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n ['latitude', 'longitude']).subscribe(\\n function (attributes) {\\n for (let i = 0; i < attributes.length; i++) {\\n vm.attributes[attributes[i].key] = attributes[i].value; \\n }\\n vm.asset = asset;\\n vm.editAssetFormGroup.patchValue(\\n {\\n assetName: vm.asset.name,\\n assetType: vm.asset.type,\\n assetLabel: vm.asset.label,\\n attributes: {\\n latitude: vm.attributes.latitude,\\n longitude: vm.attributes.longitude\\n }\\n }, {emitEvent: false}\\n );\\n } \\n );\\n }\\n ); \\n }\\n \\n function saveAttributes() {\\n let attributes = vm.editAssetFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\n }\\n if (attributesArray.length > 0) {\\n return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\\n } else {\\n return widgetContext.rxjs.of([]);\\n }\\n }\\n}\",\"customResources\":[],\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete asset\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\n\\nopenDeleteAssetDialog();\\n\\nfunction openDeleteAssetDialog() {\\n let title = \\\"Are you sure you want to delete the asset \\\" + entityName + \\\"?\\\";\\n let content = \\\"Be careful, after the confirmation, the asset and all related data will become unrecoverable!\\\";\\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n function (result) {\\n if (result) {\\n deleteAsset();\\n }\\n }\\n );\\n}\\n\\nfunction deleteAsset() {\\n assetService.deleteAsset(entityId.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n }\\n );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..dc86c0f0034d9f9a714607769f4c36b48dd9998f
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json
@@ -0,0 +1,67 @@
+{
+ "widgetsBundle": {
+ "alias": "gateway_widgets",
+ "title": "Gateway widgets",
+ "image": "data:image/png;base64,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",
+ "description": "Widgets to manage ThingsBoard IoT Gateway."
+ },
+ "widgetTypes": [
+ {
+ "alias": "gateway_configuration",
+ "name": "Gateway Configuration",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to define gateway configuration for a single selected device.",
+ "descriptor": {
+ "type": "static",
+ "sizeX": 8,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "\n\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-gateway-config-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"Gateway Configuration\",\"archiveFileName\":\"configurationGateway\"},\"title\":\"Gateway Configuration\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "attributes_card",
+ "name": "Gateway events",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to browse events from the gateway.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 8,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
+ "controllerScript": "let types;\nlet eventsReg = \"eventsReg\";\n\nself.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n\n if (self.ctx.datasources.length && self.ctx.datasources[0].type === 'entity') {\n getDatasourceKeys(self.ctx.datasources[0]);\n } else {\n processDatasources(self.ctx.datasources);\n }\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, false);\n }\n else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasourceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasourceTitleFontSize = self.ctx.width/12;\n }\n datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nfunction processDatasources(datasources) {\n var i = 0;\n var tbDatasource = datasources[i];\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"| \" + dataKey.label +\n \" | |
\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n self.onResize();\n}\n\nfunction getDatasourceKeys (datasource) {\n let entityService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('entityService'));\n if (datasource.entityId && datasource.entityType) {\n entityService.getEntityKeys({entityType: datasource.entityType, id: datasource.entityId}, '', 'timeseries').subscribe(\n function(data){\n if (data.length) {\n subscribeForKeys (datasource, data);\n }\n });\n }\n}\n\nfunction subscribeForKeys (datasource, data) {\n let eventsRegVals = self.ctx.settings[eventsReg];\n if (eventsRegVals && eventsRegVals.length > 0) {\n var dataKeys = [];\n data.sort();\n data.forEach(dataValue => {eventsRegVals.forEach(event => {\n if (dataValue.toLowerCase().includes(event.toLowerCase())) {\n var dataKey = {\n type: 'timeseries',\n name: dataValue,\n label: dataValue,\n settings: {},\n _hash: Math.random()\n };\n dataKeys.push(dataKey);\n }\n })});\n\n if (dataKeys.length) {\n updateSubscription (datasource, dataKeys);\n }\n }\n}\n\nfunction updateSubscription (datasource, dataKeys) {\n var datasources = [\n {\n type: 'entity',\n name: datasource.aliasName,\n aliasName: datasource.aliasName,\n entityAliasId: datasource.entityAliasId,\n dataKeys: dataKeys\n }\n ];\n \n var subscriptionOptions = {\n datasources: datasources,\n useDashboardTimewindow: false,\n type: 'latest',\n callbacks: {\n onDataUpdated: (subscription) => {\n self.ctx.data = subscription.data;\n self.onDataUpdated();\n }\n }\n };\n \n processDatasources(datasources);\n self.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).subscribe(\n (subscription) => {\n self.ctx.defaultSubscription = subscription;\n }\n );\n}\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-gateway-events-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Function Math.round\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.826503672916844,\"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\":{\"eventsTitle\":\"Gateway Events Form\",\"eventsReg\":[]},\"title\":\"Gateway events\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "config_form_latest",
+ "name": "Gateway configuration (Single device)",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to create or choose the gateway device and edit the configuration.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 9,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
+ "controllerScript": "self.onInit = function() {\n}\n\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\t\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-gateway-config-single-device-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"gatewayTitle\":\"Gateway configuration (Single device)\"},\"title\":\"Gateway configuration (Single device)\"}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/gpio_widgets.json b/application/src/main/data/json/system/widget_bundles/gpio_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..e539824aca4c1b41300a694dd8c81ca2d38fa631
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/gpio_widgets.json
@@ -0,0 +1,86 @@
+{
+ "widgetsBundle": {
+ "alias": "gpio_widgets",
+ "title": "GPIO widgets",
+ "image": "data:image/png;base64,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",
+ "description": "Visualization and control of GPIO state for target devices."
+ },
+ "widgetTypes": [
+ {
+ "alias": "basic_gpio_control",
+ "name": "Basic GPIO Control",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAND0lEQVR42u2cCVBVZRvHqcmpGbdSZ+SCgIYhmhskiBqgCMqSGoaiDIvgAiiCmpk6NpbVpI5WblOpn7h8bohSosiOCMiOuI5rmvip+bmh4wYU31/ezzOny13OhQuN9P/NO845x3PPgfv+eLfzPMektra2urr61q1bV69evUJII4BCEKmqqgpSmcCqioqKysrKmpqaWkIaARSCSNAJUplAMezwSyHGAjpBKhM0X2yriHHbLUhlgq6R3wUxLpCKYhGKRSgWoVgUi1AsQrEIxSKEYhGKRSgWIRSLUCxCsQihWIRiEYpFCMUiFItQLEIoFqFYhGIRQrEIxSIUixCKRSgWoViEUCxCsQjFIoRiEYpFKBYhFItQLEKxCKFYhGIRikUIxSIUi1AsQigWoViEYhFCsQjFIhSLEIpFKBahWM3BEVfXQ126sCgpN5OSKBbFolgUi2JRLIpFsZpPrOSuXTMHDMh2ccl2ds6wt0+2smrSCk7p3h23y3JyyrCzw60pVgsUC3WcN3r0sZkzy2fNkkrZjBm5Xl5NUeWwtjgoSH6vY9HRBX5+aT17UqyWI1Z6796l4eHyapaX4rCwJBubX8zNNZZEc/MkgxpFC4tcb29t94LZ6YMGab1Xly4HzM3/uWJVVVU17OrPnj1rfrFSbWxKIyK01bQoBZMnb7Gw+JepqcayydR0u0qVqKzKcz09dd+rPCYm3s5O271QNqtUexunlw6xmqju/vjjj5qamgaK9eeff3711VdWVladOnWysLD46aefxPH58+e/VUfHjh3t7Ow2bNggjltaWh4/flx8cMmSJR06dHj99df79OmTnp5e/8fKyMgICgqaM2eOQWIlW1oW+fv/Jz6+wNdX27dcMG6cnpquK6ne3joqGyXW1PQXffWd3qePknuVRERs1u6xKDvNzDTeoiQ4+E5+/t3CwgvffmuQWEeOHHFwcEAdoSLwVT948AAHS0tL33qBtbV1eHj43bt3cfzjjz9etGiR+ODhw4f79u2LusM5ixcvRmXJL1tZWTlhwgQzM7POnTtPnDjxyZMnBov1448/vv322+fPn8f22bNncSGhSExMTHR0tPCjqKhIpVL9/PPP2G3Xrl1ZWRk2li9f3rt370uXLuGEffv24fi5c+fkVx4/fvzQoUPd3NwiIiIMEutYZOTlDRtu5+YWBwRo/IrTevRQUtNiDKS3smNVqoM6xVIoMUqii4vue6HUb7dSunV7dOUKBmqYdtwrK8v18FAo1vXr19u3b79161b8kT969MjPzy80NBTHCwsL4Zk45/fff//oo4/GjRuH7cjISLiFjYsXL6K+9uzZgwbp8uXL/fv3//rrr+VXnjVrVkBAQHV1NZRycnL67rvvDBbL0dFx48aN0u727dt3794tF0uAH2j69OmSWPhNzM3NU1NTpRPCwsJmz54tv7L46/nss88MFUuUa3Fx2sQ6MnSowppG2efg0IDKljefapMDHSVn4kS999qqUtUXC7+R2MafU5aDg0KxVq1aNWzYMGkXughv5GIB1Bf8k4v1ySefhISESCegYzE1NZU3Wmg1RPMhJNPW5+gSCz9BSUlJ/eNqYnl6en755ZeSWDdv3jQxMUGDKW/50DjVv05TiJX3wQfKxUoaPlxvZWvroZ4P5mxtld+rODxc771QtM0byqZOxW+tvCucOXMmar3+t6om1rZt22xtbeVieXh4/PDDD9IJDx8+RG2i/at/qcePH9vY2KDDNVisV155RXRhBw8enFaHGE5BLPR02EVTZG9vj2GW6KeFWBcuXHj11VfRbknX2bVrFzr75hHr6OjRBojl7t4YsdBDKb9XyfTpSsTS2PMe9fG5W1SESYlysQIDA/H1YuP27dvTXoBKgVgYPIndUaNGYfSMypWLhd5tx44d8ku1atVKbSQjmDx5MkxoyOAd477s7GxsnDlzJi4uDkM2DNaEWF5eXjiSkJCAwaDUTgqx7t27B8fRf8ubZbRqzSNWjru78sr+ZfBgvTUdp10srIcpv1deSIjee22u1xWiZA8Zcq+4ONPOzqBZIepo6tSp2MAACzW1bt06VApqCmK1adMmrg5U7v3798X5klg+Pj7yYRO8xAfxr9r10Ue5uro+ffq0IWL5+vqKmwkwQ5TEkneFEtLgHTPBTZs2Scfxs+KzzSNWer9+yit7m7W17prGuoPudaaSSZMU3kvvJFSbxI9+++3G/v2X1q1DKZsyRaFY8fHxmKRLSwbPq/mFWPKuUEISa+nSpcOHD5eOo/Xq2bOn2skYeaOnkqQ0WKwTJ05gwonB2unTpzMzMwcOHCi6bb1iHThwAK1dbGws5ozz5s3DUoXoK40l1mEnpzRbW23LlcXKKjvb319vTe/R3lz9f67g4qJQrB09eui+1xYtM9A8Ly+pZA0cqFAszOmcnZ3HjBlTUFBQXl6OITkm9aIr1C0WdOnWrVtUVBQ+iOkaPrV37175mYmJiahcdKAlL2jIAimUmjRpEpQaOXLk999/L/4CYIy8QZIIDg7+9ddfxTZEHDt2LDps/MQVFRUaL46x1/r1642+QJrRvz/WJHVXc2lU1HYbG92LWHv1WSUmhsUhIXqtSvPx0W3Vdn3rGg1YIEUn+MUXX7i4uAwZMgRjeVHLGAGLdQc1MHresmWL2L5x4wbOHzRo0IcffpicnKx2JjQY91fk4+kW/kjn8Pvvl0dH63jMkjRgAFojjSXe3DzBkKc6GFOXTZumw6r8gID4Ll203W5v3UMkPit8aR5CZ9rbl4SFaZj2h4TgSaKRgxqsrQv9/TU+zMFjxEOWlnwI3bLCZiwtM997L8/bG+vj+X5+GKNk6JxbNbKkv/suNIJhRcHBuF2OhwciLBg2w0A/BvpRLBaKRbEoFsWiWBSLhWJRLIpFsSgWxWKhWH+jWHich6cuqe+8c8jCgmJRLCOIleXoWBQQcOzFQ0M8Iizw92/SxXeK1cLFQqh44fjx2p4KH8VD+6Z/fkexWppYz1PEAgN1x7Ec9vNDuKbGgriobSpVvJlZEsVqOrHy8xH0EYBUiClTppw6dUocRDyWCMRBQOmCBQtEfljtX+Ox8vLy8L/u7u5z586VhykL7ty5g7Ay/C/ippElplwsRAOfWby4Yteuk3Pnahsz6c8grSv7nZ315swYJU2ZYqmTk5OD3KC1a9ciSnD16tWIPBQOIYIUIWBpaWmIAkPsMwJHEWtaK4sgTUlJwcmIs0YeIoL2kZwoT9oBiD5D2hCiCFesWNG9e3e1lEgdYl1YseLS2rUIpLyekFA+Y4bGTlBhSlZxRMQmMzM9bpmaUizji4UMiM8//1zahQorV66srReajMZMRLVKYiEgWh4aipZp2bJl0i7CUGEbkofE7htvvIHESIVi5Y4YIYKST3366blvvtEQ4jd4sPKY9z39+umNTt6nII6UYhkmFvJOc3Nz6x/XLRaCphG3j+xC6QRkfXh7e2u8Bdo2RMRrTPbQNsbKcXO7uHr1nYIChPJpME/7+znqlwOurvrTvzRlzlCsRomF9ECRUCbSvwAC7Gtl6V87d+5cuHChWleIpFu1vEKchqRqtYsjVRL5/6+99po8Z1qJWM9DQ4OC/puVVRoaqiEFb8wY5WIljxihV6x/Uyyji9W6dWuk2dS+SFhF0qmU/iUSVpEogcR+acAuxEIoPloskUQvQLcoT/eWQG+IfArkgWjM4dEoFtKtxBunkP51bffuBo/clb9PYSe7QqOLhTyNNWvWSLsK8woxEke2v/wNM3ilCbI+pF1kbeNNIdIu0tbwggCFYl1cteq32Nj8UaNuJCYej4nR0J45OioXa2evXnrFSngZJoYvmVjo7PAmGfFmIrxmKbCOWgV5hVAQefci4R95YJhanjx5Ut5QIRkXCxnYxrD9zTffvHbtmtLlBiur8qgo6FUSEqJ5EcvKqiwyUolVeEVWy+gHX8p1LLwTC80PxteofoyrxPRNr1jIlsT6Vtu2bbHo0LVr1/3796udiSM4jrYK2bp41Y5xF0gVJpHGDxigN9cviQukTbryjoleA97Nhz5RbflKDfk4zLiPdPJ9fXVble7pidZIW9mFN369JEujfKTTrM8Kn78XFKN4TTmrWD494ubGh9AUq+ElrVevPB8fpK1CJpTi0FC8MjlF+8uAKBbFYqFYFItiUSyKRbFYKFaDuLRmDUKvWJSUB2fOUCzyj4BiEYpFKBahWBSLUCxCsQjFoliEYhGKRSgWIRSLUCxCsQihWIRiEYpFCMUiFItQLEIoFqFYhGIRQrEIxSIUixCKRSgWoViEUCxCsQjFIoRiEYpFKBYhFItQLEKxCKFYhGIRikUIxSIUi1AsQigWoViEYhFCsQjFIhSLEIpF/l6xrl69WlNTw++CGAvoBKlMbt26VVlZya+DGIv79+9DKpOqqqqKigq4xXaLNL6tgkjQCRsm2K+uroZiaL6uENIIoBBEEi3U/wDz+OXgAWa1/wAAAABJRU5ErkJggg==",
+ "description": "Allows to change state of the GPIO for target device using RPC commands. Requires handling of the RPC commands in the device firmware. Uses 'getGpioStatus' and 'setGpioStatus' RPC calls",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 4,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
+ "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-control-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n \n var i, gpio;\n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n scope.gpioList = [];\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false\n }\n );\n }\n\n scope.requestTimeout = settings.requestTimeout || 1000;\n\n scope.switchPanelBackgroundColor = settings.switchPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioStatusRequest = {\n method: \"getGpioStatus\",\n paramsBody: \"{}\"\n };\n \n if (settings.gpioStatusRequest) {\n scope.gpioStatusRequest.method = settings.gpioStatusRequest.method || scope.gpioStatusRequest.method;\n scope.gpioStatusRequest.paramsBody = settings.gpioStatusRequest.paramsBody || scope.gpioStatusRequest.paramsBody;\n }\n \n scope.gpioStatusChangeRequest = {\n method: \"setGpioStatus\",\n paramsBody: \"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"\n };\n \n if (settings.gpioStatusChangeRequest) {\n scope.gpioStatusChangeRequest.method = settings.gpioStatusChangeRequest.method || scope.gpioStatusChangeRequest.method;\n scope.gpioStatusChangeRequest.paramsBody = settings.gpioStatusChangeRequest.paramsBody || scope.gpioStatusChangeRequest.paramsBody;\n }\n \n scope.parseGpioStatusFunction = \"return body[pin] === true;\";\n \n if (settings.parseGpioStatusFunction && settings.parseGpioStatusFunction.length > 0) {\n scope.parseGpioStatusFunction = settings.parseGpioStatusFunction;\n }\n \n scope.parseGpioStatusFunction = new Function(\"body, pin\", scope.parseGpioStatusFunction);\n \n function requestGpioStatus() {\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusRequest.method, \n scope.gpioStatusRequest.paramsBody, \n scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n for (var g = 0; g < scope.gpioList.length; g++) {\n var gpio = scope.gpioList[g];\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled; \n self.ctx.detectChanges();\n }\n }\n );\n }\n \n function changeGpioStatus(gpio) {\n var pin = gpio.pin + '';\n var enabled = !gpio.enabled;\n enabled = enabled === true ? 'true' : 'false';\n var paramsBody = scope.gpioStatusChangeRequest.paramsBody;\n var requestBody = JSON.parse(paramsBody.replace(\"\\\"{$pin}\\\"\", pin).replace(\"\\\"{$enabled}\\\"\", enabled));\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusChangeRequest.method, \n requestBody, scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled;\n self.ctx.detectChanges();\n }\n );\n }\n \n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n }\n\n scope.gpioClick = function($event, gpio) {\n if (scope.rpcEnabled && !scope.executingRpcRequest) {\n changeGpioStatus(gpio);\n }\n };\n \n scope.gpioToggleChange = function($event, gpio) {\n gpio.enabled = !$event.checked;\n $event.source.toggle();\n self.ctx.detectChanges();\n }\n \n if (scope.rpcEnabled) {\n requestGpioStatus(); \n }\n \n self.onResize();\n}\n\nself.onResize = function() {\n var scope = self.ctx.$scope;\n var rowCount = scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n scope.prefferedRowHeight = prefferedRowHeight;\n var ratio = prefferedRowHeight/32;\n \n var css = '.mat-slide-toggle .mat-slide-toggle-bar {\\n' +\n ' height: ' + 14*ratio+'px;\\n'+\n ' width: ' + 36*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb-container {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-ripple {\\n' +\n ' height: ' + 40*ratio+'px;\\n'+\n ' width: ' + 40*ratio+'px;\\n'+\n ' top: calc(50% - '+20*ratio+'px);\\n'+\n ' left: calc(50% - '+20*ratio+'px);\\n'+\n '}\\n';\n css += '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n\n cssParser.createStyleElement(namespace, css);\n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-gpio-control-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"Basic GPIO Control\"}"
+ }
+ },
+ {
+ "alias": "gpio_panel",
+ "name": "Basic GPIO Panel",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to display state of the GPIO for target device using latest attribute values. You should set the label of the selected data key to GPIO pin number (e.g. '1') and use boolean values for widget to display the data.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 5,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "\n \n \n \n {{ cell.label }}\n \n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n \n {{ cell.label }}\n \n \n \n \n
",
+ "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
+ "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-gpio-panel-widget-settings",
+ "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"color\":\"#008000\",\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"color\":\"#ffff00\",\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"color\":\"#cf006f\",\"_uniqueKey\":2}],\"ledPanelBackgroundColor\":\"#b71c1c\"},\"title\":\"Basic GPIO Panel\",\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"1\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.22518255793320163,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"2\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7008206860666621,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"3\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.42600325102193426,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 1000;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}"
+ }
+ },
+ {
+ "alias": "raspberry_pi_gpio_panel",
+ "name": "Raspberry Pi GPIO Panel",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to change state of the GPIO for Raspberry Pi device using RPC commands. Requires handling of the RPC commands in the device firmware. Uses 'getGpioStatus' and 'setGpioStatus' RPC calls",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7,
+ "sizeY": 10.5,
+ "resources": [],
+ "templateHtml": "\n \n \n \n {{ cell.label }}\n \n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n \n {{ cell.label }}\n \n \n \n \n
",
+ "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
+ "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-gpio-panel-widget-settings",
+ "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"gpioList\":[{\"pin\":1,\"label\":\"3.3V\",\"row\":0,\"col\":0,\"color\":\"#fc9700\",\"_uniqueKey\":0},{\"pin\":2,\"label\":\"5V\",\"row\":0,\"col\":1,\"color\":\"#fb0000\",\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 2 (I2C1_SDA)\",\"row\":1,\"col\":0,\"color\":\"#02fefb\",\"_uniqueKey\":2},{\"color\":\"#fb0000\",\"pin\":4,\"label\":\"5V\",\"row\":1,\"col\":1},{\"color\":\"#02fefb\",\"pin\":5,\"label\":\"GPIO 3 (I2C1_SCL)\",\"row\":2,\"col\":0},{\"color\":\"#000000\",\"pin\":6,\"label\":\"GND\",\"row\":2,\"col\":1},{\"color\":\"#00fd00\",\"pin\":7,\"label\":\"GPIO 4 (GPCLK0)\",\"row\":3,\"col\":0},{\"color\":\"#fdfb00\",\"pin\":8,\"label\":\"GPIO 14 (UART_TXD)\",\"row\":3,\"col\":1},{\"color\":\"#000000\",\"pin\":9,\"label\":\"GND\",\"row\":4,\"col\":0},{\"color\":\"#fdfb00\",\"pin\":10,\"label\":\"GPIO 15 (UART_RXD)\",\"row\":4,\"col\":1},{\"color\":\"#00fd00\",\"pin\":11,\"label\":\"GPIO 17\",\"row\":5,\"col\":0},{\"color\":\"#00fd00\",\"pin\":12,\"label\":\"GPIO 18\",\"row\":5,\"col\":1},{\"color\":\"#00fd00\",\"pin\":13,\"label\":\"GPIO 27\",\"row\":6,\"col\":0},{\"color\":\"#000000\",\"pin\":14,\"label\":\"GND\",\"row\":6,\"col\":1},{\"color\":\"#00fd00\",\"pin\":15,\"label\":\"GPIO 22\",\"row\":7,\"col\":0},{\"color\":\"#00fd00\",\"pin\":16,\"label\":\"GPIO 23\",\"row\":7,\"col\":1},{\"color\":\"#fc9700\",\"pin\":17,\"label\":\"3.3V\",\"row\":8,\"col\":0},{\"color\":\"#00fd00\",\"pin\":18,\"label\":\"GPIO 24\",\"row\":8,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":19,\"label\":\"GPIO 10 (SPI_MOSI)\",\"row\":9,\"col\":0},{\"color\":\"#000000\",\"pin\":20,\"label\":\"GND\",\"row\":9,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":21,\"label\":\"GPIO 9 (SPI_MISO)\",\"row\":10,\"col\":0},{\"color\":\"#00fd00\",\"pin\":22,\"label\":\"GPIO 25\",\"row\":10,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":23,\"label\":\"GPIO 11 (SPI_SCLK)\",\"row\":11,\"col\":0},{\"color\":\"#fd01fd\",\"pin\":24,\"label\":\"GPIO 8 (SPI_CE0)\",\"row\":11,\"col\":1},{\"color\":\"#000000\",\"pin\":25,\"label\":\"GND\",\"row\":12,\"col\":0},{\"color\":\"#fd01fd\",\"pin\":26,\"label\":\"GPIO 7 (SPI_CE1)\",\"row\":12,\"col\":1},{\"color\":\"#ffffff\",\"pin\":27,\"label\":\"ID_SD\",\"row\":13,\"col\":0},{\"color\":\"#ffffff\",\"pin\":28,\"label\":\"ID_SC\",\"row\":13,\"col\":1},{\"color\":\"#00fd00\",\"pin\":29,\"label\":\"GPIO 5\",\"row\":14,\"col\":0},{\"color\":\"#000000\",\"pin\":30,\"label\":\"GND\",\"row\":14,\"col\":1},{\"color\":\"#00fd00\",\"pin\":31,\"label\":\"GPIO 6\",\"row\":15,\"col\":0},{\"color\":\"#00fd00\",\"pin\":32,\"label\":\"GPIO 12\",\"row\":15,\"col\":1},{\"color\":\"#00fd00\",\"pin\":33,\"label\":\"GPIO 13\",\"row\":16,\"col\":0},{\"color\":\"#000000\",\"pin\":34,\"label\":\"GND\",\"row\":16,\"col\":1},{\"color\":\"#00fd00\",\"pin\":35,\"label\":\"GPIO 19\",\"row\":17,\"col\":0},{\"color\":\"#00fd00\",\"pin\":36,\"label\":\"GPIO 16\",\"row\":17,\"col\":1},{\"color\":\"#00fd00\",\"pin\":37,\"label\":\"GPIO 26\",\"row\":18,\"col\":0},{\"color\":\"#00fd00\",\"pin\":38,\"label\":\"GPIO 20\",\"row\":18,\"col\":1},{\"color\":\"#000000\",\"pin\":39,\"label\":\"GND\",\"row\":19,\"col\":0},{\"color\":\"#00fd00\",\"pin\":40,\"label\":\"GPIO 21\",\"row\":19,\"col\":1}],\"ledPanelBackgroundColor\":\"#008a00\"},\"title\":\"Raspberry Pi GPIO Panel\",\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"7\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.22518255793320163,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"11\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7008206860666621,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"12\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.42600325102193426,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"13\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.48362241571415243,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"29\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7217670147518815,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}"
+ }
+ },
+ {
+ "alias": "raspberry_pi_gpio_control",
+ "name": "Raspberry Pi GPIO Control",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to display state of the GPIO for target Raspberry Pi device using latest attribute values. You should set the label of the selected data key to GPIO pin number (e.g. '1') and use boolean values for widget to display the data.",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 6,
+ "sizeY": 10.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
+ "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-control-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n \n var i, gpio;\n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n scope.gpioList = [];\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false\n }\n );\n }\n\n scope.requestTimeout = settings.requestTimeout || 1000;\n\n scope.switchPanelBackgroundColor = settings.switchPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioStatusRequest = {\n method: \"getGpioStatus\",\n paramsBody: \"{}\"\n };\n \n if (settings.gpioStatusRequest) {\n scope.gpioStatusRequest.method = settings.gpioStatusRequest.method || scope.gpioStatusRequest.method;\n scope.gpioStatusRequest.paramsBody = settings.gpioStatusRequest.paramsBody || scope.gpioStatusRequest.paramsBody;\n }\n \n scope.gpioStatusChangeRequest = {\n method: \"setGpioStatus\",\n paramsBody: \"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"\n };\n \n if (settings.gpioStatusChangeRequest) {\n scope.gpioStatusChangeRequest.method = settings.gpioStatusChangeRequest.method || scope.gpioStatusChangeRequest.method;\n scope.gpioStatusChangeRequest.paramsBody = settings.gpioStatusChangeRequest.paramsBody || scope.gpioStatusChangeRequest.paramsBody;\n }\n \n scope.parseGpioStatusFunction = \"return body[pin] === true;\";\n \n if (settings.parseGpioStatusFunction && settings.parseGpioStatusFunction.length > 0) {\n scope.parseGpioStatusFunction = settings.parseGpioStatusFunction;\n }\n \n scope.parseGpioStatusFunction = new Function(\"body, pin\", scope.parseGpioStatusFunction);\n \n function requestGpioStatus() {\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusRequest.method, \n scope.gpioStatusRequest.paramsBody, \n scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n for (var g = 0; g < scope.gpioList.length; g++) {\n var gpio = scope.gpioList[g];\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled; \n self.ctx.detectChanges();\n }\n }\n );\n }\n \n function changeGpioStatus(gpio) {\n var pin = gpio.pin + '';\n var enabled = !gpio.enabled;\n enabled = enabled === true ? 'true' : 'false';\n var paramsBody = scope.gpioStatusChangeRequest.paramsBody;\n var requestBody = JSON.parse(paramsBody.replace(\"\\\"{$pin}\\\"\", pin).replace(\"\\\"{$enabled}\\\"\", enabled));\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusChangeRequest.method, \n requestBody, scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled;\n self.ctx.detectChanges();\n }\n );\n }\n \n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n }\n\n scope.gpioClick = function($event, gpio) {\n if (scope.rpcEnabled && !scope.executingRpcRequest) {\n changeGpioStatus(gpio);\n }\n };\n \n scope.gpioToggleChange = function($event, gpio) {\n gpio.enabled = !$event.checked;\n $event.source.toggle();\n self.ctx.detectChanges();\n }\n \n if (scope.rpcEnabled) {\n requestGpioStatus(); \n }\n \n self.onResize();\n}\n\nself.onResize = function() {\n var scope = self.ctx.$scope;\n var rowCount = scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n scope.prefferedRowHeight = prefferedRowHeight;\n var ratio = prefferedRowHeight/32;\n \n var css = '.mat-slide-toggle .mat-slide-toggle-bar {\\n' +\n ' height: ' + 14*ratio+'px;\\n'+\n ' width: ' + 36*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb-container {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-ripple {\\n' +\n ' height: ' + 40*ratio+'px;\\n'+\n ' width: ' + 40*ratio+'px;\\n'+\n ' top: calc(50% - '+20*ratio+'px);\\n'+\n ' left: calc(50% - '+20*ratio+'px);\\n'+\n '}\\n';\n css += '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n\n cssParser.createStyleElement(namespace, css);\n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-gpio-control-widget-settings",
+ "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#008a00\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":7,\"label\":\"GPIO 4 (GPCLK0)\",\"row\":3,\"col\":0,\"_uniqueKey\":0},{\"pin\":11,\"label\":\"GPIO 17\",\"row\":5,\"col\":0,\"_uniqueKey\":1},{\"pin\":12,\"label\":\"GPIO 18\",\"row\":5,\"col\":1,\"_uniqueKey\":2},{\"_uniqueKey\":3,\"pin\":13,\"label\":\"GPIO 27\",\"row\":6,\"col\":0},{\"_uniqueKey\":4,\"pin\":15,\"label\":\"GPIO 22\",\"row\":7,\"col\":0},{\"_uniqueKey\":5,\"pin\":16,\"label\":\"GPIO 23\",\"row\":7,\"col\":1},{\"_uniqueKey\":6,\"pin\":18,\"label\":\"GPIO 24\",\"row\":8,\"col\":1},{\"_uniqueKey\":7,\"pin\":22,\"label\":\"GPIO 25\",\"row\":10,\"col\":1},{\"_uniqueKey\":8,\"pin\":29,\"label\":\"GPIO 5\",\"row\":14,\"col\":0},{\"_uniqueKey\":9,\"pin\":31,\"label\":\"GPIO 6\",\"row\":15,\"col\":0},{\"_uniqueKey\":10,\"pin\":32,\"label\":\"GPIO 12\",\"row\":15,\"col\":1},{\"_uniqueKey\":11,\"pin\":33,\"label\":\"GPIO 13\",\"row\":16,\"col\":0},{\"_uniqueKey\":12,\"pin\":35,\"label\":\"GPIO 19\",\"row\":17,\"col\":0},{\"_uniqueKey\":13,\"pin\":36,\"label\":\"GPIO 16\",\"row\":17,\"col\":1},{\"_uniqueKey\":14,\"pin\":37,\"label\":\"GPIO 26\",\"row\":18,\"col\":0},{\"_uniqueKey\":15,\"pin\":38,\"label\":\"GPIO 20\",\"row\":18,\"col\":1},{\"_uniqueKey\":16,\"pin\":40,\"label\":\"GPIO 21\",\"row\":19,\"col\":1}]},\"title\":\"Raspberry Pi GPIO Control\"}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c9a9fe718bbe4efd58afe9fda89a6927f51419e
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json
@@ -0,0 +1,505 @@
+{
+ "widgetsBundle": {
+ "alias": "input_widgets",
+ "title": "Input widgets",
+ "image": "data:image/png;base64,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",
+ "description": "Various input forms that allow users to set location, image and other configuration parameters of the target entity"
+ },
+ "widgetTypes": [
+ {
+ "alias": "markers_placement_openstreetmap",
+ "name": "Markers Placement - OpenStreetMap",
+ "image": "data:image/png;base64,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",
+ "description": "Allows configuring location of the selected entities on the OpenStreetMap.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8.5,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7867521952070078,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7040053227577256,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"defaultZoomLevel\":5,\"provider\":\"openstreet-map\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${coordinates|ts:7}
Delete\"},\"title\":\"Markers Placement - OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"54c293c4-9ca6-e34f-dc6a-0271944c1c66\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"6beb7bed-dfd8-388d-b60c-82988ab52f06\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}"
+ }
+ },
+ {
+ "alias": "update_multiple_attributes",
+ "name": "Update Multiple Attributes",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAXDUlEQVR42u2dB3tURReA+Ss27EpAEUSpH2ABAZUmRHq1AAJKUSCU0KuAoSi9hlAEpPdQQ+8QIpBQQ0uoIQnme7NHx8u9u5ttuLvknCdPnlvmzp07886ZM7NzZkoUFBTk5eVlZmamp6efU1EJQkAIkHJzc4GqBFRlZGRkZ2fn5+cXqKgEISAESOAEVCVAjBPNFJVQCTgBVQnUl+oqldDqLaAqQdOoeaESWgEqBUtFwVJRsFQULAVLRcFSUbBUFCwVFQVLRcFSUbAClL+8imaxghUgT4+8ihKmYPlNFdzkuyTPJbkWkSty1+Cl2a1gFUGVICUwPXz4MCcn54FDuMgtgczgpTmuYBVBlSAFQPfu3YMh6DGaiQNOucgtAgheypaC5RNVws39+/e54ik8twgg5ClbClbRVImiQhX58iDBRHUpWwqWe2vd6Kq7d+8Ciu+v2bdv3y+//DJ58uQpU6ZMnTr1V5f89rjMnDlz2bJlhPQrZpWoB0ssJ1E/gOXvm1auXJmQkABbnsAyMmvWrLNnzwb5YdSBO3fuhCqbpk+ffvr06RDmOzkpB4sWLTpw4ECxBss0grdv30Zp+fsmJtgD1qRJk4zS+s2rBMzWpUuX2rRpU7JkyWeeeebdd9+dN29e8Nn01ltvoU1DlelHjhx58cUXr127xnHt2rXR5cUULGNdoa7QVdevXw/sZX/88Yc0iEUqLdFbgbWJtWrVatu27eXLl0nt6tWrX3rpJd4bUWCRsO3bt4uhqWD9PYKQlZWFA0bANXXChAk+goXs37/f31fcuHEDRbVnzx5zpUePHp07d5bjW7duwSsFaVqfCxcuLFmyhOsotokTJ1rbOzQKZh9J5XudYFG7eITc4P+YMWMOHTrExa1bt3LMFWuV4F0///wzX52ammoSOXfuXPHqVLAKwSK/bt68KTo8ALly5QpZTGvoI1gBKAnUatmyZbt37+5srPnM8uXLt2rVqm/fvm+88cacOXO4uHbt2ldffbVu3bo//vhjbGws6k2qDQqPZrROnTo9e/asUaMGLZctMeACwZ999hmxNWvW7Pnnn+/YsePnn3/OaYUKFZo2bSrBgLV06dKDBw8mSUQixB8+fJhnsSgUrEKwxMACrAAMLBEeFLAws3wBC+0SwFtSUlJgokyZMpTxiRMnzPV27dp99913ckwTGRMTwxcBFkwcPXpUPrNSpUrY6Rz369fvk08+EXdLIHvllVfcgmWUUG2XyJDe3r17uYUW5BieduzYIWG+/vrrbt26KVjuwUKNBwwWj/8HYAnBv//+e8uWLV944QXUCUXIJ7z88su9evWa6xISQNHy4aKxzIONGjUaNWoUB1BlLW9nUyhgmd4x6oo2V44BkVtnzpyR02PHjs2ePZvY0GfoSwXLI1h4Twf2MnLcL7CCt5fPnz///vvvx8XFgdqzzz7buHHjryzy559/egKrcuXK0lb6CBax0Wg6waIRJAHECVtNmjRRsLw1hVTBwF62a9cuv8AKwHhfsWJFzZo1rb8yYTyJxYOBtXDhQlt4T2ABQXx8fJBgMZD23HPPiV2P9O/fX8HyZrzT9wnsZfSMfDfeqeIBDDegTd98800sJFnmBBsLnsaOHcvx6NGjK1asmJaWJkULCphQnsBKTEzECCMY37548WK0XQBgMZJMW8xnEgmG1zvvvEO7rGC5H27AJqXei2Xql9CXxHodP368j8MNAc9rxRJv2LAhqoIuHkY3ekKGuRk9ok2kawYx9NSSkpK8aCyYg06wIJIOHTrQ0QusKZw/fz7xc1q9evVOnTpVqVJFwXI/QMrIzYIFC7Zs2eLvmxjBooR8GcdCVwU/DR9tQQE7Z17wFYx6+Li6DlMzAvjxyiZgHfCQ8lMOVoHlJx3sBuo6HSvpovs+5k5vn2pN7fT0kw59QLQCdpX+CF28wDKt4caNG2GLTpOPbBGMwDzCg9YpgZr7CtZj02bQKCBCg0ibxYHYCm6FWwQgGIE5MFOydAq8guXG0pIGkZ/YsOLnuGTz5s30tjC/xIeCA065KHcJRmAe0Yl+ClbRVvy2bdukTeQ3V0wuAJptEU65yC1pAQnMI0qVguWT3sJgQi2hjRL/kYUuMafcIoBOeFew/GNLJpQmJyejkLY6hIvcIoC6fylYfrClDqsqoQerQF3sVZ4QWE7CdFEQlVCCpaKiYKkoWCoKloqKgqWiYKkoWCoqCpaKgqWiYKmoKFgqCpaKghUS4afrJJfgtIPzdMBLRTgFZ0B8gVgRRJYQsgpeqUmPC/5CvJrpil5W7/Xr1bijKVjhFAqSldMofuY044TYu3dvLwsncWvAgAG+RMtKDSz2QgETJxjZ7srkRKa/du3aVY5ZK4tZisuXLw8SLCKhquBOwtIEZoVIBStsYOHLL6esGzNu3Dg5pmBYCmHTpk0smFbgWkht/fr133zzDQ7ssoQzjkAsG7Rz506noynTWfHDloMZM2a4fTUu+az1YE5RbCy2K9HivozfKcCJ3z3LaMG9dRkLwrBOH6/GV9b23p9++gmHbDz9WZHGfJeCFWaw8CHD2VVcx4YMGQITKDN0D+6KV69epanCD5YrsjgqRYgHB47trFhkK2CwYH0sQCQMztC+gMUCELy6wLVkHG+BbyhHg+KGzyIUHP/www/iFE5IHpznEgLgQm2NlnR26dJlxIgRqrEiCKwC18oIFC3cYHLJFdZgZh1HKXKzEqS4aMsxrtUwZIsWIACFtY08vdoLWOhFsczQYTSX4qSPAmPRgALX4sfQLE/h5I0ys0YLhVhscCn6T8GKCLAoTkpXliE5ePAgDQqLTqEqKD8bWKwnwyp+o12C2jCQiWBaQRsabuTIkWBBJM4lFbyAhcoxYXiLHLP0I+/igDhZRERejbI0kImIguQTQrhCuIIVLFjHjx///vvvMWuwq+BJDPl169Y5wUJ5rFq16to/YnXRBpH27dvTyyMegrGUElrHaZUHDBbW25o1a8yriw9AUQkWENCVo6TFjkFdUaLiFESzggaSXiENpSCCD+PQoUNleAITmwXfTZw8xSpCsjgWixLyCNrLr6bQO1i0fcOGDZMOBN2LYjWyEGVgwQGqiKEE+lmmS0g7iKZBgbFqzaBBgyQwYdBktGsc0/yxnizmOWa+rf8FaixhRe+MAJhERMLy4KECi1fj/03MPM6rA1g/TMEKs9DXszVhnFoHF+DPS0vErSfnO8Srg19PS8FSUVGwVBQsFQVLRUXBUlGwVBQsFRUFSyWKwOrukuDDqChYCpbKEwOLn8wGDhzY3U/hEevmlCoKll0CoEpEfjxWUbC8tW6Biea7ghU2sJh5whwY5onLZvHWW8wgYAV5ptAQwNN2r0uXLrVNQWYWDc4OtmDMoCIk8z+ZniUOGk5hc02zDy+pYsoNr2aOqG1aDlMbmAXP7GdmINpeLRsjMsGQt5w6dcr5CiZc7N6928dNwpjfgTcROYMrm81/iblouBUxWY05PBkZGZ5iYD4IMyWZMM30bma82e4yi5p5i3yj2c366QELHxg2o2fnSPZ1fvvtt9nM8uLFi8a2K++S1q1bsyvua6+9RpHYHocDthoU5ijUDRs2sKNdyZIl2TfVVkJsF8iO9s2bN2ebcTY3lF3mrQJt7EYuW9uT42y1ykt5NQkoVaqUmdEFoOxoT2KIqmrVquyKaLYLZaJO3bp1X3/99RYtWtSqVYvYpk2bZuKn+ClC3s4OhrimFZkzzFIkMPstEht7qpMGQypJZcdhUkjySAM7LTKp1RkDXif169cnPWz3ytbXZBS11NyFfnb95FvY85GkMt86PGA9oaeYVffee++JokIxUIrmqTp16rBrt2w9RwUlaz7++GPb48xnb9OmjRzjccU2499++22NGjVsYDEHFdpEGzGzvkGDBhS8LaoxY8aYpygttkgVlwqgZG9LNs6UW2gIikG8wUD5yy+/rFatmtxiaiEcmOmsTManLGUDWBQbG3aSML7OR7AwbYFJZqiSBp6FMLkFKCRJJmSTBpJKzXTGgD6DKqPPmLRI1RLfEOYzkjaZuigfBWSBdbYiFCy0FDN9zSn2PnucSiPIp+L8aa1hqAfr3D2aJLQdTY+colQkrykAG1hkvSkVhIpL6Vonf9JkoJ9kb1XZN5WNF81dZoqWKVNGjplQTxU3t3BHI7DA9NFHHzFb1dzC0YhbslE0eInOQ1P6CNaHH35ozUDqBppJVj5HX4rLkAi5RJxSOZmTzZxboQdc2DLSBMOXiWDSdtOUc2xaZOotWlx8C54GsCCDeoxpZa7gS4g+cLstKgqAkrNeASm4dHrwOcH64IMP+vTpY05TUlLIVmsFxdeUHaY9bcyJFjQwffrpp9bvwueRqNw6e+Gjhpq0zV32HSyUH7aROQV0KwpWwdpDY0mVgyoqpFuri4YYBSY+mGBq3cgYodlFRz4lYGEbkVnUHmthcMXpQ0z5UV9ld2cjNII0hc5onWCh2HC+sI7M8RargQ86TJ93m0gohHWjFykAqzmCAiAqDGTbU6goQuIfZrvuI1hoUPiwqhAsOR5kNQBbSBJAlUhISDB11dY14V2oLlICSWa7a3LD1npiG+BkEAVg0TrY/EidQkvvC1goefbuxrCwtoP4HGMlpKam+gIWdq4TLLSUiR9qnQ4XBS7XIMw+3D3MlYoVKzrBMtgZLGgxCWnr5PoOFjqbYE6wbD1NVCymJ0rUi+P19OnTgQb4sNLMe6mQTrBwH48CsHwRACKzrF0VvEy5YnWZJ++wtWnLbH4T1FF6NG6jdYIFHPHx8eYU05u3sAyEnNIVtzWyZrCDvmS9evWsTSSmutW9hz48UZmOobHiKUi3ww0+gkUVotqI35sIHm/GQjLw4eNE14c6VmRW05nF/sN4l0rLEIaxGo1J59SvkQIWDRZtfG+XUPDWhTQ8ZR+qwur3x1PYAda8k6pvW4KBBylgmxeyF7CAw/oJUvtNIUGP0/eQLmFsbCy13KY+v/jiC3wVzSl0EtXJkyfNFXKAzqlzOMNfGwtirFpWdLl1qQhc3NDEztEpT4Jdb5Sr9G2tTlAoMPrFkQgWiyw4x7HwV/b+FKqItRLMKaNQFLPVYMc8dyYbuweLwZMDlhMslLwZFJB+OJ0gcTqlMtDZtNFDjtP8UbROf1TUFf1WUyRTp05FtZiUiMHOcJqn7/UdLLA2YxwInQ/0rnVwhBrotvk2wuPYD+aUwLyarOMY+5JjhuvMwBi9KGvTESlgUTwGprUuMac4znt5EDOCkmAVK8YD+U8hmUFFejHUKvrSRywiJe09SU6wGFkmHykMbCaO6XCZcQFGd6wayIwhkSpMXeurpcWBCcxqBiDoeUAJFd1UDFor0o9XrfUp8cn2AhZuuk2bNnVWEkxPXkRukDPkJxVp+PDhcovtkrnFUIL1RdITxFmXhlhG4Bivl0431YYxvMaNG1O7xH0cm4y+BYocIxWqGJ2OiYnxspl82MBC/3sCy3RYPFmp1EVKkezmf1xcnBlrQFc94xCsTsqAXPbU1rgFq8C1KA3DP8RAXtOdFIuNXCZDAcIWmAba+WrT+ef3JYwVrlC6jL8bbUer7XzKZgg6wZJBNdtKTGYcgVaVu1QwlK6oWBkwc75ITG/RoFL9ZPUKFJsklUbcSjk9mJo1a8qzGBvOXzUiAizaLCtYtAXm1GrqevlRDIvHttaUJ0GHMTIegJcz1ZRfbGQo3GgFfi1xO2xWZFRoiJD411NV+H3Gi9FNzvjldW1jlIyCM7fgSsecu8G4jP93YPG7JvoAReU7WMVWYLpcuXL8Oh69n/BkwTIYidAywhY9fI5ZSEgB8iI+znR4CsHyPtFPRkEZWbBdF6qKNN5Vol2CmprMb8OewKIPIuOHjCw47/IbrWa9ghWsoJxoE2WAlBZQdZWCpaKiYKkoWCoKlhsZr1IsRTWWijaFKgqWioqCpaJgqShYKioKloqCpaJgqagoWCoKloqCpaKiYKkoWMVPDqderN5+ZEzDfqUaRMEf6fxfu5HJB84oWBEth05fiGkYFxVIPYZXg36kXMGKUMEXtHq7EVFHlfzVaDeiSF9WBSs8gs90TIO+UQpWqfp9vay8pWCFU1ifI1qpcv3J+iIKloKlYClYCpaKgqVgKVgKloKlYKkoWAqWgqVghU+SD6T+tjQ5Ny8/ksF698v4txr3V7D+O7mfk1uxxZDe4xebK5tTTnJl+ZaDPsbQL2EZGXfvwcMIBKt0w7jx8zfcyCpcejQ//9Hmvac+/Gq0gvVfCEDw2V1H/Ls92Lqdx7iStH7fUwDW2DmFm/Os2HqIDxw1a+3d+w/PXrhWJhyqS8F6DKxlmw7sOXo2Nf3qtGXJC9bssdKzdd/pyUlbth9ItYF16FTGlKSt81btPn/pRtjB2n/i/IOcXDPNZuLCTdl3H8T2msJx2SYDeo1LmpS4eeCUFe+3GMKVxj0mDZi8vErrYRK409C5fSculePa344bM3vdyJlr6nf7RcEKAVjvNR/8QYdRFZrF85+LLfv8vaMJWcxp5VbDyscOqtBssAFrwvyNHNfsMKpSy6Flmwxcv+t4eMFaue0wzw6fvprEWK/zRafPX8l5mAd5pPzqjdtV2wxv238GgQf/upIAGGR37uVs23+a487D5j3Mzac9zbx5Oy//0Q9jEhWsUIDVcbTYKJKh17PuZly5WbpR3NeD5zx69Bf6oNH3CQIWig3d0GPsIqaIYLqhACq1GspBGMGq0X7k6fOFG2Hcun3vtyXbjIHVJm76rsNpHQfN4pgPIQB6C4MMdLYfPCMBuIjp+fYXA25m3zucegE0aUP3HT8HhQHMNFSw7GCR+3KdBo7rVPQ1O45ysH73cZuNNXP5Dg4OnEyX64lrUzjdd+J8eHuF4NJp2Lydh9LAnZogupY/dG3PcUkTFmxcvKHwSxMSN3Nx1oqdKCe6kHwL+gzF1vynwi3p+FhaSf7o2XBavd1IBasIQeXw2V2G/7sBmHCzZMN+T2DBHAfYXjawJrnKButYrq/efqSQvxC1hsGPY3363YTjf14Cr1rfjEUNo3guZWZhC1rBatKzcAM6qtnZi9elgok+u5h563jaJfNXt/PPClbRUq3tiM+7TjSnmORkxN5j5zyBteNQYWORuO7vbQrFwgWsVclHrJqMfj6nJ89eDhdYJB6MGGYzV8bNLVxMH1uKtHFQv9tEAc6AxV/65RuiZcGL07pdChfmo+9iIgmsU1kcwYqfWmiuDpy8nDKgBtNFwhahIfAEFo0FtgsWOgwtWre3XOwgAQtziut0oIgHq5lnm/aaEl7jHe0LW2DBV9DwXb6WhUlOx2LEjNVShdoNmEkryfHUxVvlkcmLtnB6936O2PuYU+hmcoNcatVv2vzVe1KOnVUbyyd5mJtHXxrDQqpjx/jZ1Fq55RYsjg+eSq/p6ieCIN1y0yvEyBU1wB+K4cr17PCCBfRzVu4CJokE9UnHVq7T4yso3FHxL5pC/q91NXz81XOpKMaHTSRo9A27TzC+ynUa0O6jFqrG8k+u3bzj1y8z0lt0yu27D/iLnJ90GDtAlZZ3aVbrHz8wlHNc9PRH9xCbvXSgbkL6W2GEiv4IraJgKVgKloKlYClYKgqWgqVgKVgKloKlomApWFECVhQvCtKgr4IVuWBVbhEfpWBVaz1EwYpcsJJWJ0en0uq7dsseBStywSLb/9i4s0rLeJabipZlsaq2HLxq405SrmBFqLBwWUZGRlpa2ploE9JMynXhtQgVplXl5ORQQueiTUgzKdelIiOarTyX5EaPSIKLpErBUnlSomCpKFgq0QVWenp6fn6+5oVKqAScgKpEZmZmdna2ZodKqCQrKwuoSmDt04eELdVbKsHrKkACJw5KFLjG60AM9XVORSUIASFAEg31fyyc9OkBPXzZAAAAAElFTkSuQmCC",
+ "description": "Allows to create an input form and set values for multiple attributes simultaneously.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": ".tb-toast {\n min-width: 0;\n font-size: 14px !important;\n}",
+ "controllerScript": "self.onInit = function() {\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.multipleInputWidget.onDataUpdated();\r\n}\r\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-update-multiple-attributes-widget-settings",
+ "dataKeySettingsDirective": "tb-update-multiple-attributes-key-settings",
+ "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\":{}}"
+ }
+ },
+ {
+ "alias": "device_claiming_widget",
+ "name": "Device claiming widget",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to claim the device using name and optional secret key.",
+ "descriptor": {
+ "type": "static",
+ "sizeX": 7.5,
+ "sizeY": 4.5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": ".claim-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n",
+ "controllerScript": "let $scope;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n}\n\nfunction init() {\n $scope = self.ctx.$scope;\n let $injector = $scope.$injector;\n let utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n let $translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n let deviceService = $scope.$injector.get(self.ctx.servicesMap.get('deviceService'));\n let settings = self.ctx.settings || {};\n \n $scope.toastTargetId = 'device-claiming-widget' + utils.guid();\n $scope.secretKeyField = settings.deviceSecret;\n $scope.showLabel = settings.showLabel;\n\n let titleTemplate = \"\";\n let successfulClaim = utils.customTranslation(settings.successfulClaimDevice, settings.successfulClaimDevice) || $translate.instant('widgets.input-widgets.claim-successful');\n let failedClaimDevice = utils.customTranslation(settings.failedClaimDevice, settings.failedClaimDevice) || $translate.instant('widgets.input-widgets.claim-failed');\n let deviceNotFound = utils.customTranslation(settings.deviceNotFound, settings.deviceNotFound) || $translate.instant('widgets.input-widgets.claim-not-found');\n \n if (settings.widgetTitle && settings.widgetTitle.length) {\n titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n titleTemplate = self.ctx.widgetConfig.title;\n }\n self.ctx.widgetTitle = titleTemplate;\n \n $scope.deviceLabel = utils.customTranslation(settings.deviceLabel, settings.deviceLabel) || $translate.instant('widgets.input-widgets.device-name');\n $scope.requiredErrorDevice= utils.customTranslation(settings.requiredErrorDevice, settings.requiredErrorDevice) || $translate.instant('widgets.input-widgets.device-name-required');\n \n $scope.secretKeyLabel = utils.customTranslation(settings.secretKeyLabel, settings.secretKeyLabel) || $translate.instant('widgets.input-widgets.secret-key');\n $scope.requiredErrorSecretKey= utils.customTranslation(settings.requiredErrorSecretKey, settings.requiredErrorSecretKey) || $translate.instant('widgets.input-widgets.secret-key-required');\n \n $scope.labelClaimButon = utils.customTranslation(settings.labelClaimButon, settings.labelClaimButon) || $translate.instant('widgets.input-widgets.claim-device');\n \n $scope.claimDeviceFormGroup = $scope.fb.group(\n {deviceName: ['', [$scope.validators.required]]}\n );\n if ($scope.secretKeyField) {\n $scope.claimDeviceFormGroup.addControl('deviceSecret', $scope.fb.control('', [$scope.validators.required]));\n }\n \n $scope.claim = function(claimDeviceForm) {\n $scope.loading = true;\n\n let deviceName = $scope.claimDeviceFormGroup.get('deviceName').value;\n let claimRequest = {};\n if ($scope.secretKeyField) {\n claimRequest.secretKey = $scope.claimDeviceFormGroup.get('deviceSecret').value;\n }\n deviceService.claimDevice(deviceName, claimRequest, { ignoreErrors: true }).subscribe(\n function (data) {\n successClaim(claimDeviceForm);\n self.ctx.detectChanges();\n },\n function (error) {\n $scope.loading = false;\n if(error.status == 404) {\n $scope.showErrorToast(deviceNotFound, 'bottom', 'left', $scope.toastTargetId);\n } else {\n let errorMessage = failedClaimDevice;\n if (error.status !== 400) {\n if (error.error && error.error.message) {\n errorMessage = error.error.message;\n }\n }\n $scope.showErrorToast(errorMessage, 'bottom', 'left', $scope.toastTargetId);\n } \n self.ctx.detectChanges();\n }\n );\n }\n\n function successClaim(claimDeviceForm) {\n let deviceObj = {\n deviceName: ''\n };\n if ($scope.secretKeyField) {\n deviceObj.deviceSecret = '';\n } \n claimDeviceForm.resetForm(); \n $scope.claimDeviceFormGroup.reset(deviceObj);\n $scope.loading = false;\n $scope.showSuccessToast(successfulClaim, 2000, 'bottom', 'left', $scope.toastTargetId);\n self.ctx.updateAliases();\n }\n \n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-device-claiming-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"deviceSecret\":true,\"showLabel\":true},\"title\":\"Device claiming widget\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"enableDataExport\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "markers_placement_image_map",
+ "name": "Markers Placement - Image Map",
+ "image": "data:image/png;base64,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",
+ "description": "Allows configuring location of the selected entities on the image map.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8.5,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}
X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapImageUrl\":\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iMTEzNC41MTgzIgogICBoZWlnaHQ9Ijc2Mi43ODI0MSIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC41IHIxMDA0MCIKICAgc29kaXBvZGk6ZG9jbmFtZT0id2ljaGl0YW1hcC1ub2xpYi5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjM1IgogICAgIGlua3NjYXBlOmN4PSI4OS45MDc4NTciCiAgICAgaW5rc2NhcGU6Y3k9IjQ1My43ODI0MSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzNjYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iNzIxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItNCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTQiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpvYmplY3QtcGF0aHM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIHNob3dndWlkZXM9InRydWUiCiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSIKICAgICBmaXQtbWFyZ2luLXRvcD0iMCIKICAgICBmaXQtbWFyZ2luLWxlZnQ9IjAiCiAgICAgZml0LW1hcmdpbi1yaWdodD0iMCIKICAgICBmaXQtbWFyZ2luLWJvdHRvbT0iMCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjcuMDcxNDI4LC0zMDcuOTAyOTkpIj4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM3ODciCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzY0ZTU5O3N0cm9rZS13aWR0aDoyLjk5OTk5OTc2O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDkwNi4wMzMxNSw3MDYuMTMzNjcgMy40MjkyLDE3Ljc5NTUyIE0gMjguNTcxNDI4LDc2NS4wNTA2NyBjIDE1MC40MzUyMDIsNi44MzM0MiAxNDYuMzkyMzIyLC0yNi4zMzQxNSAxNjYuNDM0NTQyLC0yOS4zMjAwOSAzNi4xNDM3NSwtNS4zODQ3NiAxMTQuMjg2NzYsLTYuNTI1NCAxNDguMzI1MDgsLTguNjIzNTQgNDMuMzc4MDgsLTIuNjczODUgMTQxLjc2MjIxLC0xMS4yMzA5OSAxODguODU1NzgsLTE5LjgzNDE4IDM5LjgxMTM4LC03LjI3Mjg0IDIyMS4zNjk5MSwtMC44NjIzNSAzMTkuMDcxNDEsLTAuODYyMzUgNzAuODI3MzUsMCAxNDYuOTE4NjcsLTEuNzI0NyAyMTguMTc1ODYsLTEuNzI0NyAtMzEuNjE5NywwIDExNy44NTUyLC0yLjU4NzA3IDg2LjIzNTUsLTIuNTg3MDcgbSAtMjUuMDkwNywtNjguMTI2MDYgYyAtNTIuNzk5NiwzNC43ODQ4NCAtNjUuODk1MSw1MS43NDg2NSAtOTUuNjM5LDgxLjQ5MjU4IC0yNC45MzEzLDI0LjkzMTI3IC0xNDAuMzk2NTMsLTE5LjEzOTIgLTE3OC45Mzg3MSwzNi42NTAwNyAtMTIuMjgxNCwxNy43NzcxNSAtNDcuMDAyNTcsNDYuNTQ2NTMgLTY1LjEwNzgzLDU5LjA3MTMzIC0yMC4xMDUsMTMuOTA4MTggLTU2LjAzNjcyLDQ0Ljk1NjY0IC02Ny43Njg4NSw3My4wNzgyNyAtNC44MDE0NywxMS41MDkwMiAtMTMuMzgwNDYsMzUuOTkyOTggLTIzLjQ0OTQ5LDQ2LjA2MjAxIC0xMC40OTY5OSwxMC40OTY5OSAtMzguMzc3MzMsNi4zODU2OSAtNDQuMDIzNDUsMTcuNjQ3NjQgLTE5LjAwNTAyLDM3LjkwODEyIC0yNS40NjUzLDEwMC45MjM1MiAtNjcuNjE3ODksMTAyLjA1MTAyIG0gMTkuMjgxNTEsLTYyNC4wMTQ2NCBjIDM0LjY1OTM0LC0xLjg3MzgyIDg0LjAyNzMzLDcuMzkxMzEgMTA5LjkwMDcxLC00LjI4NTQ1IDEzLjI4MTcyLC01Ljk5NDA4IDQxLjQwNzIxLC0yLjQ2MTM1IDY2LjgyODY2LC0yLjMyMDQ2IDM1LjMyMjM4LDAuMTk1NzggNjQuMzgyNDksMC42MzQ3NyAxMDEuOTE2Nyw1LjAyMzIgMjUuMDMwMzYsMi45MjY1IDQ0LjY2MjczLDM0LjI4NzIyIDU4LjUyNjk4LDUwLjY0MzkgMTcuMDk4NzgsMjAuMTcyNjggNjIuNzYzODYsLTEuNzE0NjcgNjYuMzA1NjYsMzIuMTM0MzMgNS4xMDI3LDQ4Ljc2NTg3IC02LjMyODQsNzguNjM3MjUgNi4xNDExLDk3LjM0MTUgMTkuOTY5MiwyOS45NTM3OSA1MC40ODY0LDE3Ljg1NTc5IDQ0LjYxOTMsODMuOTcxMTkgTSA1ODkuMTAyMjcsMzA5LjcyNzE1IGMgNC42NDM0NiwyMy43MjkyMyAxNS4wNjkwNCw3Mi43NzU3NSAxOS4wNjEyOCwxMzAuNjQyODggMC44NzIwNiwxMi42NDA0OCA1LjQ0NzE4LDI0Ljk5MjUzIDQuMjIyMzEsNDUuMjc3NTcgLTIuNTE3MjEsNDEuNjg3NSAtMTUuNzE3MDYsNDMuNjc3MjcgLTE1LjA5MTIyLDYwLjM2NDg2IDEuNDMxOTUsMzguMTgyMjQgMzAuNjEzNjEsOTMuODM3MTkgMzAuNjEzNjEsMTM5LjcwMTU0IDAsMjQuMTgwOCAtMi42Njk2NCwxMTUuMzkwNDUgNy4zMzAwMSwxMzUuMzg5NzYgMC4xNTkxMSwwLjMxODIxIDEwLjA2NDc2LDM1Ljg4MzMyIDEwLjc3OTQ1LDQ5LjE1NDI0IDAuOTQzNzgsMTcuNTI0NjkgLTI0LjQ3OCwzOS40NzAwOCAtMjguMDI2NTUsNDYuNTY3MTYgLTUuNDc3NywxMC45NTUzOSAtMzYuOTczMjQsMTAuODgxOTcgLTQwLjA5OTUsMjQuMTQ1OTUgLTMuODY4ODQsMTYuNDE0NTEgLTMuODY2Myw0My43OTczNSA0LjA0NjQ3LDU5LjQ0MTI5IG0gOTcuMzM3MzQsLTY5MS4wMDk0MSBjIC01LjAxMzMyLDM1LjUxNTk1IC00My42NTkwMSwxMS4zMTY1MiAtNTguNTM4NjEsMjMuNzgxMzEgLTIxLjMzMDE5LDE3Ljg2ODUyIC02Mi40OTk2NCwzMS40MzIxMiAtNzAuMTI0MzcsMzUuMzY3MDggLTM1LjA4NzYzLDE4LjEwNzkzIC0xMTAuNDcyMTUsLTE1LjE0MTk2IC0xMjUuNjE0MSw0LjI2ODQzIC0xNS45NTA2MywyMC40NDcwMyAtMC4wNzM1LDYxLjQ2NjQ4IC05LjE0NjY2LDg0LjE0OTI0IC02LjAzNTcsMTUuMDg5MjYgLTE4Ljg3NjcsMjMuMDE3MzQgLTI3LjQzOTk3LDMyLjkyNzk4IC0xOS43NDgyOSwyMi44NTU1NSAtNjkuOTc0MjgsNjkuODI0MTkgLTg0Ljc1OTA0LDEwMC4wMDM0NiAtNy40OTc0MSwxNS4zMDQwNCAtMy4yODQyNiw0NC40MjA0MSAtMy40NzA1Myw2My4zNDI4NCAtMC4xMjc5MywxMi45OTQxNCAtMC44MTAxNSwyMy4xMDM4NSAyLjQwMzQzLDI4LjI3NjE4IDQuOTYxNTgsNy45ODU4MSAyMy43MjA1LDI4LjExMjA3IDI0LjIzODY1LDUwLjYxMTQ5IDAuMjk0MTEsMTIuNzcxNDYgMC4wMTMzLDc4LjU5MTAxIDMuMDQ4ODgsODcuNjU1NDkgMi4zMTI1Niw2LjkwNTQ2IDQuMjIwMDQsMjYuNTY0OTcgMTAuMjEzNzcsMzYuNTg2NjIgMTEuMzU0MDEsMTguOTg0MTUgNC4zODczNyw0MC4xNTY2MiAyNy44OTczLDUzLjUwNzk1IDE5LjA1MDEyLDEwLjgxODU5IDQ2Ljg3NzgxLDEyLjIxODYyIDgxLjkyNjE4LDE0LjQ2MDU0IDMzLjcwMzQ1LDIuMTU1ODkgNjEuNTEyMTcsLTEuNDMwMzUgNzYuOTIwNzcsNi4xNDExIDExLjU4NTA4LDUuNjkyNjYgOC41ODE1MSwxNy45MzM0NCAxNC4yOTU0MSwyOS4zNjEyMyA1LjY0MDQyLDExLjI4MDg1IDMxLjUwMjYzLDExLjE1NjI3IDQxLjgwNDA5LDQzLjQ1NDg3IDcuNjA1OSwyMy44NDcxIDMuMDg1OTMsNDQuMTU2OSA2LjcwNzU1LDY1Ljg4NjYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Nzc3NzY2Njc3Nzc3NzY2Nzc3Nzc3NjY3Nzc3Njc3NzY2Nzc3Nzc3Nzc3Nzc3Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNDMuMjc3ODgxLDUxNy45NDY3OSBjIDAsMCAyMzAuODQ4Mjg5LC0zLjYzODA1IDI1MC4wMDg2MzksLTMuNjU4NjcgNy40ODIyMiwtMC4wMDggOC42MTk1NCw1LjE1MTk0IDE0LjAyMDksMTEuNDU4NjkgMjQuNTk2MDgsMjguNzE4OTMgOTMuOTA5NjYsMTEyLjkzNTg1IDkzLjkwOTY2LDExMi45MzU4NSIKICAgICAgIGlkPSJwYXRoMzc4OSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDM1Ljk2MDU1NSw1NzcuNzA0OTQgYyAwLDAgMTY1LjUyNDU2NSwtMS42ODQ1NCAyNDguNzc5NTY1LC0xLjY4NDU0IDQuOTQ3NDksMCA3LjcyOTkzLC0yLjg4MzMgMTAuNTM3NzEsLTUuNzI5NzcgOS42NjEwNywtOS43OTQxNiAyNS42MzE5OSwtMjguNTg5OTUgMjUuNjMxOTksLTI4LjU4OTk1IgogICAgICAgaWQ9InBhdGgzNzkxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzguMzk5NjYzLDY0MS43MzE1NSA0MzEuNzA1OTMsNjM3LjQ2MzExIgogICAgICAgaWQ9InBhdGgzNzk1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzkuMDA5NDQyLDcwNC41Mzg1OSA1MjMuMTcyNTMsNjk3LjgzMTA0IgogICAgICAgaWQ9InBhdGgzNzk3IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzAzLjk1NzYyLDY4Mi41ODY2MSAxNDYuNzk1NDIsMS44MjkzMyBjIDEwLjUzNDAzLDAuMTMxMjcgMTQuMzQzNzQsLTIuNjM3MzkgMjUuNDg3MTUsLTYuMzcyOCAxMC40MTIxMiwtMy40OTAyNyAzMS40MjQxNSwtMi42OTg5NiA0MS4zODUzOCwtMi43NzM4NSBsIDQwNS41NjA3OSwtMy4wNDg5IgogICAgICAgaWQ9InBhdGgzNzk5IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgzODA0IgogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDQyNi4yMTc5NCwzMTQuODkwOTggYyAyLjA2NzU0LDkuMDUyNzMgMS44NDE3Nyw1MS43Mjc3NyA2LjUwNzk0LDc0LjgzNDY2IDEuNjc0NzUsOC4yOTMzNiA4LjY3NTA4LDE0LjA2NTk4IDEwLjA1NTQxLDE0Ljg1ODYyIDQuOTAxNDcsMi44MTQ2MyAxMC44MTQ3OSw4LjE0OTgyIDEzLjA0NTc5LDE2LjA4ODMxIDYuNzU3NzksMjQuMDQ1OTEgMC44Nzk3Miw2OC40NTIxMiAwLjg3OTcyLDExMC42ODkzIDAsNi4wOTc4MiAxLjY2MDEsMzAuMTQ2NiAtMi4xNTU4OCwzMy45NjI1OSAtMi41NDA4NSwyLjU0MDgzIC0wLjI4MTYzLDEyLjk5MDY5IC0zLjQzNjc1LDE2LjE0Mzc3IGwgLTkuODQ5NDQsOS44NDMxMSBjIC0xMC4zNjcxNSwxMC4zNjA0NyAtMTEuNTkwMTcsNi41MjYxNCAtMTcuNzM4NDgsMTguODIyNzYgLTMuNTY3NzIsNy4xMzU0MyA1LjQwMjM1LDIwLjY3MjEgNy4zNTQzMiwyNC41NzYwMiAxLjkzMjE0LDMuODY0MyAtMS44NDIxNiw0Ljc3NzczIC0xLjc5MjM1LDcuNDQ2MjYgMC4yNTI4NiwxMy41NDQ4MyAyLjI5NzUsMzczLjkyNzEyIDIuMjk3NSwzNzMuOTI3MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2NS4yNDAyMiw1MTkuNzc2MTIgNC4xMTU5OSw1MDIuMTUxNTgiCiAgICAgICBpZD0icGF0aDM4MDYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMTYuNTMxNjUsNTA0LjE4Njk5IDMuODgwNTksMzEwLjk2NDM2IgogICAgICAgaWQ9InBhdGgzODMxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM4ODkiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzE3LjY3NzYsNTc2LjQ4NTM5IDEzMC4xODc0MiwxLjUyNDQ0IGMgNC41MTA3OSwzLjI0MTY5IDIwLjM0NDcxLDcuOTY4NTMgMjcuNzQ0ODYsNC4yNjg0NCAzLjE1NTQ2LC0xLjU3NzcyIDkuNDE5LC01LjM4ODE3IDE0LjAyNDg5LC0zLjk2MzU1IDQuMjY2OTgsMS4zMTk4MSA2LjAxNjg5LDMuMTE2MzIgMTAuMzY2MjEsMy4wNDg4OSAxMC4zMDQwMywtMC4xNTk3NSAyMC4yMTE3LDAuMzg3NDEgMzAuNDg4ODYsMC4zMDQ4OSAxNzcuODkwOCwtMS40MjgyNyAzNTYuNTkwMzUsLTIuMTMyNDcgNTM0Ljc3NDU2LC0zLjA0ODg4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDc1LjMwNTAxLDU4Mi44ODgwNSBjIC0zLjQ0NDE4LDExLjM1MDY2IC0yLjEwMzQzLDEyLjQzMzczIDMuNjU4NjUsMjEuMDM3MzEgMy43OTQ0NSw1LjY2NTY0IDUwLjg2MjYxLDEzLjAzODQ1IDQxLjQ2NDg1LDI3LjEzNTA5IC0xMC41MzY5NywxNS44MDU0NyAtMjIuODk3NDUsLTUuNDc3NzIgLTMzLjg0MjYzLC0xLjgyOTMzIC01LjQ1MjM2LDEuODE3NDUgLTcuMzQ5MDEsNS40NTYzMSAtMy42NTg2Niw5LjE0NjY1IDIuODA2ODMsMi44MDY4NCA0LjA0OCwxLjgwMzk2IDYuNTIwMzQsNS4xMDA0MSIKICAgICAgIGlkPSJwYXRoMzkxMCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjAxMDgyLDYzNi44NTMzMyBjIDguMzE4OTksMTMuMTEwMTYgMTguODQ2MjEsMTQuNjM0NjUgMzUuNjcxOTYsMTQuNjM0NjUgMi45Mzg2NSwwIDcuODY5OTgsLTAuOTMzNzEgMTAuNjcxMTEsMCAxMS4zNTkxNywzLjc4NjM5IDI3LjE5Mzk4LDEwLjI3NTc3IDM2LjIwMTkzLDIxLjEyOTQ4IDguMjgwMDIsOS45NzY2MSAxMC4yNTI3OCwyMy44ODMwOCA3LjcwMjAyLDM3LjEwNDI0IC02LjE2OTg5LDMxLjk3OTk4IC0xNi43MTQzMSw1Ni45ODg1MyAtMTkuMDQzNTUsODYuNTY5MDUgLTEuMzQ3OTgsMTcuMTE4OCA0LjUwOTU3LDIyLjUzNTIyIDExLjA3MTQzLDMzLjkyODU3IDEwLjY3MDIzLDE4LjUyNjcyIDguNzI0NTMsMTQuMTk5NTUgOC41NzE0MywzNC4yODU3MiAtMC4xMzk2MywxOC4zMTk0NCAwLDYwLjI2Mzg1IDAsODAuNzE0MjkiCiAgICAgICBpZD0icGF0aDM5MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUyOC41MDgwNiw2NTguOTU3NzYgYyAtMTAuNjgxMjMsMC45MDQ1NCAtNy4xMDgwNCwtNS42MDI1NSAtMTAuODIzNTQsLTguMDc5NTYgLTQuNzg0NTQsLTMuMTg5NjkgLTEyLjIyNzA0LC0xLjI1MTA0IC0xNi43Njg4OCwtNS43OTI4OCAtMC42NjYxMiwtMC42NjYxMiAtOC44MDk2OSwtNC4xMDg3NyAtMTAuMTc0NDcsLTIuNzQzOTkgLTguMzY0NTksOC4zNjQ1OSAtMy4wNDg4OCwyMC41NTE4OCAtMy4wNDg4OCwzMy41Mzc3NCBsIDMuMDIyLDMzOS42OTc0MyIKICAgICAgIGlkPSJwYXRoMzkxNCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA1MTcuOTg5NDEsNjUxLjAzMDY1IGMgLTAuMjIxNzEsLTIuNzAxODQgMS45MDM0NiwtNS41NjIxMyAzLjM1Mzc3LC03LjAxMjQ1IDEuNzk5NDMsLTEuNzk5NDIgNi45MjI5NCwxLjAwNDE5IDguODQxNzgsLTAuOTE0NjYgMC4yODc2NSwtMC4yODc2NiAwLjg0MzI5LC0xMS4xNjQxIDAuMjI4NjYsLTEzLjU2NzUzIC0yLjA2NDgzLC04LjA3NDE2IC0yLjA1ODAxLC0yOC42NTY1OCAtMi4wNTgwMSwtMzguNzIwODYgbCAwLC03My4xNzMyNiIKICAgICAgIGlkPSJwYXRoMzkxNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNTI4LjY2MDUsNjc1LjQyMTczIC0wLjQ1NzMzLC0zMS41NTU5NiIKICAgICAgIGlkPSJwYXRoMzk3NCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc2Ni4zMTYyNSw1NzkuNjQ0MzEgMC40MzExOCwxMy43OTc2OCBjIDMuMTM2NDMsNC42NjkxNSAzLjAxODI0LDkuNjAwNjggMy4wMTgyNCwxNi4zODQ3NSBsIDAsMTU3LjM3OTgxIgogICAgICAgaWQ9InBhdGgzOTgyIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMTEyMi45MDAxLDc2NS45MTMwMyBjIC0yMDIuMzA2NjksNC42OTA1IC00MDMuNzQ0MDUsLTEuMTEzODEgLTYwNS45NTQ1NCwzLjM1MzkgLTEwLjg2MzYyLDAuMjQwMDIgLTMuMzYxNDcsLTguNTg2MyAtMjguNTM2OCwtOC41ODYzIgogICAgICAgaWQ9InBhdGgzOTg0IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA4NjAuMDA4MDUsNzM3LjA2NjUxIGMgMCwwIC05Ny40NDc1LDAuODU4MDYgLTE0Ny41Njg5MiwwLjg1ODA2IC01LjI2ODYxLDAgLTQuNTE1NDYsLTguMzI5ODYgLTcuMzAwODksLTguMzI5ODYgLTMuOTc0MzUsMCAtOC42MjkyNSwwLjAyMDEgLTEwLjUwOTQ4LDAuMDM1OSAtMi4zMzQ3NywwLjAxOTcgLTEuODEwOTQsOC4zNjU5NyAtNC4xNDU4LDguMzY2OTIgLTQ2LjE2ODk5LDAuMDE4OCAtMTY3LjQwNzY3LC0xLjMwNzk5IC0xNzUuMDUyNjMsLTEuMzA3OTkgLTQuNDI5NTUsMCAtOC41NzYyNywtNi40Mzk3MiAtMTMuMTMxOTgsLTYuNDM5NzIgLTEuMzYxMTUsMCAtNi4yMzg3MywwIC0xNC4zOTQ2NywwIgogICAgICAgaWQ9InBhdGgzOTg2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJNIDY3NS4wMDcwMyw4MzEuMTc0MDIgNjc0LjM5NzI1LDMwOS40MDI5OSIKICAgICAgIGlkPSJwYXRoMzk4OCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc5OS40MDE1NywzMTMuMDYxNjUgMS4yMTk1NSw0OTUuODY2NTMiCiAgICAgICBpZD0icGF0aDM5OTAiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA3MzYuNTk0NTIsMzEyLjQ1MTg4IC0xLjIxOTU1LDcxNi40ODgyMiIKICAgICAgIGlkPSJwYXRoMzk5MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUzMC4wMzA5NCw2NDMuNDU4NTkgMzkyLjM3MTU5LC0zLjAxODI1IgogICAgICAgaWQ9InBhdGg0MDQ4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gODU5LjQ1MDYsMzE0LjkwMTI4IDEuMjkzNTQsNTA3Ljk4MDU4IgogICAgICAgaWQ9InBhdGg0MDUwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjAuOTk5OTk5OTRweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gOTIxLjU0MDE3LDMxMC41ODk0OSAxLjcyNDcxLDUzMS43NTIyNyIKICAgICAgIGlkPSJwYXRoNDA1MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDczNi4yODk2Myw0NTMuMzEwNCAxODUuNjc3MTUsLTAuMzA0ODkiCiAgICAgICBpZD0icGF0aDQxODciCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMDYwLjgxMDUsNTE0Ljk2NzY3IGMgMCwwIC0zNjMuMjgxMjYsLTUuNjI2MTggLTU0NC42NTA0MiwyLjUyMTc4IC00LjE3Nzc2LDAuMTg3NjkgLTEyLjUwMDQ0LDEuMDY3MTEgLTEyLjUwMDQ0LDEuMDY3MTEgLTEuNTcwOTUsMC4xMzQxIC0yLjAwMDkzLC0yLjMyNDk1IC0yLjU5MTU1LC0zLjUwNjIzIC0wLjA5NjcsLTAuMTkzNDMgLTcuMDYwODEsLTEuOTMzNCAtNy42MjIyMSwtMS4zNzE5OSAtMi44OTMxNCwyLjg5MzE0IC03LjYzMTY3LDQuMjQ4NjkgLTEyLjE5NTU1LDQuMTE2IEwgMzY5LjIwMTcsNTE0LjUzNjUiCiAgICAgICBpZD0icGF0aDQyNjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzOTkuODE1MzEsNDc5LjYxMTEyIDExLjY0MTgsNS42MDUzIGMgMi45ODQxMiwxLjQzNjc5IDYuNTI4NzgsLTAuNDc3MTIgOS45MTcwOCwtMC40MzExOCBsIDEyNy4xOTczOSwxLjcyNDcxIgogICAgICAgaWQ9InBhdGg0MjYzIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gNTE5LjI1MTUxLDUxNy4xMjM1NyA1MTguODIwMzIsMzA4LjQzMzYyIgogICAgICAgaWQ9InBhdGg0MjY1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjkyNTQ5LDM4OS43MTQ5OCBjIDExLjA0NDk2LDAgMzUuNTMzMDcsMC42MTkyNyA0Mi41Nzk3OCwtMS4wMDM5NyA4LjQwNTIyLC0xLjkzNjE4IDcuMDY2LC02Ljk1Mzc4IDE0LjE5NzEyLC02Ljk1Mzc4IDcuODA5NSwwIDYuNTQyOTEsOC4wNjIzNyAyMC4xNDE3LDguMDYyMzcgMTMuOTkwNjgsMCA0NC45NzY4OSwwLjM3ODg2IDYzLjkzOTkyLDAuMzc4ODYgMTIuMDgzOTUsMCA4Mi4wMDI2NiwwLjMwNDg5IDkzLjYwMDgxLDAuMzA0ODkgOC43NjA0NywwIDEzLjE1OTcsLTIuMjg4MjcgMjEuMzQyMTksLTcuMDEyNDMgNy4xOTUxNSwtNC4xNTQxMyAyLjA1NDU5LC05LjQ5MTM3IDIwLjQyNzU0LC04Ljg0MTc3IDIzLjE0NTQsMC44MTgzMyAxMi42NDMzNCwxNC4wMjQ4NyAzMi4zMTgxOSwxNC4wMjQ4NyAyNS4zNTk1NCwwIDEzMC45OTkwMiwwIDE1MC45MTk4NSwwIDE0LjMzMjQ0LDAgLTQuMTE5MTEsLTEzLjExMDIxIDI5LjI2OTMsLTEzLjQxNTEiCiAgICAgICBpZD0icGF0aDQyNjkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU4OC42Nzk1NyIKICAgICAgIHk9IjczNS44MDQ2MyIKICAgICAgIGlkPSJ0ZXh0NDMxMCIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMiIKICAgICAgICAgeD0iNTg4LjY3OTU3IgogICAgICAgICB5PSI3MzUuODA0NjMiPkxpbmNvbG48L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY4Ni4zOTg1IgogICAgICAgeT0iNzY1LjYyODQyIgogICAgICAgaWQ9InRleHQ0MzEwLTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNiIKICAgICAgICAgeD0iNjg2LjM5ODUiCiAgICAgICAgIHk9Ijc2NS42Mjg0MiI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgIHk9Ii04MDIuMzc3MzgiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgiCiAgICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgICAgeT0iLTgwMi4zNzczOCI+V29vZGxhd248L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU2Mi4xMTkyNiIKICAgICAgIHk9Ii03NzEuOTY4MTQiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yIgogICAgICAgICB4PSI1NjIuMTE5MjYiCiAgICAgICAgIHk9Ii03NzEuOTY4MTQiPkVkZ2Vtb29yPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTguMzA0ODciCiAgICAgICB5PSItNzM4LjM2NjQ2IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yLTkiCiAgICAgICAgIHg9IjU5OC4zMDQ4NyIKICAgICAgICAgeT0iLTczOC4zNjY0NiI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICB5PSItNjc3LjIwMzk4IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00IgogICAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICAgIHk9Ii02NzcuMjAzOTgiPkhpbGxzaWRlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTcuMzI3MDkiCiAgICAgICB5PSItODYyLjYxNDA3IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNS0zIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgtMi05LTQtMSIKICAgICAgICAgeD0iNTk3LjMyNzA5IgogICAgICAgICB5PSItODYyLjYxNDA3Ij5Sb2NrPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1ODcuMzcwMTgiCiAgICAgICB5PSItOTI2LjEzNjYiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTktNy01LTMtMiIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00LTEtMyIKICAgICAgICAgeD0iNTg3LjM3MDE4IgogICAgICAgICB5PSItOTI2LjEzNjYiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9Ijg3MS4xNjEwMSIKICAgICAgIHk9IjYzNy41NzUyIgogICAgICAgaWQ9InRleHQ0NDY1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDY3IgogICAgICAgICB4PSI4NzEuMTYxMDEiCiAgICAgICAgIHk9IjYzNy41NzUyIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICB5PSI1NzcuMDMyNDciCiAgICAgICBpZD0idGV4dDQ0NjUtMyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00IgogICAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICAgIHk9IjU3Ny4wMzI0NyI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgaWQ9InRleHQ0NDkwIgogICAgICAgeT0iNTEwLjI2MTgxIgogICAgICAgeD0iODc1Ljk2NjQ5IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSI1MTAuMjYxODEiCiAgICAgICAgIHg9Ijg3NS45NjY0OSIKICAgICAgICAgaWQ9InRzcGFuNDQ5MiIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iODgxLjMxNjU5IgogICAgICAgeT0iNDUwLjE5ODc2IgogICAgICAgaWQ9InRleHQ0NDk0IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDk2IgogICAgICAgICB4PSI4ODEuMzE2NTkiCiAgICAgICAgIHk9IjQ1MC4xOTg3NiI+Mjl0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNjE1Ljc5MjQ4IgogICAgICAgeT0iMzg3Ljc0NzE2IgogICAgICAgaWQ9InRleHQ0NDY1LTMtMSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00LTEiCiAgICAgICAgIHg9IjYxNS43OTI0OCIKICAgICAgICAgeT0iMzg3Ljc0NzE2Ij4zN3RoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MTkiCiAgICAgICB5PSI0ODEuNjUyODYiCiAgICAgICB4PSI0ODQuNjkwMzciCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjQ4MS42NTI4NiIKICAgICAgICAgeD0iNDg0LjY5MDM3IgogICAgICAgICBpZD0idHNwYW40NTIxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj4yNXRoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NjMuMDQ2NzUiCiAgICAgICB5PSI1MTMuMzYxMzMiCiAgICAgICBpZD0idGV4dDQ1MjMiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1MjUiCiAgICAgICAgIHg9IjU2My4wNDY3NSIKICAgICAgICAgeT0iNTEzLjM2MTMzIj4yMXN0PC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MjciCiAgICAgICB5PSI1NzcuODk0ODQiCiAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTc3Ljg5NDg0IgogICAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgICAgaWQ9InRzcGFuNDUyOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzMSIKICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICB4PSI0MzMuNTgwNzUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICAgIHg9IjQzMy41ODA3NSIKICAgICAgICAgaWQ9InRzcGFuNDUzMyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+QW1pZG9uPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI0MDUuNTMwOTgiCiAgICAgICB5PSItNTIzLjU0MDE2IgogICAgICAgaWQ9InRleHQ0NTM1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDUzNyIKICAgICAgICAgeD0iNDA1LjUzMDk4IgogICAgICAgICB5PSItNTIzLjU0MDE2Ij5BcmthbnNhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzOSIKICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICB4PSI3NDUuNDg0NjIiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICAgIHg9Ijc0NS40ODQ2MiIKICAgICAgICAgaWQ9InRzcGFuNDU0MSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+V2VzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTk2LjcyODMzIgogICAgICAgeT0iLTUzMS4yNTkyOCIKICAgICAgIGlkPSJ0ZXh0NDU0MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NDUiCiAgICAgICAgIHg9IjU5Ni43MjgzMyIKICAgICAgICAgeT0iLTUzMS4yNTkyOCI+V2FjbzwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU1NSIKICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICB4PSI1OTUuNDM0ODEiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICAgIHg9IjU5NS40MzQ4MSIKICAgICAgICAgaWQ9InRzcGFuNDU1NyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+TWF6aWU8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgIHk9IjE2Mi4wNjg3NyIKICAgICAgIGlkPSJ0ZXh0NDU1OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC43MDcxMDY3OCwwLjcwNzEwNjc4LC0wLjcwNzEwNjc4LDAuNzA3MTA2NzgsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjEiCiAgICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgICAgeT0iMTYyLjA2ODc3Ij5ab288L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjI0MC41ODk5NyIKICAgICAgIHk9IjU3NC40NDU0MyIKICAgICAgIGlkPSJ0ZXh0NDU2MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU2NSIKICAgICAgICAgeD0iMjQwLjU4OTk3IgogICAgICAgICB5PSI1NzQuNDQ1NDMiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU2NyIKICAgICAgIHk9IjUxMS42MzY2MyIKICAgICAgIHg9IjIwNi4wMzE3NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTExLjYzNjYzIgogICAgICAgICB4PSIyMDYuMDMxNzUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjYyMC40NDMxMiIKICAgICAgIHk9Ii01MDYuNjgyMTkiCiAgICAgICBpZD0idGV4dDQ1NzEiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NTczIgogICAgICAgICB4PSI2MjAuNDQzMTIiCiAgICAgICAgIHk9Ii01MDYuNjgyMTkiPk5pbXM8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU4MyIKICAgICAgIHk9IjY5OC44NDAwOSIKICAgICAgIHg9IjM3MC4yMTY4NiIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNjk4Ljg0MDA5IgogICAgICAgICB4PSIzNzAuMjE2ODYiCiAgICAgICAgIGlkPSJ0c3BhbjQ1ODUiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1hcGxlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSIzODQuMDg0MiIKICAgICAgIHk9IjY4MC44NTEzOCIKICAgICAgIGlkPSJ0ZXh0NDU5OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDYwMSIKICAgICAgICAgeD0iMzg0LjA4NDIiCiAgICAgICAgIHk9IjY4MC44NTEzOCI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzNjcuOTA4MTcsMTAwOS45NTk2IDI2My4wMTgzMywwIgogICAgICAgaWQ9InBhdGg0NjA1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDciCiAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgeD0iNzM2LjI2NzQ2IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgICB4PSI3MzYuMjY3NDYiCiAgICAgICAgIGlkPSJ0c3BhbjQ2MDkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1lcmlkaWFuPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ5NzkiCiAgICAgICB5PSI2NDAuMjA1MjYiCiAgICAgICB4PSI1NzIuODMyMTUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjY0MC4yMDUyNiIKICAgICAgICAgeD0iNTcyLjgzMjE1IgogICAgICAgICBpZD0idHNwYW40OTgxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NzUuMDg5NjYiCiAgICAgICB5PSI2NzAuOTAzNSIKICAgICAgIGlkPSJ0ZXh0NDk4MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDk4NSIKICAgICAgICAgeD0iNTc1LjA4OTY2IgogICAgICAgICB5PSI2NzAuOTAzNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNDk5LjQ4OTYyIgogICAgICAgeT0iMTAwOC42MDY5IgogICAgICAgaWQ9InRleHQ1MDQ3IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5IgogICAgICAgICB4PSI0OTkuNDg5NjIiCiAgICAgICAgIHk9IjEwMDguNjA2OSI+NDd0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iMjE2LjY0NTQzIgogICAgICAgeT0iNzI1Ljk4Mjk3IgogICAgICAgaWQ9InRleHQ1MDUxIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDUzIgogICAgICAgICB4PSIyMTYuNjQ1NDMiCiAgICAgICAgIHk9IjcyNS45ODI5NyI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICAgPGZsb3dSb290CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgaWQ9ImZsb3dSb290NTA1NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6MThweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIj48Zmxvd1JlZ2lvbgogICAgICAgICBpZD0iZmxvd1JlZ2lvbjUwNTciPjxyZWN0CiAgICAgICAgICAgaWQ9InJlY3Q1MDU5IgogICAgICAgICAgIHdpZHRoPSIzNDMuNTcxNDQiCiAgICAgICAgICAgaGVpZ2h0PSIxMDMuNTcxNDMiCiAgICAgICAgICAgeD0iMTkuMjg1NzE1IgogICAgICAgICAgIHk9IjE3LjE0Mjg1NyIKICAgICAgICAgICBzdHlsZT0iZm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIgLz48L2Zsb3dSZWdpb24+PGZsb3dQYXJhCiAgICAgICAgIGlkPSJmbG93UGFyYTUwNjEiPjwvZmxvd1BhcmE+PC9mbG93Um9vdD4gICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDYwNy03IgogICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgIHg9Ijc3NC44NzU2MSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgICAgeD0iNzc0Ljg3NTYxIgogICAgICAgICBpZD0idHNwYW40NjA5LTciCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1jQ2xlYW48L3RzcGFuPjwvdGV4dD4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzY0LjE1OTk5LDY1OC40Mjg5MSAyOTkuNTEwMjMsLTEuMDEwMTYgYyA2LjQ5ODcyLC0wLjAyMTkgNi45NzcxOSw5LjI1NDEyIDE2LjU5NjMxLDkuMzkyNDcgMTIuMDU0MjcsMC4xNzMzOSAyOS4xMTA4MywtMC41MzU3MiA1NC4xMTQzNywtMC4zMDExIgogICAgICAgaWQ9InBhdGg1NDQwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgIHk9Ijk0NC4zNTc1NCIKICAgICAgIGlkPSJ0ZXh0NTA0Ny05IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5LTMiCiAgICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgICAgeT0iOTQ0LjM1NzU0Ij5NYWNBcnRodXI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDctNy0xIgogICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgIHg9Ijc4MC44NDYwNyIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgICAgeD0iNzgwLjg0NjA3IgogICAgICAgICBpZD0idHNwYW40NjA5LTctOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+U2VuZWNhPC90c3Bhbj48L3RleHQ+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2Ny42OTU1Myw1MzcuMjEwNiAxNDEuMjgzMDMsLTEuMDEwMTUgYyA2LjQ4OTk5LC0wLjA0NjQgMTIuNzgxMTQsNy4yMzU0NSAxOS4xOTI5LDcuMzIzNiA1NS45MjM2MiwwLjc2ODkgMTU4LjY4OTk3LC0wLjE3MzMzIDIzNi41MTQwMiwtMS4wMTAxNSA3LjgzOTU2LC0wLjA4NDMgMjIuNjMxNDcsLTE5Ljg1MzU1IDMwLjMwNDU3LC0yMC40NTU1OSAyMi4yNjU4OSwtMS4zNTE4MSA0NS4xNzk0NSwtMC41MDUwNyA2Ny42ODAyMiwtMC41MDUwNyAxNi4xNDczMSwtMC42MzI0MSAzLjYxMDE2LDIwLjcwODEzIDI2Ljc2OTA0LDIwLjcwODEzIGwgMjQzLjQ0Njc5LC0xLjAxMDE2IgogICAgICAgaWQ9InBhdGg1NDk2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzY2NjY2MiIC8+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI2ODUuMjA4MTMiCiAgICAgICB5PSI4MjcuNTMwODIiCiAgICAgICBpZD0idGV4dDQzMTAtNy04IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtNiIKICAgICAgICAgeD0iNjg1LjIwODEzIgogICAgICAgICB5PSI4MjcuNTMwODIiPlBhd25lZTwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSA1NTQuMjg1NzIsNzIxLjQyODU3IDU1MCw1NDMuMjE0MjkgNTQ3LjE0Mjg2LDEwMi41IDU0Ni43ODU3MiwyMy4yMTQyODUiCiAgICAgICBpZD0icGF0aDU1MTkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIiAvPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTI5LjYyNTMxIgogICAgICAgeT0iLTU1MC44NDc3OCIKICAgICAgIGlkPSJ0ZXh0NDU0My01IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU0NS0wIgogICAgICAgICB4PSI1MjkuNjI1MzEiCiAgICAgICAgIHk9Ii01NTAuODQ3NzgiPkJyb2Fkd2F5PC90c3Bhbj48L3RleHQ+CiAgPC9nPgo8L3N2Zz4K\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"showTooltip\":true,\"autocloseTooltip\":true,\"showTooltipAction\":\"click\",\"defaultCenterPosition\":\"0,0\",\"provider\":\"image-map\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${coordinates|ts:7}
Delete\"},\"title\":\"Markers Placement - Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"c39f512a-21c6-6b06-3aa1-715262c6553d\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"94bf5ffd-b526-c6c3-ae3b-ab42191217d9\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}"
+ }
+ },
+ {
+ "alias": "update_integer_timeseries",
+ "name": "Update integer timeseries",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==",
+ "description": "Simple form to input new integer value for pre-defined timeseries key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}\n",
+ "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n dataKeyOptional: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-integer-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update integer timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_double_timeseries",
+ "name": "Update double timeseries",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC",
+ "description": "Simple form to input new double value for pre-defined timeseries key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n dataKeyOptional: true\n }\n}\n\nself.onDestroy = function() {\n\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-double-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update double timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_boolean_timeseries",
+ "name": "Update boolean timeseries",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=",
+ "description": "Simple form to input new boolean value for pre-defined timeseries key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\r\n overflow: hidden;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n display: flex;\r\n}\r\n.grid__element:first-child {\r\n flex: 1;\r\n}\r\n\r\n.grid__element {\r\n display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n width: 32px;\r\n min-width: 32px;\r\n height: 32px;\r\n min-height: 32px;\r\n padding: 0 !important;\r\n margin: 0 !important;\r\n line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-icon {\r\n width: 20px;\r\n min-width: 20px;\r\n height: 20px;\r\n min-height: 20px;\r\n font-size: 20px;\r\n}\r\n\r\n.tb-toast {\r\n font-size: 14px!important;\r\n}",
+ "controllerScript": "let settings;\nlet utils;\nlet translate;\nlet http;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [{\n key: $scope.currentKey,\n value: $scope.checkboxValue\n }]\n );\n\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-boolean-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update boolean timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_string_timeseries",
+ "name": "Update string timeseries",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==",
+ "description": "Simple form to input new string value for pre-defined timeseries key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-string-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update string timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_server_image_attribute",
+ "name": "Update server image attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new image for pre-defined server-side attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n font-size: 16px !important;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.displayPreview = utils.defaultValue(settings.displayPreview, true);\r\n settings.displayClearButton = utils.defaultValue(settings.displayClearButton, false);\r\n settings.displayApplyButton = utils.defaultValue(settings.displayApplyButton, true);\r\n settings.displayDiscardButton = utils.defaultValue(settings.displayDiscardButton, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, []]}\r\n );\r\n \r\n $scope.attributeUpdateFormGroup.valueChanges.subscribe( () => {\r\n self.ctx.detectChanges();\r\n if (!settings.displayApplyButton) {\r\n $scope.updateAttribute();\r\n }\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n if (settings.displayApplyButton) {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n }\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n var value = self.ctx.data[0].data[0][1];\r\n if (settings.displayApplyButton || !$scope.originalValue) {\r\n $scope.originalValue = value;\r\n }\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(value, {emitEvent: false});\r\n self.ctx.detectChanges();\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n $scope.attributeUpdateFormGroup.valueChanges.unsubscribe();\r\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-image-attribute-widget-settings",
+ "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\":{\"showResultMessage\":true,\"displayPreview\":true,\"displayClearButton\":false,\"displayApplyButton\":true,\"displayDiscardButton\":true},\"title\":\"Update server image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_server_date_attribute",
+ "name": "Update server date attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new date value for pre-defined server-side attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n\r\n $scope.datePickerType = settings.showTimeInput ? 'datetime' : 'date'; \r\n \r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\r\n $scope.labelValue = translate.instant('widgets.input-widgets.date');\r\n \r\n if (settings.showTimeInput) {\r\n $scope.labelValue += \" & \" + translate.instant('widgets.input-widgets.time');\r\n }\r\n \r\n var validators = [];\r\n \r\n if (settings.isRequired) {\r\n validators.push($scope.validators.required);\r\n }\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, validators]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.clear = function(event) {\r\n event.stopPropagation();\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(undefined);\r\n }\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n var currentValueInMilliseconds;\r\n \r\n if (!$scope.attributeUpdateFormGroup.get('currentValue').value) {\r\n currentValueInMilliseconds = undefined;\r\n } else {\r\n currentValueInMilliseconds = $scope.attributeUpdateFormGroup.get('currentValue').value.getTime();\r\n }\r\n \r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: currentValueInMilliseconds\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n \r\n $scope.isValidDate = function(date) {\r\n return date instanceof Date && !isNaN(date);\r\n }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n\r\n if (!$scope.isValidDate($scope.originalValue)) {\r\n $scope.originalValue = undefined;\r\n }\r\n \r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n}\r\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-date-attribute-widget-settings",
+ "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 server date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_server_boolean_attribute",
+ "name": "Update server boolean attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=",
+ "description": "Simple form to input new boolean value for pre-defined server-side attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-boolean-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_server_double_attribute",
+ "name": "Update server double attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC",
+ "description": "Simple form to input new double value for pre-defined server-side attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-double-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_shared_image_attribute",
+ "name": "Update shared image attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new image for pre-defined shared attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n font-size: 16px !important;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.displayPreview = utils.defaultValue(settings.displayPreview, true);\r\n settings.displayClearButton = utils.defaultValue(settings.displayClearButton, false);\r\n settings.displayApplyButton = utils.defaultValue(settings.displayApplyButton, true);\r\n settings.displayDiscardButton = utils.defaultValue(settings.displayDiscardButton, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, []]}\r\n );\r\n \r\n $scope.attributeUpdateFormGroup.valueChanges.subscribe( () => {\r\n self.ctx.detectChanges();\r\n if (!settings.displayApplyButton) {\r\n $scope.updateAttribute();\r\n }\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n if (settings.displayApplyButton) {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n }\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n var value = self.ctx.data[0].data[0][1];\r\n if (settings.displayApplyButton || !$scope.originalValue) {\r\n $scope.originalValue = value;\r\n }\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(value, {emitEvent: false});\r\n self.ctx.detectChanges();\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n $scope.attributeUpdateFormGroup.valueChanges.unsubscribe();\r\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-image-attribute-widget-settings",
+ "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\":{\"showResultMessage\":true,\"displayPreview\":true,\"displayClearButton\":false,\"displayApplyButton\":true,\"displayDiscardButton\":true},\"title\":\"Update shared image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_shared_date_attribute",
+ "name": "Update shared date attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new date for pre-defined shared attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n\r\n $scope.datePickerType = settings.showTimeInput ? 'datetime' : 'date'; \r\n \r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\r\n $scope.labelValue = translate.instant('widgets.input-widgets.date');\r\n \r\n \r\n if (settings.showTimeInput) {\r\n $scope.labelValue += \" & \" + translate.instant('widgets.input-widgets.time');\r\n }\r\n \r\n var validators = [];\r\n \r\n if (settings.isRequired) {\r\n validators.push($scope.validators.required);\r\n }\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, validators]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.clear = function(event) {\r\n event.stopPropagation();\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(undefined);\r\n }\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n var currentValueInMilliseconds;\r\n \r\n if (!$scope.attributeUpdateFormGroup.get('currentValue').value) {\r\n currentValueInMilliseconds = undefined;\r\n } else {\r\n currentValueInMilliseconds = $scope.attributeUpdateFormGroup.get('currentValue').value.getTime();\r\n }\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: currentValueInMilliseconds\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n \r\n $scope.isValidDate = function(date) {\r\n return date instanceof Date && !isNaN(date);\r\n }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n \r\n if (!$scope.isValidDate($scope.originalValue)) {\r\n $scope.originalValue = undefined;\r\n }\r\n \r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-date-attribute-widget-settings",
+ "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\":{\"showResultMessage\":true,\"isRequired\":true,\"showLabel\":true,\"showTimeInput\":true},\"title\":\"Update shared date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_shared_boolean_attribute",
+ "name": "Update shared boolean attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=",
+ "description": "Simple form to input new boolean value for pre-defined shared attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\r\n overflow: hidden;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n display: flex;\r\n}\r\n.grid__element:first-child {\r\n flex: 1;\r\n}\r\n\r\n.grid__element {\r\n display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n width: 32px;\r\n min-width: 32px;\r\n height: 32px;\r\n min-height: 32px;\r\n padding: 0 !important;\r\n margin: 0 !important;\r\n line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-icon {\r\n width: 20px;\r\n min-width: 20px;\r\n height: 20px;\r\n min-height: 20px;\r\n font-size: 20px;\r\n}\r\n\r\n.tb-toast {\r\n font-size: 14px!important;\r\n}",
+ "controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue || false\n }\n ]\n ).subscribe(\n function success() {\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-boolean-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_server_integer_attribute",
+ "name": "Update server integer attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==",
+ "description": "Simple form to input new integer value for pre-defined server attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n \n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-integer-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_server_string_attribute",
+ "name": "Update server string attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==",
+ "description": "Simple form to input new string value for pre-defined server-side attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n \n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-string-attribute-widget-settings",
+ "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\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_location_timeseries",
+ "name": "Update location timeseries",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new location for pre-defined timeseries keys.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentLat: [undefined, [$scope.validators.required,\r\n $scope.validators.min(-90),\r\n $scope.validators.max(90)]],\r\n currentLng: [undefined, [$scope.validators.required,\r\n $scope.validators.min(-180),\r\n $scope.validators.max(180)]]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"timeseries\"){\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityTimeseries(\r\n datasource.entity.id,\r\n 'scope',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-location-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update location timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_shared_double_attribute",
+ "name": "Update shared double attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC",
+ "description": "Simple form to input new double value for pre-defined shared attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n \n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-double-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_shared_integer_attribute",
+ "name": "Update shared integer attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==",
+ "description": "Simple form to input new integer value for pre-defined shared attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-integer-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_shared_string_attribute",
+ "name": "Update shared string attribute",
+ "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==",
+ "description": "Simple form to input new string value for pre-defined shared attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-string-attribute-widget-settings",
+ "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 shared string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_server_location_attribute",
+ "name": "Update server location attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new location for pre-defined server attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n settings.isLatRequired = utils.defaultValue(settings.isLatRequired, true);\r\n settings.isLngRequired = utils.defaultValue(settings.isLngRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n \r\n var validatorsLat = [$scope.validators.min(-90), $scope.validators.max(90)];\r\n var validatorsLng = [$scope.validators.min(-180), $scope.validators.max(180)];\r\n \r\n if (settings.isLatRequired) {\r\n validatorsLat.push($scope.validators.required);\r\n }\r\n \r\n if (settings.isLngRequired) {\r\n validatorsLng.push($scope.validators.required);\r\n }\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group({\r\n currentLat: [undefined, validatorsLat],\r\n currentLng: [undefined, validatorsLng]\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"attribute\") {\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-location-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server location attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "markers_placement_google_maps",
+ "name": "Markers Placement - Google Maps",
+ "image": "data:image/png;base64,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",
+ "description": "Allows configuring location of the selected entities on the Google Map.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8.5,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Delete\",\"markerImageSize\":34,\"gmDefaultMapType\":\"roadmap\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"colorFunction\":\"\\n\",\"color\":\"#fe7569\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"defaultZoomLevel\":5,\"provider\":\"google-map\",\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${coordinates|ts:7}
Delete\",\"showPolygonTooltip\":false},\"title\":\"Markers Placement - Google Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"8d3c0156-0a14-7a6f-0ddd-0ec16b9ffc91\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"46bf69cd-8906-234c-a879-e2e4c92f5b67\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}"
+ }
+ },
+ {
+ "alias": "update_shared_location_attribute",
+ "name": "Update shared location attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new location for pre-defined shared attribute key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n settings.isLatRequired = utils.defaultValue(settings.isLatRequired, true);\r\n settings.isLngRequired = utils.defaultValue(settings.isLngRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n\r\n var validatorsLat = [$scope.validators.min(-90), $scope.validators.max(90)];\r\n var validatorsLng = [$scope.validators.min(-180), $scope.validators.max(180)];\r\n \r\n if (settings.isLatRequired) {\r\n validatorsLat.push($scope.validators.required);\r\n }\r\n \r\n if (settings.isLngRequired) {\r\n validatorsLng.push($scope.validators.required);\r\n }\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group({\r\n currentLat: [undefined, validatorsLat],\r\n currentLng: [undefined, validatorsLng],\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"attribute\"){\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-update-location-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared location attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "web_camera_input",
+ "name": "Photo camera input",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to take webcamera image or upload photo.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.photoCameraInputWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-photo-camera-input-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Photo camera input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "update_json_attribute",
+ "name": "Update JSON attribute",
+ "image": "data:image/png;base64,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",
+ "description": "Simple form to input new JSON value for pre-defined attribute/timeseries key.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.jsonInputWidget.onDataUpdated();\n}\n\nself.onResize = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-update-json-attribute-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"attributeScope\":\"SERVER_SCOPE\",\"showLabel\":true,\"attributeRequired\":true,\"showResultMessage\":true},\"title\":\"Update JSON attribute\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json
new file mode 100644
index 0000000000000000000000000000000000000000..32c93adc3ec0dd8492f305a517ef2f031d147320
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/maps.json
@@ -0,0 +1,181 @@
+{
+ "widgetsBundle": {
+ "alias": "maps_v2",
+ "title": "Maps",
+ "image": "data:image/png;base64,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",
+ "description": "Visualize latest location or trip animation of the devices or other entities on the indoor or outdoor maps."
+ },
+ "widgetTypes": [
+ {
+ "alias": "route_map_tencent_maps",
+ "name": "Route Map - Tencent Maps",
+ "image": "data:image/png;base64,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",
+ "description": "Trip animation on the Tencent maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8.5,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('tencent-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-route-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"tencent-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details
\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d3\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map - Tencent Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "test",
+ "name": "Trip Animation",
+ "image": "data:image/png;base64,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",
+ "description": "Trip animation on the OpenStreetMap or other map providers. Allows to visualize location and other timeseries data for each point in time.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 10,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
+ "controllerScript": "self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n}\n\nself.actionSources = function() {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n};\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-trip-animation-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"strokeWeight\":2,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180,\"provider\":\"openstreet-map\",\"normalizationStep\":1000,\"decoratorSymbol\":\"arrowHead\",\"decoratorSymbolSize\":10,\"decoratorCustomColor\":\"#000\",\"decoratorOffset\":\"20px\",\"endDecoratorOffset\":\"20px\",\"decoratorRepeat\":\"20px\",\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"pointTooltipOnRightPanel\":true,\"autocloseTooltip\":true,\"useCustomProvider\":false,\"useLabelFunction\":false,\"useTooltipFunction\":false,\"useMarkerImageFunction\":false,\"useColorFunction\":false,\"usePolylineDecorator\":false,\"useDecoratorCustomColor\":false,\"showPoints\":false,\"showPolygon\":false},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":false,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"displayTimewindow\":true}"
+ }
+ },
+ {
+ "alias": "route_map",
+ "name": "Route Map",
+ "image": "data:image/png;base64,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",
+ "description": "Trip animation on the Google maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8.5,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-route-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"google-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d2\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABdCAYAAAAyj+FzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAH3gAAB94BHQKrYQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7b13uB3VdTb+rrX3zJx6i7qQUAEJIQlRBAZc6BgLDDYmIIExLjgJcQk/YkKc4gIGHH+fDSHg2CGOHRuCQ4ltbBODJIroIIoQIJCQdNXLvVe3nT4ze6/1/XHOlYWQAJuWP37refYz58yd3d6zyt5rr1mX8B7S5Xo5/0nPYaNFM1PY0gGqOhfAgQCNBGlWFFUAYEIeihigbhFdZQwt85BV5Gj9r/718R2XX365vFdzoHe7w6d77xnPkn4YpAtU0YiizNJcmPNkMQFkDiSlowHt2HNtGlTSJ6B+pTpsKTfKgTj3Pi8SMtFtEZnFs8d8dPu7OZ93BcCHtt0+OiL+FJjOiqy5K5dtLwD4PBHGvy0dKLYo8B+1+lAldv50FfmFzWX+84i2M3a8Le2/Dr1jAKqCHtl2y1wC/pEMP9ZRLBaYzF8CCN+pPluUkOKfB6qlmk/dBwTyt8eOv2AZCPpOdPaOAPjA1h9/SJX+TyGXuz0TZi4EcPBeOk+U+RErZh2YyMAyQJEoZUjFgtkCAEScgDyx1hmInTglqDj2U1X0WILaPbWvwHO1WummeuLONhaXHTf2wsfe7rm+rQDe133j/i5xPyrmCr+OouhSKPbdQ5fLiezTIYUBQGMJBgYWxMYSISZhbxgQT8wGAgDiwWxUvCiBxKhSKOqdh4OyV5+6XiEfK/kjVOXQ13apG+I0+adKpXaG0/Si0yZdvPbtmvPbAuCNT98YTBhT/8fAmEpHoXgKgPe/6gFGP0nwG8s2YykcaRCAYYQ5tKTkDVuArDEwMRF5AICS4VZ1AQBSr6oEgL36CBAvlKqIsyLOKQl5TZH4uN+TawDuY6o64lWTJX20v1S633uJNvfmvnbRERelb3XubxnAX26+5gDy6Y9HtrU/wERff1XjSt0WwULDmZEMawPOgilgQ4FaGCEygaXQMQyRMaxiUijUkAEAImIGAFURAOrVA1AmI1ZExGuqoqkVFefhyGtKDql4X4eHc6LxJof0VIVM3nVc4uXaHUPlo0Tpc2fv/zer38r83xKAd6y74iImO31EMf9REA7cpdVBY8NbA5+dFNqsCTQipkitBjAUsLUZNd4qm8AyjDMmJAIRhDzDEBEbJkBVAyJWQJ14AEaciIeSGicOgBeBWNHEeXLkXIM8UvFI4bVBCVJNfdk7STd5xOcp0LZzjIqV/eXq/4i61edM/eaN7yqAqpfzf62Nf5LP5lbko/DbCuxU4saEN1mN2kKTzQbIkuEIEWfVagRDEVkOyXCkVq0aDg2p9YYNAySVerU0WN1R27Jjo6ulMQ1V+ggAOgsjNRNEus/IiUFnYUy2kM23AcrivXh2RiTxjhx5iSmVWEWdpmhQ4qvwSBBrXVPfqDmuVsT7C3aZvKslyZcr9dpxdr81F8ynO/w7DuD1q/8y6kDw2872ticN0deG7wvQHXHmdxGK+1ibQag5ikweliIElNUAEayNYBCSRQRiYzf2rNtx11O/rC5d9dj+1aQyM2Pyz3WGozaNisYNWY7SYtgWA0A5KUVO4qAn3t4+lOzYt+Grh+bDwstHzvjA2tPfd1Z+39FTRhGpi7VBKrE4nyBFDKcNJL5OCerqUEXdVeEQb0mk8lECjR0euxe9cqBUOnoQ6RkXT78hfscAvH71X0Z5kf8Z0dH2CgNf2NkI0d0ZbmtElMtFVEAQ5BFIlkKb00AzFJqCGooQcJjv7t868P3/ubayZvua48ZlJt57xLjjB/cpTssXokK7IQNrbeoZ3pIRJm1aYSUW9cwixglZ7xNU40ppY7mr+sy2ezt7G1s+vP+EGfd/+fS/Ko5pH9/pJK04X6MUDSRapcTXkXJN46QKp1UkqNVqvpxVyLzhOajihh1DpVkmrJ7+uak/bbztAF6/+i8j62p3j20vbgXR+cP3LYU/Djg/KcsdEnIWERcRIk+hzWtEOYSch2U76tk1T6+84Tf/NCdni2tOmbRgy6T26WOiKDBhGFEQhrBhiNAyjDGiQp4DFgI8AChg1BGBXOC9p8QJ0kas3jvEcUxxnLgNpTW9izfdOqGWlve7+OOXrThk6qEHKtKehq9xIlWkvoaYytrwFYqlglgrcZxW+oXSz+ycpOLmnsHypDTIfuTNcuKbAvD2288x22dn7hrVnt/ATBftBE/CH2aCtqkZU6CI2hHZomS4YCPK+5AKHFB2ZNe2Nev/739/e9qY3KRnPzHtQp/LtnfkMhnKZDMa2oDCTIjQhghDC2MCCQITAyYxpmkhAIAZDDA7l4bOSeR9YpLEwfkUjXqMOE0QN2LU4waq9aGBX6/+d7O9sXnu3579jbVTx02dlEilL0FDG1pJG64cJX5IGr6MupY5duU1npIv7sTQ4196ytUDx8+sf+TN6MQ3AyBd8+L8W0a15zYw0d8O3ww4vC7ijlkZU5QctVPE7QhNEVlTRNYUjHcy7tu3fuuVSqXBF8z66962fMeIfDaHfD4nmUyWsrk8BdaYIAh9EFoxzExEysYoAQ5A0ioAEIpIBGZmAM459iKaJo6cT209TnyjWkOSNLRWi1GtV9A3sGPg56uvG1vIZ9N/OO9rM8jS9oavSOwqaEhZYh3khq9K3fdpXWsbvdR3MoYCV/UOVadcOvv2C/AG9IYAfue5j1/U0R5mIhNctxM8yvxLyMVpOduJyLRRnto1MkXK23axlB27sXtT1z//8vqDTt3vk/fMGnX4xGyhiEI2Qi6X1Ww2S7lCIQ3DkCxzQEQKYADANgCbW6UHvwcRaO6fAwCjAewLYAKAcao6UkRIBEniEtRqNVOrVKjeSFCP61oaqurKvqe237P2lnkXn/X/PT9l3OT9Eql2V90QN1wZdRqSuhukhi9T3Q2s9ki+NDzHWppeUqnG/qsH/+b7fzSA33ruI7ODIDh/RCH6KkEZAEINfhia4n4ZO0KzphN5005Z06aRaeOAcjP++4Ff3P/86hWTLjr08i3FfEeurS3LUTanhVwe+XxOwjAw1loLoB/ASgBrAdSAV232Gc0NyJGt70+27mlrzNT6nAEwDcBMACO892kcx1KvN6hUqWu9Xka9XsfgUP/Qjcu+Nf3g6bO7zj7urBNT1F+quxLXfUkaMmDrviQ13+8THdqYqvuLZpfq+qrJNXFDbrp87t0v/cEAXr5iduiTMQvHd2QnKDC9+bC9NUfF9kwwgvNmBGW5Q3O2SFkzAkaCg/71Nz9+2MTZ6rlzLs4Vi0WbyWS5o63N5fM5G0VRaoxpA7ChBVw3ANMq1AKoHUAewCwARwHYvzWctQCeaNUrt4pvgeha17Gtevt47+M4jrVSqZlSqepqjQpVyyX/8xU3VBHF2T//+OeOFbgXaq5fa75ENR3SarzDxDToYz846FTORbPRV7oHG9sm+qEPX3TEM3vc9pm9AfiBP53+T6Pbwo0Cd4aog4p/yXK+lDX5IDIFZDinGS7CckEM+JB//u9/e3Z8NGPTgjl/Maq9s8N2FNtcPpc1bW1tFIZhaIxJATwFYA2AtAVWh4hERBQByIgIE1Gsql8gou8AeAjAfQAeVdUvEtE9reFFIpIloiyATgARgCqALQAGmHmUtTYTRWHDhhaGYE0YYmbHEXZj//rBRc/fXTly5qGHEus2FUceCbxP4DShRJ2mvuIFboyqG5kNcNuWVM965MbNd71pAC99+vADA+MnR6F+TeAg6h1TeE/I2bbAFjVLBbJcpIDzZNke8qNf//yxKblZWz42+9Pj2opFbutop7ZCQdva2hAEQZGZXwGwDEBDRCJV7VTVfVV1BDNPUtXZqnomER2tqi8S0REAzgJwUqvMI6JBAM+p6pdU9f1ElGu1E6lqUVVZVYWI6gA2EFFijJmSiUIPsDbXmGT3b59V6Kv0dd334uLGYTPmHK7Q7lRi65DCawqviXWSrEm1PlvgWMh9KPbut+/77Ohtj/97d98bA6igo7aM+O/Ogp0l8BNFPQhyY2RyE0MqcC7Ia2jyGpksBYj2//WDCx9uk/EDZ8783JhiW5HbigXpaG9HNpvNMXMGwAoR6SWiUKS5KhERS0QqIgmAHcz8sqrOA7AdwCcB9AK4CcBvAdwP4EVV3V9VPwGgC8B4Zv4PIqqoqgPQYObEOadExC1A60RUJaLxURQaZqoRW0NEsm/xgI6u7rV9L295vmvGlKmHQ32vk0QdxfA+oYTq+Vgbi70mR4p6BEaKlTid98S/9f4MV7wBgF/66AEnFbPUz+z/VNTBiywLgxxCFDgwGQqR5wznOeR8+6p1657r6uopfu7wv4mKbW0oFvIoFovIZDIBEXkReUlVG6o6Fs2N/EjvfSczj2Hm/YnoY6r6Ae/9w0T0cVXdSkTfE5FsC8iTAZwI4DAAjxDRj0TkUABTACxS1csAzG39MHlmzqvqGCLKt1xZA0Q0QERtQRBkDZMngrcmNAeMmB08uHpxNsrz2pFtbft4TWInDZtSLE5T8i7uSKRS8XDjBX4fYbnusI2jMkt/tGP9rnjxrl+gICP4Riagrzb1ssKa4CkrYRhwwBFHYGSUOZJKo8oPP/vCoV846opSoZCnQj7HxUJRMplMgGblR5h5wHtfbE1oZAvIHBFtVtX7RKTQ4pSrnHOXAThQRK4BcIaqNkTkRRF5UVUTVf1462/TVPVSEfm2974qIm3MvBhAl6pGAEYAaBcR45zLiUiPiDxKRC6bzZpsNhtGUaj5fIG/dNTltYeeWja3ltbVcGgMZX1IWbUUqDUBbBA+OYxDPuDLSORq6KsN76s48MvzZnwwlzNDgaFzAIBAi0LKtGVtEQHlOaQCQpOHoWDWL+9+ZODCuV99cnTbmM5cIY+2JudZIpronHukxUWemavOuZIxpuG9H8fM8wDMJaJHVfV0ANcDOIyIPg5ghTHm+0S0UETWq2oCoA/AI6r6C2PMgyKyD4BPM/MggJ8COIGIFqnqV1T1YADbVXUjEfUaYxrOOcPMBVXdCmCutbZirQGIlIBwavucl2577NaJM6ftO1nJ9aY+YfEpvDryknamSNdAMQ1AGwxdc/DqDjz9k/7Nw5i96ixBSK/MhTRxJ7oUbracmWAoVGNCtRSCYOxLazfcN7VjdjK+beK4KAqpkMtpJpNRABNVdT2AowHUvffjAYgxZpNz7hUiuk9VT1LVWFX/iojuBfA1IrpfVRcS0Xne+6tUX33+M/zdew8AzxljLvPefxTA3xPRIufcpQA8EYUAFhPRSCKaKSL7EFGgqjtU1RDRZmaeGIbh1sh78s7LxM59R09um7585fqNdtqUMZOMMc4igE0DthSppcYWL80VTNbyX1QCPgNN1fJqDvzi0tnjQviObGia3Ee0JEAml+E8DOUo4pxaE4GUJz3yxJr9/vSIv+8uFAu2kM8jl8vBGNNJRE+q6grn3AZV3QRgi6q2AZjHzHNE5FEAp3vvv8HM8wFQSywvADAPwDgAi0TkPwDcBWDhcFHVh9FcXH9ARE4BMI6ZvyEiHwYwSVW/CeB0IlpERJeo6hwiepmIlnrvVzLzemZex8yDzDwZqlUikGGm6R0H66+evuPYafuNynvFkCCF4xjiBd67otN4C4GmEDAqTuVnR3++beWT/z5YfRUHio8/0dEe7DynJTUvswmmEiwxWcCDwGyee37j4ydNO6ucy+YmZMJQM5kMWWvHqmqPc24eADCzENEGAMvTNH2AiM5Q1W1E9GkR2cLM3yOiS0TkO0R0lao+zMy/8N7PBHAmEZ2C3YiIoKrdqnqjqq5i5j/x3n8bTQt8iapeKyKbjDGfFpEhAGOccw8EQdBhjPmQqk723rP3PrTWvhxF0Xgi6vHeayaTyx075fS7nlvxcPGgg8ZNIjHeSKRMdbEUIEHwEuCOA4DOvB25vSRnAfghMGxEFNRb7ZoM0HFNadFeIjvRgMFkhEDKbEl8Oqq7u3bs+/c9cXQUWo2iCGEYsqrG3vvHAPwEwL2qulZETnXO/Zm1FqoKVf2Bqh6qqr8SkW3e++tU9T4i+ntVnem9vw7ARQA6ReQ5AL9yzl3vnLsewK8APIfmovkiIrpWVWeo6t977x/w3l8nIluI6Dcicqiq/quqgpnJOfdnIvJR59wmEVlCRD9S1QeJKLHWmmw2hyAM9bhpp47q7q4d733aSVBlkBoNQGxgYPdVRZ82N5In9lS7dp42GgA483hMyUY0RXgwXzAjQgUtshp1WhOR5YgDzoiB0U2baqsPLB7z0oxxBxWz2Rxls1lh5gNVdbn3/rwWR68moi5VPZWZt4nIvgBGquoRAH5BRH+OprH4oYh8XlVPQXMvfIOI/BJAFxF1qupxRPRBIjpKVSe3dOtdInKbqj5PRIe3RHayiHydiMYDOIuZfyIin0HTfI4kIgAYa4y5UUQaAI4QkY8ZY5YR0aGq0kcE8k5NNS4t665u6G9r47xDCi8pqabsNbFe9WkoRvU0upYl8GunnqebX7kZQ00O9DipLbKjRfQTPWnXYyBTBxMBBiIML2IVkt20sf6B46d9rJjJ5chaQ0EQRAC2pWm6VlVXq+rZIvIXSZKELcX/Y1U9RlW/AWC8iJyqql9V1aOcc99W1SXMfAmAh1X1qy3O+rKIHCMiGRGptUqude9iIrqWiC4brisiDxHRt1X1KFX9qnPuowDGe++vUNUPishNLQkIiOjPVPVs7/02EVkLYHsYhtYYg0wm1FNmnZPftKF2lFPJisCIkhE1DFiFaNLr1i5R+PntGR5lFMcBLWfCxxbhrgkjgqMAjCKgkrWFX48KZ7RHJm8CziJLOXJpUNu4omAuOfbKOMxkKBOGHIbhHBG576qrrtLHH3/8QmaOdtdd/5tIROLTTjvtyc9//vN3BUGQs9aOA3CyiDxXr9dRrzfo2gf/Ljt1TpyYIMnWtQ4nVW2kNd+bri41fOlMADkQerb1p4/f+WGcaS9X8HOLUQIwCgCUdFGi6ehBt7k+3k4DqQ8cOd2+mQdPnP6xijHB+MAYhGEoqppL03T/J5544iRmpvnz5z+4Zs2a1dOnT5/+8ssvr5o5c+aMWq1WSdM0VdXORYsWHW+tXXbmmWcONV2jQG9v744dO3b0jR07dvSIESNG3HbbbbNFpHPBggWPtMTvVUREWL58ee2VV145bcSIEU+ddNJJ1RY4unLlytXTpk2bEoZh2N/f37dw4cKTrLUdxWLxvnnz5pnf/e53unDhwhPa2tpWnnfeecekabopCIIMEYGIyBjGCfufvmbpltuKY6a4LKkzCh8PpZu913g0oIsAOhOKMQTElyvYPrsY43IRP6uK8wCAYHrUo+gpiXoaG+LR0X5VaNgxNEAHz5pz6PIgMGBmBTCKiJZVKpUjjDEmTdPG/PnzPwSgLCJHoLlY/omqXgLgWSJauHjx4uNPP/30obPPPnsAwGNoLl+O32Xdt/a3v/3txnK5HM6fP/+3aJ2JAAi89zkAUwGcdOqpp+YvvPBCnH322fEJJ5yQA3CH9/5YY8yft0C+SkTmP/roo72NRqPjhhtuODCTyRTPOuusRy+88MJVd9xxx8cWLFiwiog+oqp3ARgVBMEO7xVzJ70/v2jdHbNGqu/16uq98WakmuQgANhsU98MRQwMP7N0iYxhUuybD/n3WzqlAMROROElzfY3NrXHrtTNFHTkMvkiGQNiZhGZ7ZzbPDx5IoKIXK2qZzDzd9F0T/0pEV2qqoeKyN8BwLZt27ap6hmq+l0RmQXgZhH5iohcpaqrwzA0RATn3DXOueta5buqeoWqnqWqT9dqte8DwPbt2zeKyBGq+l1m/giA7wL4map+jYj2S5LEA0AYhp0AvsvMp5577rn3Axi/YcOGxaoKEdkCYBYzqzGEMMgUWILRjXSopzfekFUf5wUKYXYQCoZhykcM08C+DMUMw7Rva8sHqHZCJFD1VtTDaYLuoe3xrLGH/Yu1NiZVtcYAQEVVy7vpmPNU9VHv/RUArgZQ9d5f473/qYj8OwBMmDBhPIBnnXNfAfAj59w5AK4F8DURmcfM1JrY/4jIrSJyq/f+XlV9vmVMPlEoFC4GgM7OznEicmPrB3hJRC4Tkc+IyI+897cFQWBay5lrVfVKVX30lFNOOUZV/aJFiz7YMi79RFQiIgbg2NrazHEHf7+70q1eGiwkROoteQkhOmIYp8DQBGUcYIVwOJMepCCAkBCooCAnUPVwXoU1rrXVoyi7nwgoDO1QyymwzTn34d7e3p8B+NsWFx4AYLP3/l4iuoKIHhaR/yaiLw1z6rp169Z57+cR0bUiAiIaVNU7ReR5Y0xcrVbPbf0ek1U1DwCq2qOqG4jofhHZUi6XAeC7IkIAvqCqIKItaG4LZ4jInxERvPevtK5fY+b7W+0eBGD78uXLx6nqd51z85i5G0Bore1rNJJsxuan1EumFo3w3mtKSupAMASNRJEACBk6ixWphWCaKs1tqegVUIWyiBcPIYhRQlLKhQccNDtW9YEIh0TkiciJyGFtbW29LfCCxx577PtHHHHEhdbabd77bzLzFap6jPf+X5o46Jf333//qWh6kP+P934HMx8F4HQA53rvkc/nl9frdYjIQbsw99SWy6opPvl8BQC6u7u3ENFfq+poVb1IRK4iIvHeX7dy5UpKkuR8Zka9Xv9WNps9n4j2B/DNkSNHnrV9+/ZRIvIhIjpMVZeoqlfVEcyQ6WNmpQ8+nyva9m4IO/XeQ1XFE6UKfYkUhyrTEVDEFkAWO4NuZAuAsPnDKlgFzih8ku0cU5y4NQiCxFrLAPYDUCOizxpjrgAAY4y54YYbvtwS5f1E5B9UdSgIgloURR8BIESEO++8c8qmTZtetNYeHYahdnR0wHv/pIhsrVarvX19fQsA5H71q1/dYq01pVKpkCRJXCqVaGBgwDcaDdfX1zcRwDELFy788JIlS96XJEnBOQcADSIKmfkSIsKwpXfO/bmItBljLlHVa6dNm/bIE088sR+AMUT0WRG5kIgmWWtfIWPcuPZJDJ9r90hIRVTEq5KAlBIIdYH0UCg6FMhZUvDvjSDVnZBhUhUSUijICxHCbDFXZGOMqKoH0KmqQ/l8/ptdXV0/rlar38rn8zs5hJmJmUM0jyPb4/j3h/ze+ylLly6dgr2QaepX3Hnnnefv7ZmdoyUamyTJWABoHvTtmbq6un4xa9asSQCuA7DSWvtSo9E4zHt/dbFYvKLRaKwF0E5EwoBENlKVMOPFkcJDCRBVUlEloLQTLgWz1987FAhImCECJVEh8Z6cdzBk20ITkIg4Y4xX1ZFoHuJM3XfffT/S29uLLVu2oFKp7HQ9/W8ia+2RzHyGqv6TiPzjsccei97e3kxbW9uZACYTURVNb7mIiIYmJIOwLUWqTqQVIqFEDFHV6nC7orDMBB22LOzhWbRC0LJRLalqGYqyQWAJVDPGVJIkqQPYrKq9AGCMmQoAaZpix44d2Lx5M/r7+5Gmbzn4822jVatWvei9/9M0Ted77/9j5syZawAk27ZtswCgqt0AtohIzRhTssZWDdvQkA4RtETaxAOqZSWWnXgR1Kr8/kTbG2ThtaAE9QQSZWIQ2EilFteyhoJCa4lxYMvf9xry3qNUKqFUKiEMQxQKBeRyudcVsXeC0jRFrVZDtVrFzTffnOnp6Tl2/Pjx944ePXrt9OnTzyGirY888sjLCxYsOERExhPRDGvtswACrz4m60pOqIMIBIX4ZqCYAWsZLXumAtid6z8A5DSvlgkKFkcMiBERqHUDiUu8994SkQCoEFF+jyPfhZIkQX9/P/r7+xEEAbLZLKIoQhRFbzugzjnEcYxGo4FGo/EqCejp6Tnv5ptvfk2dH/zgB8sWLFgAVS0CqHjvyTlnq2mFYF3VORnJICKwI2IFI0Qi7TCtLaYCVgnbAdoA6GRhaoPXhipIVJkEUCXP7CrleBAd2RHsvYcxpopmfMreaICZN6LpQWYRmZSmaeeuk7LWIggCWGsRhiGstWBmWGuxqwUFABEZ9ilCROCcQ5qmcM7BOYckSYbd/XuiTczcT80YHHjvZ6MZZ4O+vr5hx+14Va1Qa/M9WB0Asa+SUCcIRuAtg5QEBKDYrEJrwdhiIXhBRQyIJkMxQxQvkELh4RUq4kCJ2VHdOLiOx+YmmTC0trWwnQOgsvtoiegFInKdnZ3rRo0aJT09PTw0NAQAm0VkzvBzw5N/B0mMMU+pqhk7dmxXsVjkzZs35xuNhojICDSPRpPt27c/WSgU5hLRC95722g0aOPgWnbcW5VUBYCSJYBBChgQzWnt2J4BsJyheFkVr7Q6Hc2kZYU6ARSejCjZFN259UOrc6reOucMEfWpqnXOPQIAhULhN8PgMXNl3rx5Y4IgOIuZz46i6KyTTz55JBFVmXnFO4nYrmSMeTKKooEPfvCDs40x8621Z3d2dp566qmnxsxcArC1s7PzkVWrVi1X1QBAv/eeiYg2DK0upOgpiCBQIlIBBOrBOgTCCAAQ0jUQrGS1WF1vUPewLlTlKoQCOARewOqVUgzmtlXWTWuKiqiIVAAgjuOtuy1bgtNOO21ET0/PhO9973sQEXznO99BT0/PxJNPPrkDQAO/97C8k7RBVaO5c+ce19nZmb3yyisxZcoU/NVf/RVWrFjx/kMOOWQ9M3dXKpVRjUYjbKmGinOOnPPYWt04PZGhjHoQCZigAQsFpFwbxqlRpx6k6LI6gK5Kpz8zm20d0JHWQFAYTSUlALDexSNdEB+Y+nQxpZRlppSZ4ZybdPvttz9QqVSOt9Y+SkR+xYoVxx522GF4/PHHceCBB2LZsmWYPn06nnrqqQOZ+REiekZERr+T6BFR37hx47rWr18/NwxDvPLKKygWi3jhhRdw5JFHolarzXvuuee60jSdYFordxFJnHNI0rghiGc4jb3xUDEQEngyYEBrwx7KcuJHZzux1t79KZQ++iv5AHTnCadVBZGQhULh1SsIMfoe7KlsGRqTm5Q1xmkQBJtV9dijjz766f06bwAAEgVJREFUnpUrVy4EgIMPPjh300034bjjjsOaNWtQqVQgIjjqqKOwZMkSzJs3b/Xy5cstgFUA3rZF954cr6eccsrYxx57DJ/85CexcOFCDA0N4cQTT0S1WsWjjz4azp49+4l6vc5Tp049TVU3eu/hVXVbZUN/TH33k8c4DVRIiMFEohCjCIdXLC6VY+44DV+zACCEXiiWgnCkEp1EpKsEqqTEIsTq1Axg+eCy/kczp+QmqDZfuXpRVedNmjRpx9VXX32hiEBEsHTpUtx5551YsGABnHM47LDDcNNNN+GAAw7Al770pc8NPzdsUXe1rsOA7n4dBmjXK3NzgbHrZ2beWQDg7rvvxq233oqLL74YS5YswY4dO/Dkk09i7ty5uOCCCz4bx/FPRGSUiNydph71ap2W9T9eGGgsr4iqZSVVsLJ6Z5lIlU5srfmWAlgHtE7lDjgP5SjgAWb6MBTtoroMgpwoERTwniiJhwq5aPrxB+YOWwuQIaKEmWd573NBEHSoKosIpk+fjltvvRWqitWrV6O7uxvLli3DV77yFRQKhVeBtzcgd/2+exmm3bl3dy4kIowfPx4LFy5EpVLBpk2b0Nvbi+7ublx22WWw1ro4jgsARgJYVq/XUG/Uk2fK95+ypXxfrESGGUIEMhYGTP1ovQOYOr2+kcjvVt+K9c130cp4slyX4nDnBqYbRCAGkTZXUELIVtPeezeUu3rjOEaSJFDVpwEcmKbpLcMTnDhxIm644QYEQQDTPDvBNddcg3322ec1IL1e8d6/qryZOruDffTRR+PrX/866vU6kiTBAQccgOuvvx5hGKI15hki8lTz76lura/fUUt6F4siJIKCiREAakhB6BnGp1ST9lwbngJ2CfE99Zd4cPzIcDqg4xl4wQl64EE+BlyicCnYanHz4RMumviR9vO7C4UC5fN5JqKzVfXlKIomtzzGr5nwGwGwOxe+ngi/ntjuXowxe/s+0Gg0+ohofxG5o1KpoFqv6+LBn496dssPt6dcmWAtlCOCNRDKgJgxEopDoLRl60Cy5p5P4Hhgl/A2NbgmTuUGBeCBOUTokVZAtyiIFJSk5QmJlJKeyvaeer2u9XpdVPVxVZ1Zr9dv25PI7Q7M3sDbEwe+0Q+wt/b21vdwqdVqv1XVaar6eJwkqNdj9JY3bW9IKU5cZRwUDNPcuagBE2G7Kg5RAKnI9SD832HcdgJIARYOVdyknXtjoTpBoaRsTPOMHQy7fMutQy/qQzOr1arW63VNvd+kTc/NfO/9I3vTXXub0N5E9/U+v57Yvp7+VFWkabpYVc8DMJSm6aZyqcSNRk1fxOMHPb/5v+pQtWwgUBCxErGCiOJhXHYMuRkU4r7XAHj3aYhTAaC4rakI9dNkMMSWPBhMSsRKmjRKIyuuZ3Bzfe32crnGlVJJReQ+Vc3HcdyuqgPD4re3ib1ZHfhmVcDuYO4JxNaYetI0HYvmMen91WqVqo1YNqVdW2uutz9NSp3KTNpcxMEYgjEYVNULmvVxiwLVu09D/BoAAcAZXL6j7F9SBVRgiUwPkRJYCQaqrEoMWrrqp4WN2ZfmxXGtWq7UqFwuJyJyP4A5cRw/qKryelywNw7ck+58I336ZvtR1Uaj0XgewMEicl+5XPblcpXqtXJtk33x1KUr/6MAbnKdgQKsDFUVMTtUYFWBvpLvohRX7orZqyJU192K6tSz9Qv5HPcQaCpBZyvjRSiyEFIVkDioiBbL1W3LglGduWJ9LKDExnAtCIJEVU/w3t/MzIfsbiD2dn0jHbkrF+1qSPZkXHY3MMNX59ydaB5ePdNoNLZUqlVfrpSxOvO4earr5xvqvm8iGfggBFNIyiGYQwwQ4xwABqqLhmo+c885eJVf7NUx0gDE4iv9Q/JYc1+MDABvDJQs2DDYhlBmxD2Da6YNxOulW9dsr1TLWiqVtF6vrwawXFU/7Zz7TwB/FCf+MUuW1ylJmqY/F5GzVXVZvV5fWy6XaahU5q26asuA22L7hlbvR4a8NVAYKFsgMBACJZDm7mNHSZ41HpfujtdrovS7bkV58p/oRwpZ8zIIhwM0C0SLoBipCmqNnaHAhq3L7MT9D9mfhjIrrYRt3nu0fG9VAKd673+Npq8t82a5cW9ADdOb4bZdljfbRWSpNt9BeSJJknVDQ0MYHBqiwXRHd9+IriPvffpa4YBCE0I5grCFMRlSGFoF4DMt3ffDUtXLPfPxyzcEEADGnoNH01gWFLNmChQhgTJEOqiKQIQEAiPNU09+Zf3jfZNnH3yY9mVWasoFL16sMWVm3gzgNO/9KiJaq6qTdlfyewNv9+f+QNCGPz8qIgLgaFVdVK83egcGBk25UtWBel9f/4Q1x931yFUbYLWNIxgOoDYgDSJYE6IB8CEEjFKg1D2QdscVfHn9r/EaB+YeAdx8B9z0+Sgz8HxgeR6AMVB6hgzaVMk3Q/2JSQHvJOra+GTXlMPmfEi6o+d87NpTLyTeN5j5ZWae6b3fV0RuIaKZqmr3ZJ33BNzuAO4G0B7vMfOQiNyqzcBN8t7fN1QuN0pDJVQqJe2v9u2oTt9w0l0P/uNz3iQjghA2CMmEGXgOCSYDIqJuAk4AgHrDf7We6u/uPx97zO6x13fl1tyOtfucqRcXM+ZFAHNAmA2iu4gwRkBKos0jAVXy4vKvrHvslWlHHHZk2m1eQKJ5VfXOOauqG4Mg6FXVj4nIalVdpKoHqSrtsrzYed1VXAHsDaQ9caAQ0S0iMoqIPkBEDzWSZHWlXI6HBkvBUKWsQ2nf5uSA7SfeueTqFxPUxtpQAxMSmxBqAhKTBZhoBYALAUCBW3ZU/D6Lz8E1e8NprwACwKQv4nf1fvlUMWsJwEgC5oDpIVJ0EhGrJ6sAICCXuvYVqx8uzXj/YZPSWFbWelyHeA/nPRLvqwxa3XRN4COqugrNKPwx2ozifxVww1y3K4CvA95WAHdQ8xWHDwJY4b1/tlwupwNDVVTKQ9rfP6j19h3dsv+Ow29bdEWvUmO0CWBshowJCTZL3kQAW1pPTb1noPTK9oG0no7Cp9b/7LWi+6YAXP8zuMnn4rFG4kfnQ3MYgIgIU5jxDCmKCigBpE1xZlEfvPDSErffrFkU7BNQpSutxQ1PLo6zSerFi9RV/CvMXFXVQ1R1H1VdhGaIbxnAzgQ5u4vtLsUx8yMA7mPmbQAOJKI2VV2XJMlLtVqtViqVaLBUlUqpn0vloTofOhBVMptzv1h4dd4Yn7cR1GSJwwhiQhIbIjUBthBwJoC8ElzvUHqzKL5+/+l4zQuGu9Kbyplw4m04Ix/xjI68+W6r2gZifdI1dFSaEEtdOW2AJYG6hnqXEMaOnL7ptGO/+L5kjVks2/JjM5nIZKJAoihLmUyIIAjIGANjTEBEHSIyWUQ6RWSdqm5V1YqIpC3RDImoQETjiGgKM5eIaKOIDKpq4r2Hcw6NRgO1egzvUq3V6l5Hxhuys9OPP7T0lke7tj41nQNiG0FtBmojeBMR2yzIRNhKQh9U6L6kkMGq/7t6Ii8uXoDfvRE2bzprx0n/hc93FLiQi8x1zYq0CdAHvcdkV4V3Dupi9b6OgosR+wRGvU3PPuXSHcXcPiMGnvAvcJIZlwsjG2UzMESUzWa16SExZGxLGFS9sVbFK5SUAGBYWYoIMzN5BbnUgSCaph5xXCfvvSZJouVaw1NWejrfL3NK1a07frHwmpFsXcgRvA3hTRahNeRsHmKaXpZtIDoa0P0AoBb7SwZqEt+/AP/6ZnD5g/LGnHwbvtlZCAYzAYbzJwwo4U5xOl0aUB8jcDHUxUSuoQ4pJE0gmbCt9vFTLm4UM2NHDCxNlidDweiQOAyCUDkwFLBBEFhSZrVEqkDzHLEVAiA6PFBFE0pFkjhS9YjjVJ1Lkfg0sZ3SO+rI8NBSo7vvznuuz8S+lDMhwBbWhmRtVr3JgmwAmAhqAlolij+h5svfqMW4ZKiaFu49F1e/WUz+4MxFJ92GS3MR246M+bYSGEAizD8mJ4d6p+oa8L4OcQnUJzA+hhWnqU+gUdA2cPKxnylNHj/rmOrW9N7+F5JGOiQjyXIYcgC2zRejiVXFw5Np5Y3xMGxgxBMJPMSlFHtPUI1NG/eNmhNm8uODUzZse+nB+x78WVs9KXXaDMgYspyBNyG8iQATwIRZwIawYPOCQj4LICSFDNX9V6qJ5O5bgH/8Q/D4o3JnnfhzfC6yvM/IdvPXADpaLd0KoaJPNS+xmjSF1QYkTeEkVfYpGR8j9Q5WRKvjRkztPf5DC3j0iCkn+AQvlDdUu6rbXaPWn5KrCEEErTwXTTKALbDmRgSaGxNk26bmppoQc7p7ux546PE7ZHvfutHGUJ4DOGMRmEi9sSQcwgYR2GTgOCRvDFXVaJUU81sA9PcM+X92Trru+yT+8w/F4o/O3nbyrTiaGF8cUwgOIMZRreZegerDgB6YJiQSw0uqgYsh3sFrjMB5eE1gfAovHka9pjaM+ke2TxiaNnWujBkzOcxnO/KFXKHNBpnRAODSRm+lVh6q1odqPT0bkjXrnuW+oS3tLo1HsKGADIQDsAnhjEFAFgmHsDYCmYBSG4BMRgMQvQTQcYBOBwBVPN5TStd6hxvuPx9L/xgc3lL6u5N+hpGwuHl0u33a2N/nDiTSXxBIRHWCNMilMdQ7DSVF6h1YUxXvyKhD6h0CCKCCVLxa9YASKYlyK/AOIJAyCUFBDGImB4KlEEoMbywCCtQbQ8QhxFiEJqDYWLDJakBEm4g1UKFPDI/Rq16xY9AdZQzOXzgf/X8sBm85AeM5t8P0eXwtItYRbfZToOavCyDxKj81RCPgaKJ3iL1TAw9xCVgdvHcw6uBVm/pNvQIKpwJV2pkKBQCEFKoMYoKFITVGQQxPBsZYeLIwNoQQw3BAjiNEzNioQKzAebQzkJRW9lXcbXEqctx5uOryYUv1R9LblkP1+JsxjS1+MDJn7wkDuhKEHACQQqD4OUgExJPFq/EpqTglcXDqEXoPJYETDwbgROBVAQY7ABCIJQKYYQBYZogaWGMAMkhhEJiQPLMaG5BTlvWUsgXjvJahAxS1RqpfH6i5eYjxhfs/i7clj+rbm8VXQSf/HB8T4LOj2uwzgaF/0GZ2oeHuVqjq48zIQzHee4QiSLUZgwN4kDYdt0Kkqq38BM1XhYnAMMwKGDQ979y0rERIRbENQJWIPgDorF0m2Ei9Xt0/5N4njH+//zzc9XamRH5H0iAffiOC9gLOVeD8kXl7bxjyxYC+OqMv0VaoPsCEukAigNqg1EEEFlWBQKHUFC9SBoOYiEUhRDoIaInBiSgyBDpJoeN2m9qG2Mv1/SV3iir+s1zFbc9chLc97vgdzWR+uYIfugUnC/C3keUlHQXTaQiX7LUCox9en1XwIBENCqTcvM1FVe0gSAcMzYVgxN6a8IrrBit+IHFyrCF850Orcf/ll781Pfd69K7l0j/mJxhtLb4+ot2uDy3t1T30Vihxeml/2U1WxpVLPol3PA088O7/MwI6/ib819j2YDOb154vvBVSxfXdA+nEBz6Ns4G3T8e9Eb3mUOkdJsW++NT2UjpHVO/V5vrvrRfVh7f3pTNLdZyLdxE84N0HEEtOgMsRzukdcBUV2vRWwYOnbTuG3HZXw4J3wki8Eb2uQ/WdojW/RLz/n+CluKaZTMhzm4eJwB9aFHADFf1X7+X6h/4MG9+LubzrHDhM934KLyhoaSPB3/yx3Nco42+811UPfBbvWvD67vSu/0eb3enEn/K17RkeNExXvPHTvyfxeuVQQ0be9zn50hs//c7Re8aBw3T/Z+TScl3niuBm9cCbLLeXGjr3mA3yl+/1+N9zAEHQ6oA/rxLLBPF49o1Fl54vxVJ08Ge/kwvkN0vvPYAAHv8K6ur8BbVEnlNF6XUArNQS/ziJv2jJ5/Cm07W/k/SeWOE9UddvUJ5+pimpYhODTtyT1Y29fsOrv2fxhXj+vR3t7+l/BQcO0z2fc0ucEyeil+7OfV7xFYXI4gvx4Hs9zl3pPbfCeyA67cfmFiaziVX/BgCUcL1XGf27z/vz8S7vNN6I3t23oN8caW0//+lcF/0PC+4VIBJgZm2aPw3/y8AD/peJ8DAtOQEuZLfAQ0sK7Q0rbv6SE/Yen/L/017ojH8LZ5/xb+Hs93ocr0f/D6s769KBP+5xAAAAAElFTkSuQmCC\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "route_map_openstreetmap",
+ "name": "Route Map - OpenStreetMap",
+ "image": "data:image/png;base64,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",
+ "description": "Trip animation on the OpenStreetMap. Allows to visualize location change over time. Use Trip Animation widget for advanced features.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8.5,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-route-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"openstreet-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d3\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map - OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "tencent_maps",
+ "name": "Tencent Maps",
+ "image": "data:image/png;base64,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",
+ "description": "Show latest values and location of the entities on Tencent maps.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 9,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('tencent-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.24727730589425012,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.8437014651129422,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7558240907832925,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second Point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.19266205227372524,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7995830793603149,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.04902495467943502,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.44120841439482095,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"tencent-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details
\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true},\"title\":\"Tencent Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "here_map",
+ "name": "HERE Map",
+ "image": "data:image/png;base64,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",
+ "description": "Show latest values and location of the entities on HERE maps.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 9.5,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('here', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"here\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"HERE.normalDay\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAB/CAYAAAD4mHJdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAACWAAAAlgB7MGOJQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAwgSURBVGiB7Zt5cBT3lce/v18fc89oRoPEIRBCHIUxp2ywCAgIxLExvoidZIFNxXE2VXHirIO3aqtSseM43qpNeZfYKecox3bhpJykYgdjDkU2mBAB5vCamMNYAgQyURBCoxnNPd39O/aP7hGSEUR24L/uqqf+zfR77/Pe69/Rv6kWwcgPLRIJfZUAa7xez2xd90QBwDSNZKlkHJHAK+l09mUA7BP4vPpRUVExMVoRef+L998njxx9X57vPi/PnTsnO850yPaT7XLXrrflqjtWymhF+HA0Gp0wEp/kHymEQqG4ptJDGzf+um5RUxMSiV7Z3Lyt88L5nozgHJWj4pGmpqZav99PWve04onHHuswmViQzWb7ruZX+Udgv8/z3A+f/NGye1evxssvb+wo5PMfTZs6bfqcuXNHL7hlweh58+ZVAOTUpk2b0p9dvjyqqmrs/b8ejpUMc+unzjgUCsXjsYruE+2n1JY/NedM0zCi0VjA7/d7/f4AAgE//H4/vF4fOjvP9h5695C/oaEhcN/q1SyTzVdnMpnklXzTq4EplUsXfmaRCgC7du3cOn78+KfGj59Add3z1Md1vV7vqPa2D1sA4MYbZ6qUiqVX9X21i4TQcfX19QCA6urquN/vn0kAPRQKpYbTnzRpUhgAampqAEFrPjVYSql7fD4AgK5r2tV0AcDj8WkAoOk6JJGeTw2+nocLdsEu2AW7YBfsgl2wC3bBLtgFu2AX7IJdsAt2wS7YBbtgF+yCXbALdsEu2AW7YBfsgl2wC76mh/ppjIQgXVloPxVSBRV0rBe455P6+kTKBYF3tonxY/IWarry7DvI298Tgp0PR9RzACaN1NeIS100+EdvKXW3cMZvF8wCK10Sq2it2NAzakmukP/wmoP/KuId3BRUMg5uCfCSNVSKVn1rNto7Un8jLrUVqJ4Fi2eEQiEYBzOsy3SYL37TNQdzi8Q5FxkqJIQBsNLlYMGF/zqAJWBxSEogDAY+DJibYqTuRg4WFgO3OKhCYTExbKk5G/mbkSPP2DQhLA5IO/NhSz1MMP882BDgnAFQwdiVSs2vPVhYDIJLUMkBgw1favM6lJoZDDAYhKbAYsOX+rqAhcXAuQSIAKzhSy2vS8YmB7NYH4WCfM7kw5VaWtdpOO3bfWZJZVXgPxMX898bVsm6RhkTIseX29yyIErm/J5z5vwr6pvmsLYjBgeDwSpVJS/OmT1n1de+9qANZgLc4q9Dyj2qQhUhSSUAUCL7GBcchCymTEYBYNWqVXj30MGHT586PZEJ+WAul7ts8bjspd9QKDRNU2nz4z94YtI3H3oI+XwB//3j/9m77eRUUJ9/0eh4APGoDz6vCi4ksgUTmYyBC4k8RLGwtzF+EGu+tHqRqqrYtm0rXnzhhQ7G5cpsNnvyiuBIJFKnqvSd55772eilS5fhwIH9ye+/dPaEf1T9otW3T8GtiyYgGNBBymYEgLSbvakidu8/h01vnkYhcab1gcVs5tx5c6PHjh7DU0/9qFsINPb3939UZg28X11dXR0Qwtr9g8efqGtc+Bn89re/O7FhR9BXNaFm+n98uxHTZ1SDKQqKAihweZlITUVtXQwNs8fg+Bmzdk+bnmPdf/7bwsbGeO2ECaED+9/5XCxWuTGbzVpDwJpGNtx+28o77rr7bmzZsu3k7z+cMlHzeiPrvnoTwtVhFAVQHAZY4HBEoiAAeDXUjI/gyJGeQEd6TFj2tHYuXNgYy2azVe0fngiWDLNloHNFo4FZkXDsoTVr1+KD4x8U/3Ci1qP5PV7N74FeFUbClKDEriy57A5JANL5a68hnqoINL8OAPqbXbNp7clTxTVr1/oOHjr0MFXxq2Qy9wEFACnoY//6la9QAHj+9Q/eUL2RWkVXoWgqkhZBypRImkDKBFIWkLIk+h1JWdL+zrmeNCWSDFB0DYquQvWG637TcnozAKxbt45yTr8PAGowGBwVDAbvmT9/Pvbu3dddijV9WdUUUE0BUQm6kwaCYe+ljK/w8ruUdsYCBLlMEUQhoJoCygWM+LIvHTx4sGfevIbqYMD3BSFkJVUUrG5oaFABoPXwhd1UVUBVahtpKtoOnEV/gSHHgBwDso5c6XO6yNF24CNQTbV9qBRUUenuwz1/BoCZM2dplOJeSggWL1myFEII9IeXziIKBVUUW1QKo2Ci41Anei9kkWcY6Ex5R8qfc0wi0ZPF6QNnYeQNB2j7IQpFOtg0WwiBxoWNIBKLVQI6Z8rUqTh69FiWaFNmEIWgLFShoM5TZbIzgVxvFp6ID5rfA6JQgBAIxsGLJkrpAsycAcH4gN1gX0QPTW9vP5Grr58cJJTOpbqmjgWAnp6ei4QSEEJAKAGh1BbHCS2DLAFmMAgmICwObjDnyYMMAtJL9oN89vRc7KWUQtOUsSqhSggA8sWivSEh9qBxTiCEAGRwQARUVaB67Hf5pZAQlA0Ayrq2LTCogVyhlLURNEw55yYABP2+4ED3vHSClBKQ9jiFdHqvEBCMQzAOKYSt6/RqSGnbDPJRbgT93hAAcM4NyhjrBYDKylhswEEZJgYJFxDchnGTwSqasIomuMnsIDiH5GKIzUAQTsCVlZUxB9xLIUVbKpVEff3kiLTMfimEA7HP5bZgHMJ07mnJAiuaYEXT3jcZDMLkTgBD7exgBKRp9NfVTQwnk0kIKduoJGRH8/ZmhMNh4skc3DnEkDlAi4GbtjDDguVAmZM1M6yB68JyKsCGBqD373s7GAySnTt3gBDyFhWCvPHee/8HAJhTU5g0BMg4uMXBTT4AZSUTrGjBKpiwCnablQbDbZuyfTmAuRPMegA4euQopCRbaCaTOd2XSLzX3d2Nu+64bR7PnP3LJSCDMBm4YW9FWcmyQYMytsW+Zpfdsm1MdimAdMc7K29bMedCdzeSyeS76XT6jLNI4PGf/+w5aLqOu25IjOOWKcSg0jJjcLZ2ecsZD5TdybqsOxC0ZYpbJ58frek6nn/+eVBJHgecjXkqk2nu7Ozcdfz4cdx556rJN5C3m8v3jBt2xpdnazjysawNy5lUbKkrbmtZsWL5pGNHj6Or62+7k5lMy5CFNRQKTfN6tAMvvvhSRe3EOqx/4oXXLvia7qO6CsVZrey5154KB5YpKSG5tHs+5/ZsZnEIk6Ei1fLH73373i/09fXi0fWPpgyTLchkMqeGgAEgHA5/vjJWsf2PmzYr1dXV+K8fP7vjLxduWkY8ilpetQZPg+UJxh63lzqlNDi7gTa3fuPraz6bzxXw79/5FutP51am0+kdZdaQ/2kzDKNDUci51179w8pbP3er8sAD6+pnVCWy+/fs21LAqBnlMT50qJXFLq2a2L/5gaVy7N133j69u7sb67/7iFHIFf4tlU6/Ppg1kLGU8hYAywBMeOWV33gfXb9+1Q+ffDL+4Ne/AcYY/tS8PbV5++4Dhy+MopY2ZrLiidQDgDBSp5TS+Y7psS65ZOHsW26++eYosxje2PwGNm586eKzz/x027+sXWsBOAfgbULIQQAgUspaAA8BGAfnsamrq4u0tZ0Q333kkdGmZS3f8JNnlBXLV0AOilRKCS7sWYlxjlKxgHw+j5Y3W/C/Tz/NQ6Hgjp9seKZ31py5ajwe4wAtz9zdAH5OpJTPAqgEgL5USkpu4eLFHloqFXniYh9t3bunauuWrStisSi5//4vYnHTEkyZOhWqokBICcuy0N7ehr2trXjt1VeRzqTl3ffc81bjgsZELF4pQ6EAqa4eI6UEicfj5dhTKoCikynx6Bop5C14dJ2XcjmouipvvGFGoSJaWfr738/7tmzdjl/88pfIZjKwnH2SpmkIhSMYW1ODhvmNGFcztjhudFXR69Wgck58Hg+XEorH5ylDJYA8kVKOckpdB0ADIBOJhOzv70OhUFILuTzPZLNcSE6SfSlvJp0O5A1DN0qGDxLS4/OUAh6PGQqHC5XxeJEQgkgoRH1+L/wBP6LRuIjH4+Uf8gSAUwB+MbhzzQSwCMA0p/QUQADgNJ/PJ/v7+wnnnFiWkJZhKCYzKADoqiZUXeW67iGcSxKPx2QoFAo7AybnuE8COAZgHyHkxGXjeFAQEQCzANQCqAIQBeAH4AXgcex052w45TMcyQHIAOgBcBbAUUJI5uOM/wcaHmf3g9UM7QAAAABJRU5ErkJggg==\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true},\"title\":\"HERE Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "image_map",
+ "name": "Image Map",
+ "image": "data:image/png;base64,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",
+ "description": "Show latest values and location of the entities on image map. Uses configurable background image and coordintates in range from 0 to 1.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8.5,
+ "sizeY": 6.5,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"image-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iMTEzNC41MTgzIgogICBoZWlnaHQ9Ijc2Mi43ODI0MSIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC41IHIxMDA0MCIKICAgc29kaXBvZGk6ZG9jbmFtZT0id2ljaGl0YW1hcC1ub2xpYi5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjM1IgogICAgIGlua3NjYXBlOmN4PSI4OS45MDc4NTciCiAgICAgaW5rc2NhcGU6Y3k9IjQ1My43ODI0MSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzNjYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iNzIxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItNCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTQiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpvYmplY3QtcGF0aHM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIHNob3dndWlkZXM9InRydWUiCiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSIKICAgICBmaXQtbWFyZ2luLXRvcD0iMCIKICAgICBmaXQtbWFyZ2luLWxlZnQ9IjAiCiAgICAgZml0LW1hcmdpbi1yaWdodD0iMCIKICAgICBmaXQtbWFyZ2luLWJvdHRvbT0iMCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjcuMDcxNDI4LC0zMDcuOTAyOTkpIj4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM3ODciCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzY0ZTU5O3N0cm9rZS13aWR0aDoyLjk5OTk5OTc2O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDkwNi4wMzMxNSw3MDYuMTMzNjcgMy40MjkyLDE3Ljc5NTUyIE0gMjguNTcxNDI4LDc2NS4wNTA2NyBjIDE1MC40MzUyMDIsNi44MzM0MiAxNDYuMzkyMzIyLC0yNi4zMzQxNSAxNjYuNDM0NTQyLC0yOS4zMjAwOSAzNi4xNDM3NSwtNS4zODQ3NiAxMTQuMjg2NzYsLTYuNTI1NCAxNDguMzI1MDgsLTguNjIzNTQgNDMuMzc4MDgsLTIuNjczODUgMTQxLjc2MjIxLC0xMS4yMzA5OSAxODguODU1NzgsLTE5LjgzNDE4IDM5LjgxMTM4LC03LjI3Mjg0IDIyMS4zNjk5MSwtMC44NjIzNSAzMTkuMDcxNDEsLTAuODYyMzUgNzAuODI3MzUsMCAxNDYuOTE4NjcsLTEuNzI0NyAyMTguMTc1ODYsLTEuNzI0NyAtMzEuNjE5NywwIDExNy44NTUyLC0yLjU4NzA3IDg2LjIzNTUsLTIuNTg3MDcgbSAtMjUuMDkwNywtNjguMTI2MDYgYyAtNTIuNzk5NiwzNC43ODQ4NCAtNjUuODk1MSw1MS43NDg2NSAtOTUuNjM5LDgxLjQ5MjU4IC0yNC45MzEzLDI0LjkzMTI3IC0xNDAuMzk2NTMsLTE5LjEzOTIgLTE3OC45Mzg3MSwzNi42NTAwNyAtMTIuMjgxNCwxNy43NzcxNSAtNDcuMDAyNTcsNDYuNTQ2NTMgLTY1LjEwNzgzLDU5LjA3MTMzIC0yMC4xMDUsMTMuOTA4MTggLTU2LjAzNjcyLDQ0Ljk1NjY0IC02Ny43Njg4NSw3My4wNzgyNyAtNC44MDE0NywxMS41MDkwMiAtMTMuMzgwNDYsMzUuOTkyOTggLTIzLjQ0OTQ5LDQ2LjA2MjAxIC0xMC40OTY5OSwxMC40OTY5OSAtMzguMzc3MzMsNi4zODU2OSAtNDQuMDIzNDUsMTcuNjQ3NjQgLTE5LjAwNTAyLDM3LjkwODEyIC0yNS40NjUzLDEwMC45MjM1MiAtNjcuNjE3ODksMTAyLjA1MTAyIG0gMTkuMjgxNTEsLTYyNC4wMTQ2NCBjIDM0LjY1OTM0LC0xLjg3MzgyIDg0LjAyNzMzLDcuMzkxMzEgMTA5LjkwMDcxLC00LjI4NTQ1IDEzLjI4MTcyLC01Ljk5NDA4IDQxLjQwNzIxLC0yLjQ2MTM1IDY2LjgyODY2LC0yLjMyMDQ2IDM1LjMyMjM4LDAuMTk1NzggNjQuMzgyNDksMC42MzQ3NyAxMDEuOTE2Nyw1LjAyMzIgMjUuMDMwMzYsMi45MjY1IDQ0LjY2MjczLDM0LjI4NzIyIDU4LjUyNjk4LDUwLjY0MzkgMTcuMDk4NzgsMjAuMTcyNjggNjIuNzYzODYsLTEuNzE0NjcgNjYuMzA1NjYsMzIuMTM0MzMgNS4xMDI3LDQ4Ljc2NTg3IC02LjMyODQsNzguNjM3MjUgNi4xNDExLDk3LjM0MTUgMTkuOTY5MiwyOS45NTM3OSA1MC40ODY0LDE3Ljg1NTc5IDQ0LjYxOTMsODMuOTcxMTkgTSA1ODkuMTAyMjcsMzA5LjcyNzE1IGMgNC42NDM0NiwyMy43MjkyMyAxNS4wNjkwNCw3Mi43NzU3NSAxOS4wNjEyOCwxMzAuNjQyODggMC44NzIwNiwxMi42NDA0OCA1LjQ0NzE4LDI0Ljk5MjUzIDQuMjIyMzEsNDUuMjc3NTcgLTIuNTE3MjEsNDEuNjg3NSAtMTUuNzE3MDYsNDMuNjc3MjcgLTE1LjA5MTIyLDYwLjM2NDg2IDEuNDMxOTUsMzguMTgyMjQgMzAuNjEzNjEsOTMuODM3MTkgMzAuNjEzNjEsMTM5LjcwMTU0IDAsMjQuMTgwOCAtMi42Njk2NCwxMTUuMzkwNDUgNy4zMzAwMSwxMzUuMzg5NzYgMC4xNTkxMSwwLjMxODIxIDEwLjA2NDc2LDM1Ljg4MzMyIDEwLjc3OTQ1LDQ5LjE1NDI0IDAuOTQzNzgsMTcuNTI0NjkgLTI0LjQ3OCwzOS40NzAwOCAtMjguMDI2NTUsNDYuNTY3MTYgLTUuNDc3NywxMC45NTUzOSAtMzYuOTczMjQsMTAuODgxOTcgLTQwLjA5OTUsMjQuMTQ1OTUgLTMuODY4ODQsMTYuNDE0NTEgLTMuODY2Myw0My43OTczNSA0LjA0NjQ3LDU5LjQ0MTI5IG0gOTcuMzM3MzQsLTY5MS4wMDk0MSBjIC01LjAxMzMyLDM1LjUxNTk1IC00My42NTkwMSwxMS4zMTY1MiAtNTguNTM4NjEsMjMuNzgxMzEgLTIxLjMzMDE5LDE3Ljg2ODUyIC02Mi40OTk2NCwzMS40MzIxMiAtNzAuMTI0MzcsMzUuMzY3MDggLTM1LjA4NzYzLDE4LjEwNzkzIC0xMTAuNDcyMTUsLTE1LjE0MTk2IC0xMjUuNjE0MSw0LjI2ODQzIC0xNS45NTA2MywyMC40NDcwMyAtMC4wNzM1LDYxLjQ2NjQ4IC05LjE0NjY2LDg0LjE0OTI0IC02LjAzNTcsMTUuMDg5MjYgLTE4Ljg3NjcsMjMuMDE3MzQgLTI3LjQzOTk3LDMyLjkyNzk4IC0xOS43NDgyOSwyMi44NTU1NSAtNjkuOTc0MjgsNjkuODI0MTkgLTg0Ljc1OTA0LDEwMC4wMDM0NiAtNy40OTc0MSwxNS4zMDQwNCAtMy4yODQyNiw0NC40MjA0MSAtMy40NzA1Myw2My4zNDI4NCAtMC4xMjc5MywxMi45OTQxNCAtMC44MTAxNSwyMy4xMDM4NSAyLjQwMzQzLDI4LjI3NjE4IDQuOTYxNTgsNy45ODU4MSAyMy43MjA1LDI4LjExMjA3IDI0LjIzODY1LDUwLjYxMTQ5IDAuMjk0MTEsMTIuNzcxNDYgMC4wMTMzLDc4LjU5MTAxIDMuMDQ4ODgsODcuNjU1NDkgMi4zMTI1Niw2LjkwNTQ2IDQuMjIwMDQsMjYuNTY0OTcgMTAuMjEzNzcsMzYuNTg2NjIgMTEuMzU0MDEsMTguOTg0MTUgNC4zODczNyw0MC4xNTY2MiAyNy44OTczLDUzLjUwNzk1IDE5LjA1MDEyLDEwLjgxODU5IDQ2Ljg3NzgxLDEyLjIxODYyIDgxLjkyNjE4LDE0LjQ2MDU0IDMzLjcwMzQ1LDIuMTU1ODkgNjEuNTEyMTcsLTEuNDMwMzUgNzYuOTIwNzcsNi4xNDExIDExLjU4NTA4LDUuNjkyNjYgOC41ODE1MSwxNy45MzM0NCAxNC4yOTU0MSwyOS4zNjEyMyA1LjY0MDQyLDExLjI4MDg1IDMxLjUwMjYzLDExLjE1NjI3IDQxLjgwNDA5LDQzLjQ1NDg3IDcuNjA1OSwyMy44NDcxIDMuMDg1OTMsNDQuMTU2OSA2LjcwNzU1LDY1Ljg4NjYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Nzc3NzY2Njc3Nzc3NzY2Nzc3Nzc3NjY3Nzc3Njc3NzY2Nzc3Nzc3Nzc3Nzc3Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNDMuMjc3ODgxLDUxNy45NDY3OSBjIDAsMCAyMzAuODQ4Mjg5LC0zLjYzODA1IDI1MC4wMDg2MzksLTMuNjU4NjcgNy40ODIyMiwtMC4wMDggOC42MTk1NCw1LjE1MTk0IDE0LjAyMDksMTEuNDU4NjkgMjQuNTk2MDgsMjguNzE4OTMgOTMuOTA5NjYsMTEyLjkzNTg1IDkzLjkwOTY2LDExMi45MzU4NSIKICAgICAgIGlkPSJwYXRoMzc4OSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDM1Ljk2MDU1NSw1NzcuNzA0OTQgYyAwLDAgMTY1LjUyNDU2NSwtMS42ODQ1NCAyNDguNzc5NTY1LC0xLjY4NDU0IDQuOTQ3NDksMCA3LjcyOTkzLC0yLjg4MzMgMTAuNTM3NzEsLTUuNzI5NzcgOS42NjEwNywtOS43OTQxNiAyNS42MzE5OSwtMjguNTg5OTUgMjUuNjMxOTksLTI4LjU4OTk1IgogICAgICAgaWQ9InBhdGgzNzkxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzguMzk5NjYzLDY0MS43MzE1NSA0MzEuNzA1OTMsNjM3LjQ2MzExIgogICAgICAgaWQ9InBhdGgzNzk1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzkuMDA5NDQyLDcwNC41Mzg1OSA1MjMuMTcyNTMsNjk3LjgzMTA0IgogICAgICAgaWQ9InBhdGgzNzk3IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzAzLjk1NzYyLDY4Mi41ODY2MSAxNDYuNzk1NDIsMS44MjkzMyBjIDEwLjUzNDAzLDAuMTMxMjcgMTQuMzQzNzQsLTIuNjM3MzkgMjUuNDg3MTUsLTYuMzcyOCAxMC40MTIxMiwtMy40OTAyNyAzMS40MjQxNSwtMi42OTg5NiA0MS4zODUzOCwtMi43NzM4NSBsIDQwNS41NjA3OSwtMy4wNDg5IgogICAgICAgaWQ9InBhdGgzNzk5IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgzODA0IgogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDQyNi4yMTc5NCwzMTQuODkwOTggYyAyLjA2NzU0LDkuMDUyNzMgMS44NDE3Nyw1MS43Mjc3NyA2LjUwNzk0LDc0LjgzNDY2IDEuNjc0NzUsOC4yOTMzNiA4LjY3NTA4LDE0LjA2NTk4IDEwLjA1NTQxLDE0Ljg1ODYyIDQuOTAxNDcsMi44MTQ2MyAxMC44MTQ3OSw4LjE0OTgyIDEzLjA0NTc5LDE2LjA4ODMxIDYuNzU3NzksMjQuMDQ1OTEgMC44Nzk3Miw2OC40NTIxMiAwLjg3OTcyLDExMC42ODkzIDAsNi4wOTc4MiAxLjY2MDEsMzAuMTQ2NiAtMi4xNTU4OCwzMy45NjI1OSAtMi41NDA4NSwyLjU0MDgzIC0wLjI4MTYzLDEyLjk5MDY5IC0zLjQzNjc1LDE2LjE0Mzc3IGwgLTkuODQ5NDQsOS44NDMxMSBjIC0xMC4zNjcxNSwxMC4zNjA0NyAtMTEuNTkwMTcsNi41MjYxNCAtMTcuNzM4NDgsMTguODIyNzYgLTMuNTY3NzIsNy4xMzU0MyA1LjQwMjM1LDIwLjY3MjEgNy4zNTQzMiwyNC41NzYwMiAxLjkzMjE0LDMuODY0MyAtMS44NDIxNiw0Ljc3NzczIC0xLjc5MjM1LDcuNDQ2MjYgMC4yNTI4NiwxMy41NDQ4MyAyLjI5NzUsMzczLjkyNzEyIDIuMjk3NSwzNzMuOTI3MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2NS4yNDAyMiw1MTkuNzc2MTIgNC4xMTU5OSw1MDIuMTUxNTgiCiAgICAgICBpZD0icGF0aDM4MDYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMTYuNTMxNjUsNTA0LjE4Njk5IDMuODgwNTksMzEwLjk2NDM2IgogICAgICAgaWQ9InBhdGgzODMxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM4ODkiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzE3LjY3NzYsNTc2LjQ4NTM5IDEzMC4xODc0MiwxLjUyNDQ0IGMgNC41MTA3OSwzLjI0MTY5IDIwLjM0NDcxLDcuOTY4NTMgMjcuNzQ0ODYsNC4yNjg0NCAzLjE1NTQ2LC0xLjU3NzcyIDkuNDE5LC01LjM4ODE3IDE0LjAyNDg5LC0zLjk2MzU1IDQuMjY2OTgsMS4zMTk4MSA2LjAxNjg5LDMuMTE2MzIgMTAuMzY2MjEsMy4wNDg4OSAxMC4zMDQwMywtMC4xNTk3NSAyMC4yMTE3LDAuMzg3NDEgMzAuNDg4ODYsMC4zMDQ4OSAxNzcuODkwOCwtMS40MjgyNyAzNTYuNTkwMzUsLTIuMTMyNDcgNTM0Ljc3NDU2LC0zLjA0ODg4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDc1LjMwNTAxLDU4Mi44ODgwNSBjIC0zLjQ0NDE4LDExLjM1MDY2IC0yLjEwMzQzLDEyLjQzMzczIDMuNjU4NjUsMjEuMDM3MzEgMy43OTQ0NSw1LjY2NTY0IDUwLjg2MjYxLDEzLjAzODQ1IDQxLjQ2NDg1LDI3LjEzNTA5IC0xMC41MzY5NywxNS44MDU0NyAtMjIuODk3NDUsLTUuNDc3NzIgLTMzLjg0MjYzLC0xLjgyOTMzIC01LjQ1MjM2LDEuODE3NDUgLTcuMzQ5MDEsNS40NTYzMSAtMy42NTg2Niw5LjE0NjY1IDIuODA2ODMsMi44MDY4NCA0LjA0OCwxLjgwMzk2IDYuNTIwMzQsNS4xMDA0MSIKICAgICAgIGlkPSJwYXRoMzkxMCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjAxMDgyLDYzNi44NTMzMyBjIDguMzE4OTksMTMuMTEwMTYgMTguODQ2MjEsMTQuNjM0NjUgMzUuNjcxOTYsMTQuNjM0NjUgMi45Mzg2NSwwIDcuODY5OTgsLTAuOTMzNzEgMTAuNjcxMTEsMCAxMS4zNTkxNywzLjc4NjM5IDI3LjE5Mzk4LDEwLjI3NTc3IDM2LjIwMTkzLDIxLjEyOTQ4IDguMjgwMDIsOS45NzY2MSAxMC4yNTI3OCwyMy44ODMwOCA3LjcwMjAyLDM3LjEwNDI0IC02LjE2OTg5LDMxLjk3OTk4IC0xNi43MTQzMSw1Ni45ODg1MyAtMTkuMDQzNTUsODYuNTY5MDUgLTEuMzQ3OTgsMTcuMTE4OCA0LjUwOTU3LDIyLjUzNTIyIDExLjA3MTQzLDMzLjkyODU3IDEwLjY3MDIzLDE4LjUyNjcyIDguNzI0NTMsMTQuMTk5NTUgOC41NzE0MywzNC4yODU3MiAtMC4xMzk2MywxOC4zMTk0NCAwLDYwLjI2Mzg1IDAsODAuNzE0MjkiCiAgICAgICBpZD0icGF0aDM5MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUyOC41MDgwNiw2NTguOTU3NzYgYyAtMTAuNjgxMjMsMC45MDQ1NCAtNy4xMDgwNCwtNS42MDI1NSAtMTAuODIzNTQsLTguMDc5NTYgLTQuNzg0NTQsLTMuMTg5NjkgLTEyLjIyNzA0LC0xLjI1MTA0IC0xNi43Njg4OCwtNS43OTI4OCAtMC42NjYxMiwtMC42NjYxMiAtOC44MDk2OSwtNC4xMDg3NyAtMTAuMTc0NDcsLTIuNzQzOTkgLTguMzY0NTksOC4zNjQ1OSAtMy4wNDg4OCwyMC41NTE4OCAtMy4wNDg4OCwzMy41Mzc3NCBsIDMuMDIyLDMzOS42OTc0MyIKICAgICAgIGlkPSJwYXRoMzkxNCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA1MTcuOTg5NDEsNjUxLjAzMDY1IGMgLTAuMjIxNzEsLTIuNzAxODQgMS45MDM0NiwtNS41NjIxMyAzLjM1Mzc3LC03LjAxMjQ1IDEuNzk5NDMsLTEuNzk5NDIgNi45MjI5NCwxLjAwNDE5IDguODQxNzgsLTAuOTE0NjYgMC4yODc2NSwtMC4yODc2NiAwLjg0MzI5LC0xMS4xNjQxIDAuMjI4NjYsLTEzLjU2NzUzIC0yLjA2NDgzLC04LjA3NDE2IC0yLjA1ODAxLC0yOC42NTY1OCAtMi4wNTgwMSwtMzguNzIwODYgbCAwLC03My4xNzMyNiIKICAgICAgIGlkPSJwYXRoMzkxNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNTI4LjY2MDUsNjc1LjQyMTczIC0wLjQ1NzMzLC0zMS41NTU5NiIKICAgICAgIGlkPSJwYXRoMzk3NCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc2Ni4zMTYyNSw1NzkuNjQ0MzEgMC40MzExOCwxMy43OTc2OCBjIDMuMTM2NDMsNC42NjkxNSAzLjAxODI0LDkuNjAwNjggMy4wMTgyNCwxNi4zODQ3NSBsIDAsMTU3LjM3OTgxIgogICAgICAgaWQ9InBhdGgzOTgyIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMTEyMi45MDAxLDc2NS45MTMwMyBjIC0yMDIuMzA2NjksNC42OTA1IC00MDMuNzQ0MDUsLTEuMTEzODEgLTYwNS45NTQ1NCwzLjM1MzkgLTEwLjg2MzYyLDAuMjQwMDIgLTMuMzYxNDcsLTguNTg2MyAtMjguNTM2OCwtOC41ODYzIgogICAgICAgaWQ9InBhdGgzOTg0IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA4NjAuMDA4MDUsNzM3LjA2NjUxIGMgMCwwIC05Ny40NDc1LDAuODU4MDYgLTE0Ny41Njg5MiwwLjg1ODA2IC01LjI2ODYxLDAgLTQuNTE1NDYsLTguMzI5ODYgLTcuMzAwODksLTguMzI5ODYgLTMuOTc0MzUsMCAtOC42MjkyNSwwLjAyMDEgLTEwLjUwOTQ4LDAuMDM1OSAtMi4zMzQ3NywwLjAxOTcgLTEuODEwOTQsOC4zNjU5NyAtNC4xNDU4LDguMzY2OTIgLTQ2LjE2ODk5LDAuMDE4OCAtMTY3LjQwNzY3LC0xLjMwNzk5IC0xNzUuMDUyNjMsLTEuMzA3OTkgLTQuNDI5NTUsMCAtOC41NzYyNywtNi40Mzk3MiAtMTMuMTMxOTgsLTYuNDM5NzIgLTEuMzYxMTUsMCAtNi4yMzg3MywwIC0xNC4zOTQ2NywwIgogICAgICAgaWQ9InBhdGgzOTg2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJNIDY3NS4wMDcwMyw4MzEuMTc0MDIgNjc0LjM5NzI1LDMwOS40MDI5OSIKICAgICAgIGlkPSJwYXRoMzk4OCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc5OS40MDE1NywzMTMuMDYxNjUgMS4yMTk1NSw0OTUuODY2NTMiCiAgICAgICBpZD0icGF0aDM5OTAiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA3MzYuNTk0NTIsMzEyLjQ1MTg4IC0xLjIxOTU1LDcxNi40ODgyMiIKICAgICAgIGlkPSJwYXRoMzk5MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUzMC4wMzA5NCw2NDMuNDU4NTkgMzkyLjM3MTU5LC0zLjAxODI1IgogICAgICAgaWQ9InBhdGg0MDQ4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gODU5LjQ1MDYsMzE0LjkwMTI4IDEuMjkzNTQsNTA3Ljk4MDU4IgogICAgICAgaWQ9InBhdGg0MDUwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjAuOTk5OTk5OTRweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gOTIxLjU0MDE3LDMxMC41ODk0OSAxLjcyNDcxLDUzMS43NTIyNyIKICAgICAgIGlkPSJwYXRoNDA1MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDczNi4yODk2Myw0NTMuMzEwNCAxODUuNjc3MTUsLTAuMzA0ODkiCiAgICAgICBpZD0icGF0aDQxODciCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMDYwLjgxMDUsNTE0Ljk2NzY3IGMgMCwwIC0zNjMuMjgxMjYsLTUuNjI2MTggLTU0NC42NTA0MiwyLjUyMTc4IC00LjE3Nzc2LDAuMTg3NjkgLTEyLjUwMDQ0LDEuMDY3MTEgLTEyLjUwMDQ0LDEuMDY3MTEgLTEuNTcwOTUsMC4xMzQxIC0yLjAwMDkzLC0yLjMyNDk1IC0yLjU5MTU1LC0zLjUwNjIzIC0wLjA5NjcsLTAuMTkzNDMgLTcuMDYwODEsLTEuOTMzNCAtNy42MjIyMSwtMS4zNzE5OSAtMi44OTMxNCwyLjg5MzE0IC03LjYzMTY3LDQuMjQ4NjkgLTEyLjE5NTU1LDQuMTE2IEwgMzY5LjIwMTcsNTE0LjUzNjUiCiAgICAgICBpZD0icGF0aDQyNjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzOTkuODE1MzEsNDc5LjYxMTEyIDExLjY0MTgsNS42MDUzIGMgMi45ODQxMiwxLjQzNjc5IDYuNTI4NzgsLTAuNDc3MTIgOS45MTcwOCwtMC40MzExOCBsIDEyNy4xOTczOSwxLjcyNDcxIgogICAgICAgaWQ9InBhdGg0MjYzIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gNTE5LjI1MTUxLDUxNy4xMjM1NyA1MTguODIwMzIsMzA4LjQzMzYyIgogICAgICAgaWQ9InBhdGg0MjY1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjkyNTQ5LDM4OS43MTQ5OCBjIDExLjA0NDk2LDAgMzUuNTMzMDcsMC42MTkyNyA0Mi41Nzk3OCwtMS4wMDM5NyA4LjQwNTIyLC0xLjkzNjE4IDcuMDY2LC02Ljk1Mzc4IDE0LjE5NzEyLC02Ljk1Mzc4IDcuODA5NSwwIDYuNTQyOTEsOC4wNjIzNyAyMC4xNDE3LDguMDYyMzcgMTMuOTkwNjgsMCA0NC45NzY4OSwwLjM3ODg2IDYzLjkzOTkyLDAuMzc4ODYgMTIuMDgzOTUsMCA4Mi4wMDI2NiwwLjMwNDg5IDkzLjYwMDgxLDAuMzA0ODkgOC43NjA0NywwIDEzLjE1OTcsLTIuMjg4MjcgMjEuMzQyMTksLTcuMDEyNDMgNy4xOTUxNSwtNC4xNTQxMyAyLjA1NDU5LC05LjQ5MTM3IDIwLjQyNzU0LC04Ljg0MTc3IDIzLjE0NTQsMC44MTgzMyAxMi42NDMzNCwxNC4wMjQ4NyAzMi4zMTgxOSwxNC4wMjQ4NyAyNS4zNTk1NCwwIDEzMC45OTkwMiwwIDE1MC45MTk4NSwwIDE0LjMzMjQ0LDAgLTQuMTE5MTEsLTEzLjExMDIxIDI5LjI2OTMsLTEzLjQxNTEiCiAgICAgICBpZD0icGF0aDQyNjkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU4OC42Nzk1NyIKICAgICAgIHk9IjczNS44MDQ2MyIKICAgICAgIGlkPSJ0ZXh0NDMxMCIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMiIKICAgICAgICAgeD0iNTg4LjY3OTU3IgogICAgICAgICB5PSI3MzUuODA0NjMiPkxpbmNvbG48L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY4Ni4zOTg1IgogICAgICAgeT0iNzY1LjYyODQyIgogICAgICAgaWQ9InRleHQ0MzEwLTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNiIKICAgICAgICAgeD0iNjg2LjM5ODUiCiAgICAgICAgIHk9Ijc2NS42Mjg0MiI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgIHk9Ii04MDIuMzc3MzgiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgiCiAgICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgICAgeT0iLTgwMi4zNzczOCI+V29vZGxhd248L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU2Mi4xMTkyNiIKICAgICAgIHk9Ii03NzEuOTY4MTQiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yIgogICAgICAgICB4PSI1NjIuMTE5MjYiCiAgICAgICAgIHk9Ii03NzEuOTY4MTQiPkVkZ2Vtb29yPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTguMzA0ODciCiAgICAgICB5PSItNzM4LjM2NjQ2IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yLTkiCiAgICAgICAgIHg9IjU5OC4zMDQ4NyIKICAgICAgICAgeT0iLTczOC4zNjY0NiI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICB5PSItNjc3LjIwMzk4IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00IgogICAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICAgIHk9Ii02NzcuMjAzOTgiPkhpbGxzaWRlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTcuMzI3MDkiCiAgICAgICB5PSItODYyLjYxNDA3IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNS0zIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgtMi05LTQtMSIKICAgICAgICAgeD0iNTk3LjMyNzA5IgogICAgICAgICB5PSItODYyLjYxNDA3Ij5Sb2NrPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1ODcuMzcwMTgiCiAgICAgICB5PSItOTI2LjEzNjYiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTktNy01LTMtMiIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00LTEtMyIKICAgICAgICAgeD0iNTg3LjM3MDE4IgogICAgICAgICB5PSItOTI2LjEzNjYiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9Ijg3MS4xNjEwMSIKICAgICAgIHk9IjYzNy41NzUyIgogICAgICAgaWQ9InRleHQ0NDY1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDY3IgogICAgICAgICB4PSI4NzEuMTYxMDEiCiAgICAgICAgIHk9IjYzNy41NzUyIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICB5PSI1NzcuMDMyNDciCiAgICAgICBpZD0idGV4dDQ0NjUtMyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00IgogICAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICAgIHk9IjU3Ny4wMzI0NyI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgaWQ9InRleHQ0NDkwIgogICAgICAgeT0iNTEwLjI2MTgxIgogICAgICAgeD0iODc1Ljk2NjQ5IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSI1MTAuMjYxODEiCiAgICAgICAgIHg9Ijg3NS45NjY0OSIKICAgICAgICAgaWQ9InRzcGFuNDQ5MiIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iODgxLjMxNjU5IgogICAgICAgeT0iNDUwLjE5ODc2IgogICAgICAgaWQ9InRleHQ0NDk0IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDk2IgogICAgICAgICB4PSI4ODEuMzE2NTkiCiAgICAgICAgIHk9IjQ1MC4xOTg3NiI+Mjl0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNjE1Ljc5MjQ4IgogICAgICAgeT0iMzg3Ljc0NzE2IgogICAgICAgaWQ9InRleHQ0NDY1LTMtMSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00LTEiCiAgICAgICAgIHg9IjYxNS43OTI0OCIKICAgICAgICAgeT0iMzg3Ljc0NzE2Ij4zN3RoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MTkiCiAgICAgICB5PSI0ODEuNjUyODYiCiAgICAgICB4PSI0ODQuNjkwMzciCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjQ4MS42NTI4NiIKICAgICAgICAgeD0iNDg0LjY5MDM3IgogICAgICAgICBpZD0idHNwYW40NTIxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj4yNXRoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NjMuMDQ2NzUiCiAgICAgICB5PSI1MTMuMzYxMzMiCiAgICAgICBpZD0idGV4dDQ1MjMiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1MjUiCiAgICAgICAgIHg9IjU2My4wNDY3NSIKICAgICAgICAgeT0iNTEzLjM2MTMzIj4yMXN0PC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MjciCiAgICAgICB5PSI1NzcuODk0ODQiCiAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTc3Ljg5NDg0IgogICAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgICAgaWQ9InRzcGFuNDUyOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzMSIKICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICB4PSI0MzMuNTgwNzUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICAgIHg9IjQzMy41ODA3NSIKICAgICAgICAgaWQ9InRzcGFuNDUzMyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+QW1pZG9uPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI0MDUuNTMwOTgiCiAgICAgICB5PSItNTIzLjU0MDE2IgogICAgICAgaWQ9InRleHQ0NTM1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDUzNyIKICAgICAgICAgeD0iNDA1LjUzMDk4IgogICAgICAgICB5PSItNTIzLjU0MDE2Ij5BcmthbnNhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzOSIKICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICB4PSI3NDUuNDg0NjIiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICAgIHg9Ijc0NS40ODQ2MiIKICAgICAgICAgaWQ9InRzcGFuNDU0MSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+V2VzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTk2LjcyODMzIgogICAgICAgeT0iLTUzMS4yNTkyOCIKICAgICAgIGlkPSJ0ZXh0NDU0MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NDUiCiAgICAgICAgIHg9IjU5Ni43MjgzMyIKICAgICAgICAgeT0iLTUzMS4yNTkyOCI+V2FjbzwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU1NSIKICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICB4PSI1OTUuNDM0ODEiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICAgIHg9IjU5NS40MzQ4MSIKICAgICAgICAgaWQ9InRzcGFuNDU1NyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+TWF6aWU8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgIHk9IjE2Mi4wNjg3NyIKICAgICAgIGlkPSJ0ZXh0NDU1OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC43MDcxMDY3OCwwLjcwNzEwNjc4LC0wLjcwNzEwNjc4LDAuNzA3MTA2NzgsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjEiCiAgICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgICAgeT0iMTYyLjA2ODc3Ij5ab288L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjI0MC41ODk5NyIKICAgICAgIHk9IjU3NC40NDU0MyIKICAgICAgIGlkPSJ0ZXh0NDU2MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU2NSIKICAgICAgICAgeD0iMjQwLjU4OTk3IgogICAgICAgICB5PSI1NzQuNDQ1NDMiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU2NyIKICAgICAgIHk9IjUxMS42MzY2MyIKICAgICAgIHg9IjIwNi4wMzE3NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTExLjYzNjYzIgogICAgICAgICB4PSIyMDYuMDMxNzUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjYyMC40NDMxMiIKICAgICAgIHk9Ii01MDYuNjgyMTkiCiAgICAgICBpZD0idGV4dDQ1NzEiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NTczIgogICAgICAgICB4PSI2MjAuNDQzMTIiCiAgICAgICAgIHk9Ii01MDYuNjgyMTkiPk5pbXM8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU4MyIKICAgICAgIHk9IjY5OC44NDAwOSIKICAgICAgIHg9IjM3MC4yMTY4NiIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNjk4Ljg0MDA5IgogICAgICAgICB4PSIzNzAuMjE2ODYiCiAgICAgICAgIGlkPSJ0c3BhbjQ1ODUiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1hcGxlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSIzODQuMDg0MiIKICAgICAgIHk9IjY4MC44NTEzOCIKICAgICAgIGlkPSJ0ZXh0NDU5OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDYwMSIKICAgICAgICAgeD0iMzg0LjA4NDIiCiAgICAgICAgIHk9IjY4MC44NTEzOCI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzNjcuOTA4MTcsMTAwOS45NTk2IDI2My4wMTgzMywwIgogICAgICAgaWQ9InBhdGg0NjA1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDciCiAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgeD0iNzM2LjI2NzQ2IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgICB4PSI3MzYuMjY3NDYiCiAgICAgICAgIGlkPSJ0c3BhbjQ2MDkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1lcmlkaWFuPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ5NzkiCiAgICAgICB5PSI2NDAuMjA1MjYiCiAgICAgICB4PSI1NzIuODMyMTUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjY0MC4yMDUyNiIKICAgICAgICAgeD0iNTcyLjgzMjE1IgogICAgICAgICBpZD0idHNwYW40OTgxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NzUuMDg5NjYiCiAgICAgICB5PSI2NzAuOTAzNSIKICAgICAgIGlkPSJ0ZXh0NDk4MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDk4NSIKICAgICAgICAgeD0iNTc1LjA4OTY2IgogICAgICAgICB5PSI2NzAuOTAzNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNDk5LjQ4OTYyIgogICAgICAgeT0iMTAwOC42MDY5IgogICAgICAgaWQ9InRleHQ1MDQ3IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5IgogICAgICAgICB4PSI0OTkuNDg5NjIiCiAgICAgICAgIHk9IjEwMDguNjA2OSI+NDd0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iMjE2LjY0NTQzIgogICAgICAgeT0iNzI1Ljk4Mjk3IgogICAgICAgaWQ9InRleHQ1MDUxIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDUzIgogICAgICAgICB4PSIyMTYuNjQ1NDMiCiAgICAgICAgIHk9IjcyNS45ODI5NyI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICAgPGZsb3dSb290CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgaWQ9ImZsb3dSb290NTA1NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6MThweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIj48Zmxvd1JlZ2lvbgogICAgICAgICBpZD0iZmxvd1JlZ2lvbjUwNTciPjxyZWN0CiAgICAgICAgICAgaWQ9InJlY3Q1MDU5IgogICAgICAgICAgIHdpZHRoPSIzNDMuNTcxNDQiCiAgICAgICAgICAgaGVpZ2h0PSIxMDMuNTcxNDMiCiAgICAgICAgICAgeD0iMTkuMjg1NzE1IgogICAgICAgICAgIHk9IjE3LjE0Mjg1NyIKICAgICAgICAgICBzdHlsZT0iZm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIgLz48L2Zsb3dSZWdpb24+PGZsb3dQYXJhCiAgICAgICAgIGlkPSJmbG93UGFyYTUwNjEiPjwvZmxvd1BhcmE+PC9mbG93Um9vdD4gICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDYwNy03IgogICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgIHg9Ijc3NC44NzU2MSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgICAgeD0iNzc0Ljg3NTYxIgogICAgICAgICBpZD0idHNwYW40NjA5LTciCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1jQ2xlYW48L3RzcGFuPjwvdGV4dD4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzY0LjE1OTk5LDY1OC40Mjg5MSAyOTkuNTEwMjMsLTEuMDEwMTYgYyA2LjQ5ODcyLC0wLjAyMTkgNi45NzcxOSw5LjI1NDEyIDE2LjU5NjMxLDkuMzkyNDcgMTIuMDU0MjcsMC4xNzMzOSAyOS4xMTA4MywtMC41MzU3MiA1NC4xMTQzNywtMC4zMDExIgogICAgICAgaWQ9InBhdGg1NDQwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgIHk9Ijk0NC4zNTc1NCIKICAgICAgIGlkPSJ0ZXh0NTA0Ny05IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5LTMiCiAgICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgICAgeT0iOTQ0LjM1NzU0Ij5NYWNBcnRodXI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDctNy0xIgogICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgIHg9Ijc4MC44NDYwNyIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgICAgeD0iNzgwLjg0NjA3IgogICAgICAgICBpZD0idHNwYW40NjA5LTctOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+U2VuZWNhPC90c3Bhbj48L3RleHQ+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2Ny42OTU1Myw1MzcuMjEwNiAxNDEuMjgzMDMsLTEuMDEwMTUgYyA2LjQ4OTk5LC0wLjA0NjQgMTIuNzgxMTQsNy4yMzU0NSAxOS4xOTI5LDcuMzIzNiA1NS45MjM2MiwwLjc2ODkgMTU4LjY4OTk3LC0wLjE3MzMzIDIzNi41MTQwMiwtMS4wMTAxNSA3LjgzOTU2LC0wLjA4NDMgMjIuNjMxNDcsLTE5Ljg1MzU1IDMwLjMwNDU3LC0yMC40NTU1OSAyMi4yNjU4OSwtMS4zNTE4MSA0NS4xNzk0NSwtMC41MDUwNyA2Ny42ODAyMiwtMC41MDUwNyAxNi4xNDczMSwtMC42MzI0MSAzLjYxMDE2LDIwLjcwODEzIDI2Ljc2OTA0LDIwLjcwODEzIGwgMjQzLjQ0Njc5LC0xLjAxMDE2IgogICAgICAgaWQ9InBhdGg1NDk2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzY2NjY2MiIC8+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI2ODUuMjA4MTMiCiAgICAgICB5PSI4MjcuNTMwODIiCiAgICAgICBpZD0idGV4dDQzMTAtNy04IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtNiIKICAgICAgICAgeD0iNjg1LjIwODEzIgogICAgICAgICB5PSI4MjcuNTMwODIiPlBhd25lZTwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSA1NTQuMjg1NzIsNzIxLjQyODU3IDU1MCw1NDMuMjE0MjkgNTQ3LjE0Mjg2LDEwMi41IDU0Ni43ODU3MiwyMy4yMTQyODUiCiAgICAgICBpZD0icGF0aDU1MTkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIiAvPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTI5LjYyNTMxIgogICAgICAgeT0iLTU1MC44NDc3OCIKICAgICAgIGlkPSJ0ZXh0NDU0My01IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU0NS0wIgogICAgICAgICB4PSI1MjkuNjI1MzEiCiAgICAgICAgIHk9Ii01NTAuODQ3NzgiPkJyb2Fkd2F5PC90c3Bhbj48L3RleHQ+CiAgPC9nPgo8L3N2Zz4K\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false},\"title\":\"Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "google_maps",
+ "name": "Google Maps",
+ "image": "data:image/png;base64,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",
+ "description": "Show latest values and location of the entities on Google Maps.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8.5,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"google-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7568\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true},\"title\":\"Google Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ },
+ {
+ "alias": "openstreetmap",
+ "name": "OpenStreetMap",
+ "image": "data:image/png;base64,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",
+ "description": "Show latest values and location of the entities on OpenStreetMap.",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 8.5,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n",
+ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-map-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"openstreet-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"app_id\":\"AhM6TzD9ThyK78CT3ptx\",\"app_code\":\"p6NPiITB3Vv0GMUFnkLOOg\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}
Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}
TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true},\"title\":\"OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/navigation_widgets.json b/application/src/main/data/json/system/widget_bundles/navigation_widgets.json
new file mode 100644
index 0000000000000000000000000000000000000000..9d06c0122abf15e59b05dffb77da495d6c35e8dc
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/navigation_widgets.json
@@ -0,0 +1,48 @@
+{
+ "widgetsBundle": {
+ "alias": "navigation_widgets",
+ "title": "Navigation widgets",
+ "image": "data:image/png;base64,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",
+ "description": "Useful to define home dashboard of the user. Contains widgets that enable navigation to other dashboards and menu items."
+ },
+ "widgetTypes": [
+ {
+ "alias": "navigation_cards",
+ "name": "Navigation cards",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to open one or more specific pages using filter defined in the advanced settings of the widget.",
+ "descriptor": {
+ "type": "static",
+ "sizeX": 7,
+ "sizeY": 6,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "/*#widget-container {\n overflow-y: auto;\n box-sizing: content-box !important;\n cursor: auto;\n}*/\n\n#widget-container #container {\n overflow-y: auto;\n box-sizing: content-box;\n cursor: auto;\n}",
+ "controllerScript": "self.onInit = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onResize = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-navigation-cards-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"filterType\":\"all\"},\"title\":\"Navigation cards\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
+ }
+ },
+ {
+ "alias": "navigation_card",
+ "name": "Navigation card",
+ "image": "data:image/png;base64,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",
+ "description": "Allows to open specific page using relative path defined in the advanced settings of the widget.",
+ "descriptor": {
+ "type": "static",
+ "sizeX": 2.5,
+ "sizeY": 2,
+ "resources": [],
+ "templateHtml": "",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n\n}\n\n\nself.onDestroy = function() {\n}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "{}\n",
+ "settingsDirective": "tb-navigation-card-widget-settings",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(255,255,255,0.87)\",\"padding\":\"8px\",\"settings\":{\"name\":\"{i18n:device.devices}\",\"icon\":\"devices_other\",\"path\":\"/devices\"},\"title\":\"Navigation card\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json
new file mode 100644
index 0000000000000000000000000000000000000000..4776ef2aaeb1955a0008e1cba5a45a335e3f215a
--- /dev/null
+++ b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json
@@ -0,0 +1,140 @@
+{
+ "ruleChain": {
+ "additionalInfo": {
+ "description": ""
+ },
+ "name": "Device Profile Rule Chain Template",
+ "firstRuleNodeId": null,
+ "root": false,
+ "debugMode": false,
+ "configuration": null
+ },
+ "metadata": {
+ "firstNodeIndex": 6,
+ "nodes": [
+ {
+ "additionalInfo": {
+ "layoutX": 822,
+ "layoutY": 294
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
+ "name": "Save Timeseries",
+ "debugMode": false,
+ "configuration": {
+ "defaultTTL": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 824,
+ "layoutY": 221
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
+ "name": "Save Client Attributes",
+ "debugMode": false,
+ "configuration": {
+ "scope": "CLIENT_SCOPE"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 494,
+ "layoutY": 309
+ },
+ "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
+ "name": "Message Type Switch",
+ "debugMode": false,
+ "configuration": {
+ "version": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 824,
+ "layoutY": 383
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log RPC from Device",
+ "debugMode": false,
+ "configuration": {
+ "scriptLang": "TBEL",
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
+ "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 823,
+ "layoutY": 444
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log Other",
+ "debugMode": false,
+ "configuration": {
+ "scriptLang": "TBEL",
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
+ "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 822,
+ "layoutY": 507
+ },
+ "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
+ "name": "RPC Call Request",
+ "debugMode": false,
+ "configuration": {
+ "timeoutInSeconds": 60
+ }
+ },
+ {
+ "additionalInfo": {
+ "description": "",
+ "layoutX": 209,
+ "layoutY": 307
+ },
+ "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
+ "name": "Device Profile Node",
+ "debugMode": false,
+ "configuration": {
+ "persistAlarmRulesState": false,
+ "fetchAlarmRulesStateOnStart": false
+ }
+ }
+ ],
+ "connections": [
+ {
+ "fromIndex": 2,
+ "toIndex": 4,
+ "type": "Other"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 1,
+ "type": "Post attributes"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 0,
+ "type": "Post telemetry"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 3,
+ "type": "RPC Request from Device"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 5,
+ "type": "RPC Request to Device"
+ },
+ {
+ "fromIndex": 6,
+ "toIndex": 2,
+ "type": "Success"
+ }
+ ],
+ "ruleChainConnections": null
+ }
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json b/application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json
new file mode 100644
index 0000000000000000000000000000000000000000..f908b1661e011d316655544d23919a4d96016e01
--- /dev/null
+++ b/application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json
@@ -0,0 +1,181 @@
+{
+ "ruleChain": {
+ "additionalInfo": null,
+ "name": "Edge Root Rule Chain",
+ "type": "EDGE",
+ "firstRuleNodeId": null,
+ "root": true,
+ "debugMode": false,
+ "configuration": null
+ },
+ "metadata": {
+ "firstNodeIndex": 0,
+ "nodes": [
+ {
+ "additionalInfo": {
+ "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
+ "layoutX": 187,
+ "layoutY": 468
+ },
+ "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
+ "name": "Device Profile Node",
+ "debugMode": false,
+ "configuration": {
+ "persistAlarmRulesState": false,
+ "fetchAlarmRulesStateOnStart": false
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 823,
+ "layoutY": 157
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
+ "name": "Save Timeseries",
+ "debugMode": false,
+ "configuration": {
+ "defaultTTL": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 824,
+ "layoutY": 52
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
+ "name": "Save Client Attributes",
+ "debugMode": false,
+ "configuration": {
+ "scope": "CLIENT_SCOPE"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 347,
+ "layoutY": 149
+ },
+ "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
+ "name": "Message Type Switch",
+ "debugMode": false,
+ "configuration": {
+ "version": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 266
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log RPC from Device",
+ "debugMode": false,
+ "configuration": {
+ "scriptLang": "TBEL",
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
+ "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 824,
+ "layoutY": 378
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log Other",
+ "debugMode": false,
+ "configuration": {
+ "scriptLang": "TBEL",
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
+ "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 824,
+ "layoutY": 466
+ },
+ "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
+ "name": "RPC Call Request",
+ "debugMode": false,
+ "configuration": {
+ "timeoutInSeconds": 60
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 1129,
+ "layoutY": 52
+ },
+ "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
+ "name": "Push to cloud",
+ "debugMode": false,
+ "configuration": {
+ "scope": "SERVER_SCOPE"
+ }
+ }
+ ],
+ "connections": [
+ {
+ "fromIndex": 0,
+ "toIndex": 3,
+ "type": "Success"
+ },
+ {
+ "fromIndex": 1,
+ "toIndex": 7,
+ "type": "Success"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 7,
+ "type": "Success"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 6,
+ "type": "RPC Request to Device"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 5,
+ "type": "Other"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 2,
+ "type": "Post attributes"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 1,
+ "type": "Post telemetry"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 4,
+ "type": "RPC Request from Device"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 7,
+ "type": "Attributes Updated"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 7,
+ "type": "Attributes Deleted"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 7,
+ "type": "Timeseries Deleted"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 7,
+ "type": "Timeseries Updated"
+ }
+ ],
+ "ruleChainConnections": null
+ }
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
new file mode 100644
index 0000000000000000000000000000000000000000..88ef27e2f8a8b2fec11a65e5e7a9523b85c7b3f4
--- /dev/null
+++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
@@ -0,0 +1,140 @@
+{
+ "ruleChain": {
+ "additionalInfo": null,
+ "name": "Root Rule Chain",
+ "type": "CORE",
+ "firstRuleNodeId": null,
+ "root": true,
+ "debugMode": false,
+ "configuration": null
+ },
+ "metadata": {
+ "firstNodeIndex": 6,
+ "nodes": [
+ {
+ "additionalInfo": {
+ "layoutX": 824,
+ "layoutY": 156
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
+ "name": "Save Timeseries",
+ "debugMode": false,
+ "configuration": {
+ "defaultTTL": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 52
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
+ "name": "Save Client Attributes",
+ "debugMode": false,
+ "configuration": {
+ "scope": "CLIENT_SCOPE",
+ "notifyDevice": "false"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 347,
+ "layoutY": 149
+ },
+ "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
+ "name": "Message Type Switch",
+ "debugMode": false,
+ "configuration": {
+ "version": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 266
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log RPC from Device",
+ "debugMode": false,
+ "configuration": {
+ "scriptLang": "TBEL",
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
+ "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 379
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log Other",
+ "debugMode": false,
+ "configuration": {
+ "scriptLang": "TBEL",
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
+ "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 468
+ },
+ "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
+ "name": "RPC Call Request",
+ "debugMode": false,
+ "configuration": {
+ "timeoutInSeconds": 60
+ }
+ },
+ {
+ "additionalInfo": {
+ "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
+ "layoutX": 204,
+ "layoutY": 240
+ },
+ "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
+ "name": "Device Profile Node",
+ "debugMode": false,
+ "configuration": {
+ "persistAlarmRulesState": false,
+ "fetchAlarmRulesStateOnStart": false
+ }
+ }
+ ],
+ "connections": [
+ {
+ "fromIndex": 6,
+ "toIndex": 2,
+ "type": "Success"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 4,
+ "type": "Other"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 1,
+ "type": "Post attributes"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 0,
+ "type": "Post telemetry"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 3,
+ "type": "RPC Request from Device"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 5,
+ "type": "RPC Request to Device"
+ }
+ ],
+ "ruleChainConnections": null
+ }
+}
\ No newline at end of file
diff --git a/application/src/main/data/sql/schema-entities-idx-psql-addon.sql b/application/src/main/data/sql/schema-entities-idx-psql-addon.sql
new file mode 100644
index 0000000000000000000000000000000000000000..d78257ad8bd7dc86a57311ed1fb54f39868c7325
--- /dev/null
+++ b/application/src/main/data/sql/schema-entities-idx-psql-addon.sql
@@ -0,0 +1,38 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+-- This file describes PostgreSQL-specific indexes that not supported by hsql
+-- It is not a stand-alone file! Run schema-entities-idx.sql before!
+-- Note: Hibernate DESC order translates to native SQL "ORDER BY .. DESC NULLS LAST"
+-- While creating index PostgreSQL transforms short notation (ts DESC) to the full (DESC NULLS FIRST)
+-- That difference between NULLS LAST and NULLS FIRST prevents to hit index while querying latest by ts
+-- That why we need to define DESC index explicitly as (ts DESC NULLS LAST)
+
+CREATE INDEX IF NOT EXISTS idx_rule_node_debug_event_main
+ ON rule_node_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_rule_chain_debug_event_main
+ ON rule_chain_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_stats_event_main
+ ON stats_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_lc_event_main
+ ON lc_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_error_event_main
+ ON error_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
diff --git a/application/src/main/data/sql/schema-entities-idx.sql b/application/src/main/data/sql/schema-entities-idx.sql
new file mode 100644
index 0000000000000000000000000000000000000000..e4c766e81d248a4d431a5376785be8b37b6330a4
--- /dev/null
+++ b/application/src/main/data/sql/schema-entities-idx.sql
@@ -0,0 +1,81 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC);
+
+CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC);
+
+CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC);
+
+CREATE INDEX IF NOT EXISTS idx_alarm_tenant_status_created_time ON alarm(tenant_id, status, created_time DESC);
+
+CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time ON alarm(tenant_id, type, created_time DESC);
+
+CREATE INDEX IF NOT EXISTS idx_entity_alarm_created_time ON entity_alarm(tenant_id, entity_id, created_time DESC);
+
+CREATE INDEX IF NOT EXISTS idx_entity_alarm_alarm_id ON entity_alarm(alarm_id);
+
+CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id);
+
+CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id);
+
+CREATE INDEX IF NOT EXISTS idx_device_customer_id ON device(tenant_id, customer_id);
+
+CREATE INDEX IF NOT EXISTS idx_device_customer_id_and_type ON device(tenant_id, customer_id, type);
+
+CREATE INDEX IF NOT EXISTS idx_device_type ON device(tenant_id, type);
+
+CREATE INDEX IF NOT EXISTS idx_device_device_profile_id ON device(tenant_id, device_profile_id);
+
+CREATE INDEX IF NOT EXISTS idx_asset_customer_id ON asset(tenant_id, customer_id);
+
+CREATE INDEX IF NOT EXISTS idx_asset_customer_id_and_type ON asset(tenant_id, customer_id, type);
+
+CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
+
+CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
+
+CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time DESC);
+
+CREATE INDEX IF NOT EXISTS idx_audit_log_id ON audit_log(id);
+
+CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_and_created_time ON edge_event(tenant_id, created_time DESC);
+
+CREATE INDEX IF NOT EXISTS idx_edge_event_id ON edge_event(id);
+
+CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id);
+
+CREATE INDEX IF NOT EXISTS idx_device_external_id ON device(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_device_profile_external_id ON device_profile(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_asset_external_id ON asset(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_entity_view_external_id ON entity_view(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_rule_chain_external_id ON rule_chain(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_dashboard_external_id ON dashboard(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_customer_external_id ON customer(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_widgets_bundle_external_id ON widgets_bundle(tenant_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id);
+
+CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type);
+
+CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);
diff --git a/application/src/main/data/sql/schema-entities.sql b/application/src/main/data/sql/schema-entities.sql
new file mode 100644
index 0000000000000000000000000000000000000000..73039274a650a3f72856dc926caf70d3f9bba710
--- /dev/null
+++ b/application/src/main/data/sql/schema-entities.sql
@@ -0,0 +1,778 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS tb_schema_settings
+(
+ schema_version bigint NOT NULL,
+ CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
+);
+
+CREATE OR REPLACE PROCEDURE insert_tb_schema_settings()
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ IF (SELECT COUNT(*) FROM tb_schema_settings) = 0 THEN
+ INSERT INTO tb_schema_settings (schema_version) VALUES (3003000);
+ END IF;
+END;
+$$;
+
+call insert_tb_schema_settings();
+
+CREATE TABLE IF NOT EXISTS admin_settings (
+ id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY,
+ tenant_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ json_value varchar,
+ key varchar(255)
+);
+
+CREATE TABLE IF NOT EXISTS alarm (
+ id uuid NOT NULL CONSTRAINT alarm_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ ack_ts bigint,
+ clear_ts bigint,
+ additional_info varchar,
+ end_ts bigint,
+ originator_id uuid,
+ originator_type integer,
+ propagate boolean,
+ severity varchar(255),
+ start_ts bigint,
+ status varchar(255),
+ tenant_id uuid,
+ customer_id uuid,
+ propagate_relation_types varchar,
+ type varchar(255),
+ propagate_to_owner boolean,
+ propagate_to_tenant boolean
+);
+
+CREATE TABLE IF NOT EXISTS entity_alarm (
+ tenant_id uuid NOT NULL,
+ entity_type varchar(32),
+ entity_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ alarm_type varchar(255) NOT NULL,
+ customer_id uuid,
+ alarm_id uuid,
+ CONSTRAINT entity_alarm_pkey PRIMARY KEY (entity_id, alarm_id),
+ CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS audit_log (
+ id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ tenant_id uuid,
+ customer_id uuid,
+ entity_id uuid,
+ entity_type varchar(255),
+ entity_name varchar(255),
+ user_id uuid,
+ user_name varchar(255),
+ action_type varchar(255),
+ action_data varchar(1000000),
+ action_status varchar(255),
+ action_failure_details varchar(1000000)
+) PARTITION BY RANGE (created_time);
+
+CREATE TABLE IF NOT EXISTS attribute_kv (
+ entity_type varchar(255),
+ entity_id uuid,
+ attribute_type varchar(255),
+ attribute_key varchar(255),
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ last_update_ts bigint,
+ CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key)
+);
+
+CREATE TABLE IF NOT EXISTS component_descriptor (
+ id uuid NOT NULL CONSTRAINT component_descriptor_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ actions varchar(255),
+ clazz varchar UNIQUE,
+ configuration_descriptor varchar,
+ name varchar(255),
+ scope varchar(255),
+ search_text varchar(255),
+ type varchar(255)
+);
+
+CREATE TABLE IF NOT EXISTS customer (
+ id uuid NOT NULL CONSTRAINT customer_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ address varchar,
+ address2 varchar,
+ city varchar(255),
+ country varchar(255),
+ email varchar(255),
+ phone varchar(255),
+ search_text varchar(255),
+ state varchar(255),
+ tenant_id uuid,
+ title varchar(255),
+ zip varchar(255),
+ external_id uuid,
+ CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id)
+);
+
+CREATE TABLE IF NOT EXISTS dashboard (
+ id uuid NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ configuration varchar,
+ assigned_customers varchar(1000000),
+ search_text varchar(255),
+ tenant_id uuid,
+ title varchar(255),
+ mobile_hide boolean DEFAULT false,
+ mobile_order int,
+ image varchar(1000000),
+ external_id uuid,
+ CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id)
+);
+
+CREATE TABLE IF NOT EXISTS rule_chain (
+ id uuid NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ configuration varchar(10000000),
+ name varchar(255),
+ type varchar(255),
+ first_rule_node_id uuid,
+ root boolean,
+ debug_mode boolean,
+ search_text varchar(255),
+ tenant_id uuid,
+ external_id uuid,
+ CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id)
+);
+
+CREATE TABLE IF NOT EXISTS rule_node (
+ id uuid NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ rule_chain_id uuid,
+ additional_info varchar,
+ configuration varchar(10000000),
+ type varchar(255),
+ name varchar(255),
+ debug_mode boolean,
+ search_text varchar(255),
+ external_id uuid
+);
+
+CREATE TABLE IF NOT EXISTS rule_node_state (
+ id uuid NOT NULL CONSTRAINT rule_node_state_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ rule_node_id uuid NOT NULL,
+ entity_type varchar(32) NOT NULL,
+ entity_id uuid NOT NULL,
+ state_data varchar(16384) NOT NULL,
+ CONSTRAINT rule_node_state_unq_key UNIQUE (rule_node_id, entity_id),
+ CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS ota_package (
+ id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid NOT NULL,
+ device_profile_id uuid ,
+ type varchar(32) NOT NULL,
+ title varchar(255) NOT NULL,
+ version varchar(255) NOT NULL,
+ tag varchar(255),
+ url varchar(255),
+ file_name varchar(255),
+ content_type varchar(255),
+ checksum_algorithm varchar(32),
+ checksum varchar(1020),
+ data oid,
+ data_size bigint,
+ additional_info varchar,
+ search_text varchar(255),
+ CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
+);
+
+CREATE TABLE IF NOT EXISTS queue (
+ id uuid NOT NULL CONSTRAINT queue_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid,
+ name varchar(255),
+ topic varchar(255),
+ poll_interval int,
+ partitions int,
+ consumer_per_partition boolean,
+ pack_processing_timeout bigint,
+ submit_strategy varchar(255),
+ processing_strategy varchar(255),
+ additional_info varchar
+);
+
+CREATE TABLE IF NOT EXISTS asset_profile (
+ id uuid NOT NULL CONSTRAINT asset_profile_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ name varchar(255),
+ image varchar(1000000),
+ description varchar,
+ search_text varchar(255),
+ is_default boolean,
+ tenant_id uuid,
+ default_rule_chain_id uuid,
+ default_dashboard_id uuid,
+ default_queue_name varchar(255),
+ external_id uuid,
+ CONSTRAINT asset_profile_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT asset_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
+ CONSTRAINT fk_default_rule_chain_asset_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
+ CONSTRAINT fk_default_dashboard_asset_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id)
+ );
+
+CREATE TABLE IF NOT EXISTS asset (
+ id uuid NOT NULL CONSTRAINT asset_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ customer_id uuid,
+ asset_profile_id uuid NOT NULL,
+ name varchar(255),
+ label varchar(255),
+ search_text varchar(255),
+ tenant_id uuid,
+ type varchar(255),
+ external_id uuid,
+ CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id),
+ CONSTRAINT fk_asset_profile FOREIGN KEY (asset_profile_id) REFERENCES asset_profile(id)
+);
+
+CREATE TABLE IF NOT EXISTS device_profile (
+ id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ name varchar(255),
+ type varchar(255),
+ image varchar(1000000),
+ transport_type varchar(255),
+ provision_type varchar(255),
+ profile_data jsonb,
+ description varchar,
+ search_text varchar(255),
+ is_default boolean,
+ tenant_id uuid,
+ firmware_id uuid,
+ software_id uuid,
+ default_rule_chain_id uuid,
+ default_dashboard_id uuid,
+ default_queue_name varchar(255),
+ provision_device_key varchar,
+ external_id uuid,
+ CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
+ CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
+ CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
+ CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id),
+ CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
+ CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id)
+);
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_device_profile_ota_package') THEN
+ ALTER TABLE ota_package
+ ADD CONSTRAINT fk_device_profile_ota_package
+ FOREIGN KEY (device_profile_id) REFERENCES device_profile (id)
+ ON DELETE CASCADE;
+ END IF;
+ END;
+$$;
+
+-- We will use one-to-many relation in the first release and extend this feature in case of user requests
+-- CREATE TABLE IF NOT EXISTS device_profile_firmware (
+-- device_profile_id uuid NOT NULL,
+-- firmware_id uuid NOT NULL,
+-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id),
+-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE,
+-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE,
+-- );
+
+CREATE TABLE IF NOT EXISTS device (
+ id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ customer_id uuid,
+ device_profile_id uuid NOT NULL,
+ device_data jsonb,
+ type varchar(255),
+ name varchar(255),
+ label varchar(255),
+ search_text varchar(255),
+ tenant_id uuid,
+ firmware_id uuid,
+ software_id uuid,
+ external_id uuid,
+ CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id),
+ CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
+ CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
+ CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id)
+);
+
+CREATE TABLE IF NOT EXISTS device_credentials (
+ id uuid NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ credentials_id varchar,
+ credentials_type varchar(255),
+ credentials_value varchar,
+ device_id uuid,
+ CONSTRAINT device_credentials_id_unq_key UNIQUE (credentials_id),
+ CONSTRAINT device_credentials_device_id_unq_key UNIQUE (device_id)
+);
+
+CREATE TABLE IF NOT EXISTS rule_node_debug_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL ,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar,
+ e_type varchar,
+ e_entity_id uuid,
+ e_entity_type varchar,
+ e_msg_id uuid,
+ e_msg_type varchar,
+ e_data_type varchar,
+ e_relation_type varchar,
+ e_data varchar,
+ e_metadata varchar,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS rule_chain_debug_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_message varchar,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS stats_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_messages_processed bigint NOT NULL,
+ e_errors_occurred bigint NOT NULL
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS lc_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_type varchar NOT NULL,
+ e_success boolean NOT NULL,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS error_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_method varchar NOT NULL,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS relation (
+ from_id uuid,
+ from_type varchar(255),
+ to_id uuid,
+ to_type varchar(255),
+ relation_type_group varchar(255),
+ relation_type varchar(255),
+ additional_info varchar,
+ CONSTRAINT relation_pkey PRIMARY KEY (from_id, from_type, relation_type_group, relation_type, to_id, to_type)
+);
+-- ) PARTITION BY LIST (relation_type_group);
+--
+-- CREATE TABLE other_relations PARTITION OF relation DEFAULT;
+-- CREATE TABLE common_relations PARTITION OF relation FOR VALUES IN ('COMMON');
+-- CREATE TABLE alarm_relations PARTITION OF relation FOR VALUES IN ('ALARM');
+-- CREATE TABLE dashboard_relations PARTITION OF relation FOR VALUES IN ('DASHBOARD');
+-- CREATE TABLE rule_relations PARTITION OF relation FOR VALUES IN ('RULE_CHAIN', 'RULE_NODE');
+
+CREATE TABLE IF NOT EXISTS tb_user (
+ id uuid NOT NULL CONSTRAINT tb_user_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ authority varchar(255),
+ customer_id uuid,
+ email varchar(255) UNIQUE,
+ first_name varchar(255),
+ last_name varchar(255),
+ search_text varchar(255),
+ tenant_id uuid
+);
+
+CREATE TABLE IF NOT EXISTS tenant_profile (
+ id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ name varchar(255),
+ profile_data jsonb,
+ description varchar,
+ search_text varchar(255),
+ is_default boolean,
+ isolated_tb_core boolean,
+ isolated_tb_rule_engine boolean,
+ CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
+);
+
+CREATE TABLE IF NOT EXISTS tenant (
+ id uuid NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ tenant_profile_id uuid NOT NULL,
+ address varchar,
+ address2 varchar,
+ city varchar(255),
+ country varchar(255),
+ email varchar(255),
+ phone varchar(255),
+ region varchar(255),
+ search_text varchar(255),
+ state varchar(255),
+ title varchar(255),
+ zip varchar(255),
+ CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id)
+);
+
+CREATE TABLE IF NOT EXISTS user_credentials (
+ id uuid NOT NULL CONSTRAINT user_credentials_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ activate_token varchar(255) UNIQUE,
+ enabled boolean,
+ password varchar(255),
+ reset_token varchar(255) UNIQUE,
+ user_id uuid UNIQUE
+);
+
+CREATE TABLE IF NOT EXISTS widget_type (
+ id uuid NOT NULL CONSTRAINT widget_type_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ alias varchar(255),
+ bundle_alias varchar(255),
+ descriptor varchar(1000000),
+ name varchar(255),
+ tenant_id uuid,
+ image varchar(1000000),
+ description varchar(255)
+);
+
+CREATE TABLE IF NOT EXISTS widgets_bundle (
+ id uuid NOT NULL CONSTRAINT widgets_bundle_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ alias varchar(255),
+ search_text varchar(255),
+ tenant_id uuid,
+ title varchar(255),
+ image varchar(1000000),
+ description varchar(255),
+ external_id uuid,
+ CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id)
+);
+
+CREATE TABLE IF NOT EXISTS entity_view (
+ id uuid NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ entity_id uuid,
+ entity_type varchar(255),
+ tenant_id uuid,
+ customer_id uuid,
+ type varchar(255),
+ name varchar(255),
+ keys varchar(10000000),
+ start_ts bigint,
+ end_ts bigint,
+ search_text varchar(255),
+ additional_info varchar,
+ external_id uuid,
+ CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id)
+);
+
+CREATE TABLE IF NOT EXISTS ts_kv_latest
+(
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
+);
+
+CREATE TABLE IF NOT EXISTS ts_kv_dictionary
+(
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_params (
+ id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY,
+ enabled boolean,
+ tenant_id uuid,
+ created_time bigint NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_registration (
+ id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY,
+ oauth2_params_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ client_id varchar(255),
+ client_secret varchar(2048),
+ authorization_uri varchar(255),
+ token_uri varchar(255),
+ scope varchar(255),
+ platforms varchar(255),
+ user_info_uri varchar(255),
+ user_name_attribute_name varchar(255),
+ jwk_set_uri varchar(255),
+ client_authentication_method varchar(255),
+ login_button_label varchar(255),
+ login_button_icon varchar(255),
+ allow_user_creation boolean,
+ activate_user boolean,
+ type varchar(31),
+ basic_email_attribute_key varchar(31),
+ basic_first_name_attribute_key varchar(31),
+ basic_last_name_attribute_key varchar(31),
+ basic_tenant_name_strategy varchar(31),
+ basic_tenant_name_pattern varchar(255),
+ basic_customer_name_pattern varchar(255),
+ basic_default_dashboard_name varchar(255),
+ basic_always_full_screen boolean,
+ custom_url varchar(255),
+ custom_username varchar(255),
+ custom_password varchar(255),
+ custom_send_token boolean,
+ CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_domain (
+ id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY,
+ oauth2_params_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ domain_name varchar(255),
+ domain_scheme varchar(31),
+ CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
+ CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme)
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_mobile (
+ id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY,
+ oauth2_params_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ pkg_name varchar(255),
+ app_secret varchar(2048),
+ CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
+ CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name)
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_client_registration_template (
+ id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ provider_id varchar(255),
+ authorization_uri varchar(255),
+ token_uri varchar(255),
+ scope varchar(255),
+ user_info_uri varchar(255),
+ user_name_attribute_name varchar(255),
+ jwk_set_uri varchar(255),
+ client_authentication_method varchar(255),
+ type varchar(31),
+ basic_email_attribute_key varchar(31),
+ basic_first_name_attribute_key varchar(31),
+ basic_last_name_attribute_key varchar(31),
+ basic_tenant_name_strategy varchar(31),
+ basic_tenant_name_pattern varchar(255),
+ basic_customer_name_pattern varchar(255),
+ basic_default_dashboard_name varchar(255),
+ basic_always_full_screen boolean,
+ comment varchar,
+ login_button_icon varchar(255),
+ login_button_label varchar(255),
+ help_link varchar(255),
+ CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id)
+);
+
+-- Deprecated
+CREATE TABLE IF NOT EXISTS oauth2_client_registration_info (
+ id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY,
+ enabled boolean,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ client_id varchar(255),
+ client_secret varchar(255),
+ authorization_uri varchar(255),
+ token_uri varchar(255),
+ scope varchar(255),
+ user_info_uri varchar(255),
+ user_name_attribute_name varchar(255),
+ jwk_set_uri varchar(255),
+ client_authentication_method varchar(255),
+ login_button_label varchar(255),
+ login_button_icon varchar(255),
+ allow_user_creation boolean,
+ activate_user boolean,
+ type varchar(31),
+ basic_email_attribute_key varchar(31),
+ basic_first_name_attribute_key varchar(31),
+ basic_last_name_attribute_key varchar(31),
+ basic_tenant_name_strategy varchar(31),
+ basic_tenant_name_pattern varchar(255),
+ basic_customer_name_pattern varchar(255),
+ basic_default_dashboard_name varchar(255),
+ basic_always_full_screen boolean,
+ custom_url varchar(255),
+ custom_username varchar(255),
+ custom_password varchar(255),
+ custom_send_token boolean
+);
+
+-- Deprecated
+CREATE TABLE IF NOT EXISTS oauth2_client_registration (
+ id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ domain_name varchar(255),
+ domain_scheme varchar(31),
+ client_registration_info_id uuid
+);
+
+CREATE TABLE IF NOT EXISTS api_usage_state (
+ id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid,
+ entity_type varchar(32),
+ entity_id uuid,
+ transport varchar(32),
+ db_storage varchar(32),
+ re_exec varchar(32),
+ js_exec varchar(32),
+ email_exec varchar(32),
+ sms_exec varchar(32),
+ alarm_exec varchar(32),
+ CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
+);
+
+CREATE TABLE IF NOT EXISTS resource (
+ id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid NOT NULL,
+ title varchar(255) NOT NULL,
+ resource_type varchar(32) NOT NULL,
+ resource_key varchar(255) NOT NULL,
+ search_text varchar(255),
+ file_name varchar(255) NOT NULL,
+ data varchar,
+ CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)
+);
+
+CREATE TABLE IF NOT EXISTS edge (
+ id uuid NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ customer_id uuid,
+ root_rule_chain_id uuid,
+ type varchar(255),
+ name varchar(255),
+ label varchar(255),
+ routing_key varchar(255),
+ secret varchar(255),
+ search_text varchar(255),
+ tenant_id uuid,
+ CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key)
+);
+
+CREATE TABLE IF NOT EXISTS edge_event (
+ id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ edge_id uuid,
+ edge_event_type varchar(255),
+ edge_event_uid varchar(255),
+ entity_id uuid,
+ edge_event_action varchar(255),
+ body varchar(10000000),
+ tenant_id uuid,
+ ts bigint NOT NULL
+) PARTITION BY RANGE(created_time);
+
+CREATE TABLE IF NOT EXISTS rpc (
+ id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid NOT NULL,
+ device_id uuid NOT NULL,
+ expiration_time bigint NOT NULL,
+ request varchar(10000000) NOT NULL,
+ response varchar(10000000),
+ additional_info varchar(10000000),
+ status varchar(255) NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ ttl_ts bigint;
+ ttl_deleted_count bigint DEFAULT 0;
+BEGIN
+ IF ttl > 0 THEN
+ ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count;
+ END IF;
+ RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count;
+ deleted := ttl_deleted_count;
+END
+$$;
+
+
+CREATE TABLE IF NOT EXISTS user_auth_settings (
+ id uuid NOT NULL CONSTRAINT user_auth_settings_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ user_id uuid UNIQUE NOT NULL CONSTRAINT fk_user_auth_settings_user_id REFERENCES tb_user(id),
+ two_fa_settings varchar
+);
diff --git a/application/src/main/data/sql/schema-timescale.sql b/application/src/main/data/sql/schema-timescale.sql
new file mode 100644
index 0000000000000000000000000000000000000000..81b71f1a032cdedfdb16f0c9954bc22da4c088a7
--- /dev/null
+++ b/application/src/main/data/sql/schema-timescale.sql
@@ -0,0 +1,157 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
+
+CREATE TABLE IF NOT EXISTS ts_kv (
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts)
+);
+
+CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+);
+
+CREATE TABLE IF NOT EXISTS ts_kv_latest (
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
+);
+
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_device_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT device.id as entity_id FROM device WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_asset_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT asset.id as entity_id FROM asset WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_customer_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT customer.id as entity_id FROM customer WHERE tenant_id = %L and id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid,
+ IN system_ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ tenant_cursor CURSOR FOR select tenant.id as tenant_id
+ from tenant;
+ tenant_id_record uuid;
+ customer_id_record uuid;
+ tenant_ttl bigint;
+ customer_ttl bigint;
+ deleted_for_entities bigint;
+ tenant_ttl_ts bigint;
+ customer_ttl_ts bigint;
+BEGIN
+ OPEN tenant_cursor;
+ FETCH tenant_cursor INTO tenant_id_record;
+ WHILE FOUND
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ tenant_id_record, 'TTL') INTO tenant_ttl;
+ if tenant_ttl IS NULL THEN
+ tenant_ttl := system_ttl;
+ END IF;
+ IF tenant_ttl > 0 THEN
+ tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint;
+ deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record;
+ END IF;
+ FOR customer_id_record IN
+ SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ customer_id_record, 'TTL') INTO customer_ttl;
+ IF customer_ttl IS NULL THEN
+ customer_ttl_ts := tenant_ttl_ts;
+ ELSE
+ IF customer_ttl > 0 THEN
+ customer_ttl_ts :=
+ (EXTRACT(EPOCH FROM current_timestamp) * 1000 -
+ customer_ttl::bigint * 1000)::bigint;
+ END IF;
+ END IF;
+ IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN
+ deleted_for_entities :=
+ delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record;
+ deleted_for_entities :=
+ delete_device_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record,
+ customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+ END IF;
+ END LOOP;
+ FETCH tenant_cursor INTO tenant_id_record;
+ END LOOP;
+END
+$$;
diff --git a/application/src/main/data/sql/schema-ts-psql.sql b/application/src/main/data/sql/schema-ts-psql.sql
new file mode 100644
index 0000000000000000000000000000000000000000..c95ac4cf0f15b41a55869c3fcfbd906899a65200
--- /dev/null
+++ b/application/src/main/data/sql/schema-ts-psql.sql
@@ -0,0 +1,339 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS ts_kv
+(
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts)
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS ts_kv_dictionary
+(
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+);
+
+CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ max_tenant_ttl bigint;
+ max_customer_ttl bigint;
+ max_ttl bigint;
+ date timestamp;
+ partition_by_max_ttl_date varchar;
+ partition_by_max_ttl_month varchar;
+ partition_by_max_ttl_day varchar;
+ partition_by_max_ttl_year varchar;
+ partition varchar;
+ partition_year integer;
+ partition_month integer;
+ partition_day integer;
+
+BEGIN
+ SELECT max(attribute_kv.long_v)
+ FROM tenant
+ INNER JOIN attribute_kv ON tenant.id = attribute_kv.entity_id
+ WHERE attribute_kv.attribute_key = 'TTL'
+ into max_tenant_ttl;
+ SELECT max(attribute_kv.long_v)
+ FROM customer
+ INNER JOIN attribute_kv ON customer.id = attribute_kv.entity_id
+ WHERE attribute_kv.attribute_key = 'TTL'
+ into max_customer_ttl;
+ max_ttl := GREATEST(system_ttl, max_customer_ttl, max_tenant_ttl);
+ if max_ttl IS NOT NULL AND max_ttl > 0 THEN
+ date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl);
+ partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date);
+ RAISE NOTICE 'Date by max ttl: %', date;
+ RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date;
+ IF partition_by_max_ttl_date IS NOT NULL THEN
+ CASE
+ WHEN partition_type = 'DAYS' THEN
+ partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
+ partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
+ partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5);
+ WHEN partition_type = 'MONTHS' THEN
+ partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
+ partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
+ ELSE
+ partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
+ END CASE;
+ IF partition_by_max_ttl_year IS NULL THEN
+ RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!';
+ ELSE
+ IF partition_type = 'YEARS' THEN
+ FOR partition IN SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename like 'ts_kv_' || '%'
+ AND tablename != 'ts_kv_latest'
+ AND tablename != 'ts_kv_dictionary'
+ AND tablename != 'ts_kv_indefinite'
+ AND tablename != partition_by_max_ttl_date
+ LOOP
+ partition_year := SPLIT_PART(partition, '_', 3)::integer;
+ IF partition_year < partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ END IF;
+ END LOOP;
+ ELSE
+ IF partition_type = 'MONTHS' THEN
+ IF partition_by_max_ttl_month IS NULL THEN
+ RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!';
+ ELSE
+ FOR partition IN SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename like 'ts_kv_' || '%'
+ AND tablename != 'ts_kv_latest'
+ AND tablename != 'ts_kv_dictionary'
+ AND tablename != 'ts_kv_indefinite'
+ AND tablename != partition_by_max_ttl_date
+ LOOP
+ partition_year := SPLIT_PART(partition, '_', 3)::integer;
+ IF partition_year > partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_year < partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ ELSE
+ partition_month := SPLIT_PART(partition, '_', 4)::integer;
+ IF partition_year = partition_by_max_ttl_year::integer THEN
+ IF partition_month >= partition_by_max_ttl_month::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END LOOP;
+ END IF;
+ ELSE
+ IF partition_type = 'DAYS' THEN
+ IF partition_by_max_ttl_month IS NULL THEN
+ RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!';
+ ELSE
+ IF partition_by_max_ttl_day IS NULL THEN
+ RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!';
+ ELSE
+ FOR partition IN SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename like 'ts_kv_' || '%'
+ AND tablename != 'ts_kv_latest'
+ AND tablename != 'ts_kv_dictionary'
+ AND tablename != 'ts_kv_indefinite'
+ AND tablename != partition_by_max_ttl_date
+ LOOP
+ partition_year := SPLIT_PART(partition, '_', 3)::integer;
+ IF partition_year > partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_year < partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ ELSE
+ partition_month := SPLIT_PART(partition, '_', 4)::integer;
+ IF partition_month > partition_by_max_ttl_month::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_month < partition_by_max_ttl_month::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ ELSE
+ partition_day := SPLIT_PART(partition, '_', 5)::integer;
+ IF partition_day >= partition_by_max_ttl_day::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_day < partition_by_max_ttl_day::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END LOOP;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+END
+$$;
+
+CREATE OR REPLACE FUNCTION get_partition_by_max_ttl_date(IN partition_type varchar, IN date timestamp, OUT partition varchar) AS
+$$
+BEGIN
+ CASE
+ WHEN partition_type = 'DAYS' THEN
+ partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM') || '_' || to_char(date, 'dd');
+ WHEN partition_type = 'MONTHS' THEN
+ partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM');
+ WHEN partition_type = 'YEARS' THEN
+ partition := 'ts_kv_' || to_char(date, 'yyyy');
+ ELSE
+ partition := NULL;
+ END CASE;
+ IF partition IS NOT NULL THEN
+ IF NOT EXISTS(SELECT
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename = partition) THEN
+ partition := NULL;
+ RAISE NOTICE 'Failed to found partition by ttl';
+ END IF;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_device_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT device.id as entity_id FROM device WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_asset_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT asset.id as entity_id FROM asset WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_customer_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT customer.id as entity_id FROM customer WHERE tenant_id = %L and id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid,
+ IN system_ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ tenant_cursor CURSOR FOR select tenant.id as tenant_id
+ from tenant;
+ tenant_id_record uuid;
+ customer_id_record uuid;
+ tenant_ttl bigint;
+ customer_ttl bigint;
+ deleted_for_entities bigint;
+ tenant_ttl_ts bigint;
+ customer_ttl_ts bigint;
+BEGIN
+ OPEN tenant_cursor;
+ FETCH tenant_cursor INTO tenant_id_record;
+ WHILE FOUND
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ tenant_id_record, 'TTL') INTO tenant_ttl;
+ if tenant_ttl IS NULL THEN
+ tenant_ttl := system_ttl;
+ END IF;
+ IF tenant_ttl > 0 THEN
+ tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint;
+ deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record;
+ END IF;
+ FOR customer_id_record IN
+ SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ customer_id_record, 'TTL') INTO customer_ttl;
+ IF customer_ttl IS NULL THEN
+ customer_ttl_ts := tenant_ttl_ts;
+ ELSE
+ IF customer_ttl > 0 THEN
+ customer_ttl_ts :=
+ (EXTRACT(EPOCH FROM current_timestamp) * 1000 -
+ customer_ttl::bigint * 1000)::bigint;
+ END IF;
+ END IF;
+ IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN
+ deleted_for_entities :=
+ delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record;
+ deleted_for_entities :=
+ delete_device_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record,
+ customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+ END IF;
+ END LOOP;
+ FETCH tenant_cursor INTO tenant_id_record;
+ END LOOP;
+END
+$$;
diff --git a/application/src/main/data/upgrade/1.3.0/schema_update.cql b/application/src/main/data/upgrade/1.3.0/schema_update.cql
new file mode 100644
index 0000000000000000000000000000000000000000..88cb1dce81b222fa5d15707d5b0a526b56110a9d
--- /dev/null
+++ b/application/src/main/data/upgrade/1.3.0/schema_update.cql
@@ -0,0 +1,187 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_tenant_and_name;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_tenant_by_type_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_customer_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_customer_by_type_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_types_by_tenant;
+
+DROP TABLE IF EXISTS thingsboard.device;
+
+CREATE TABLE IF NOT EXISTS thingsboard.device (
+ id timeuuid,
+ tenant_id timeuuid,
+ customer_id timeuuid,
+ name text,
+ type text,
+ search_text text,
+ additional_info text,
+ PRIMARY KEY (id, tenant_id, customer_id, type)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_name AS
+ SELECT *
+ from thingsboard.device
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, name, id, customer_id, type)
+ WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.device
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
+ WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_by_type_and_search_text AS
+ SELECT *
+ from thingsboard.device
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
+ WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_and_search_text AS
+ SELECT *
+ from thingsboard.device
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
+ WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_and_search_text AS
+ SELECT *
+ from thingsboard.device
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
+ WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_and_name;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_by_type_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_customer_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_customer_by_type_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_types_by_tenant;
+
+DROP TABLE IF EXISTS thingsboard.asset;
+
+CREATE TABLE IF NOT EXISTS thingsboard.asset (
+ id timeuuid,
+ tenant_id timeuuid,
+ customer_id timeuuid,
+ name text,
+ type text,
+ search_text text,
+ additional_info text,
+ PRIMARY KEY (id, tenant_id, customer_id, type)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_name AS
+ SELECT *
+ from thingsboard.asset
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, name, id, customer_id, type)
+ WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.asset
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
+ WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_by_type_and_search_text AS
+ SELECT *
+ from thingsboard.asset
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
+ WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_and_search_text AS
+ SELECT *
+ from thingsboard.asset
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
+ WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and_search_text AS
+ SELECT *
+ from thingsboard.asset
+ WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
+ WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
+
+CREATE TABLE IF NOT EXISTS thingsboard.entity_subtype (
+ tenant_id timeuuid,
+ entity_type text, // (DEVICE, ASSET)
+ type text,
+ PRIMARY KEY (tenant_id, entity_type, type)
+);
+
+CREATE TABLE IF NOT EXISTS thingsboard.alarm (
+ id timeuuid,
+ tenant_id timeuuid,
+ type text,
+ originator_id timeuuid,
+ originator_type text,
+ severity text,
+ status text,
+ start_ts bigint,
+ end_ts bigint,
+ ack_ts bigint,
+ clear_ts bigint,
+ details text,
+ propagate boolean,
+ PRIMARY KEY ((tenant_id, originator_id, originator_type), type, id)
+) WITH CLUSTERING ORDER BY ( type ASC, id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.alarm_by_id AS
+ SELECT *
+ from thingsboard.alarm
+ WHERE tenant_id IS NOT NULL AND originator_id IS NOT NULL AND originator_type IS NOT NULL AND type IS NOT NULL
+ AND type IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY (id, tenant_id, originator_id, originator_type, type)
+ WITH CLUSTERING ORDER BY ( tenant_id ASC, originator_id ASC, originator_type ASC, type ASC);
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.relation_by_type_and_child_type;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.reverse_relation;
+
+DROP TABLE IF EXISTS thingsboard.relation;
+
+CREATE TABLE IF NOT EXISTS thingsboard.relation (
+ from_id timeuuid,
+ from_type text,
+ to_id timeuuid,
+ to_type text,
+ relation_type_group text,
+ relation_type text,
+ additional_info text,
+ PRIMARY KEY ((from_id, from_type), relation_type_group, relation_type, to_id, to_type)
+) WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, to_id ASC, to_type ASC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.relation_by_type_and_child_type AS
+ SELECT *
+ from thingsboard.relation
+ WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type_group IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL
+ PRIMARY KEY ((from_id, from_type), relation_type_group, relation_type, to_type, to_id)
+ WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, to_type ASC, to_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS
+ SELECT *
+ from thingsboard.relation
+ WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type_group IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL
+ PRIMARY KEY ((to_id, to_type), relation_type_group, relation_type, from_id, from_type)
+ WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, from_id ASC, from_type ASC);
diff --git a/application/src/main/data/upgrade/1.3.1/schema_update.sql b/application/src/main/data/upgrade/1.3.1/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..1ca65908353b2f1cb34d0615ed1ff3c202893248
--- /dev/null
+++ b/application/src/main/data/upgrade/1.3.1/schema_update.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE ts_kv_latest ALTER COLUMN str_v SET DATA TYPE varchar(10000000);
diff --git a/application/src/main/data/upgrade/1.4.0/schema_update.cql b/application/src/main/data/upgrade/1.4.0/schema_update.cql
new file mode 100644
index 0000000000000000000000000000000000000000..138c9f90867886b84214ce17a15c29cba0167b98
--- /dev/null
+++ b/application/src/main/data/upgrade/1.4.0/schema_update.cql
@@ -0,0 +1,112 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_entity_id (
+ tenant_id timeuuid,
+ id timeuuid,
+ customer_id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ entity_name text,
+ user_id timeuuid,
+ user_name text,
+ action_type text,
+ action_data text,
+ action_status text,
+ action_failure_details text,
+ PRIMARY KEY ((tenant_id, entity_id, entity_type), id)
+);
+
+CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_customer_id (
+ tenant_id timeuuid,
+ id timeuuid,
+ customer_id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ entity_name text,
+ user_id timeuuid,
+ user_name text,
+ action_type text,
+ action_data text,
+ action_status text,
+ action_failure_details text,
+ PRIMARY KEY ((tenant_id, customer_id), id)
+);
+
+CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_user_id (
+ tenant_id timeuuid,
+ id timeuuid,
+ customer_id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ entity_name text,
+ user_id timeuuid,
+ user_name text,
+ action_type text,
+ action_data text,
+ action_status text,
+ action_failure_details text,
+ PRIMARY KEY ((tenant_id, user_id), id)
+);
+
+
+
+CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id (
+ tenant_id timeuuid,
+ id timeuuid,
+ partition bigint,
+ customer_id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ entity_name text,
+ user_id timeuuid,
+ user_name text,
+ action_type text,
+ action_data text,
+ action_status text,
+ action_failure_details text,
+ PRIMARY KEY ((tenant_id, partition), id)
+);
+
+CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions (
+ tenant_id timeuuid,
+ partition bigint,
+ PRIMARY KEY (( tenant_id ), partition)
+) WITH CLUSTERING ORDER BY ( partition ASC )
+AND compaction = { 'class' : 'LeveledCompactionStrategy' };
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_customer_and_search_text;
+
+DROP TABLE IF EXISTS thingsboard.dashboard;
+
+CREATE TABLE IF NOT EXISTS thingsboard.dashboard (
+ id timeuuid,
+ tenant_id timeuuid,
+ title text,
+ search_text text,
+ assigned_customers text,
+ configuration text,
+ PRIMARY KEY (id, tenant_id)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.dashboard
+ WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, search_text, id )
+ WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
+
diff --git a/application/src/main/data/upgrade/1.4.0/schema_update.sql b/application/src/main/data/upgrade/1.4.0/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..c999c6b3e9a80276d9de0d053f9e5155324322ce
--- /dev/null
+++ b/application/src/main/data/upgrade/1.4.0/schema_update.sql
@@ -0,0 +1,41 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS audit_log (
+ id varchar(31) NOT NULL CONSTRAINT audit_log_pkey PRIMARY KEY,
+ tenant_id varchar(31),
+ customer_id varchar(31),
+ entity_id varchar(31),
+ entity_type varchar(255),
+ entity_name varchar(255),
+ user_id varchar(31),
+ user_name varchar(255),
+ action_type varchar(255),
+ action_data varchar(1000000),
+ action_status varchar(255),
+ action_failure_details varchar(1000000)
+);
+
+DROP TABLE IF EXISTS dashboard;
+
+CREATE TABLE IF NOT EXISTS dashboard (
+ id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY,
+ configuration varchar(10000000),
+ assigned_customers varchar(1000000),
+ search_text varchar(255),
+ tenant_id varchar(31),
+ title varchar(255)
+);
diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.cql b/application/src/main/data/upgrade/2.0.0/schema_update.cql
new file mode 100644
index 0000000000000000000000000000000000000000..5f7daa0ea8bbaaf0ead272c832eb765ec58bffad
--- /dev/null
+++ b/application/src/main/data/upgrade/2.0.0/schema_update.cql
@@ -0,0 +1,103 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_queue (
+ node_id timeuuid,
+ cluster_partition bigint,
+ ts_partition bigint,
+ ts bigint,
+ msg blob,
+ PRIMARY KEY ((node_id, cluster_partition, ts_partition), ts))
+WITH CLUSTERING ORDER BY (ts DESC)
+AND compaction = {
+ 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+ 'min_threshold': '5',
+ 'base_time_seconds': '43200',
+ 'max_window_size_seconds': '43200',
+ 'tombstone_threshold': '0.9',
+ 'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue (
+ node_id timeuuid,
+ cluster_partition bigint,
+ ts_partition bigint,
+ msg_id timeuuid,
+ PRIMARY KEY ((node_id, cluster_partition, ts_partition), msg_id))
+WITH CLUSTERING ORDER BY (msg_id DESC)
+AND compaction = {
+ 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+ 'min_threshold': '5',
+ 'base_time_seconds': '43200',
+ 'max_window_size_seconds': '43200',
+ 'tombstone_threshold': '0.9',
+ 'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions (
+ node_id timeuuid,
+ cluster_partition bigint,
+ ts_partition bigint,
+ PRIMARY KEY ((node_id, cluster_partition), ts_partition))
+WITH CLUSTERING ORDER BY (ts_partition DESC)
+AND compaction = {
+ 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+ 'min_threshold': '5',
+ 'base_time_seconds': '43200',
+ 'max_window_size_seconds': '43200',
+ 'tombstone_threshold': '0.9',
+ 'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.rule_chain (
+ id uuid,
+ tenant_id uuid,
+ name text,
+ search_text text,
+ first_rule_node_id uuid,
+ root boolean,
+ debug_mode boolean,
+ configuration text,
+ additional_info text,
+ PRIMARY KEY (id, tenant_id)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.rule_chain
+ WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, search_text, id )
+ WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
+
+CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
+ id uuid,
+ rule_chain_id uuid,
+ type text,
+ name text,
+ debug_mode boolean,
+ search_text text,
+ configuration text,
+ additional_info text,
+ PRIMARY KEY (id)
+);
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_plugin_token;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_api_token;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_tenant_and_search_text;
+
+DROP TABLE IF EXISTS thingsboard.rule;
+DROP TABLE IF EXISTS thingsboard.plugin;
diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.sql b/application/src/main/data/upgrade/2.0.0/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..4a07d571e86d24aa0c57025f6a3c0c1ce1ab9444
--- /dev/null
+++ b/application/src/main/data/upgrade/2.0.0/schema_update.sql
@@ -0,0 +1,44 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS rule_chain (
+ id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY,
+ additional_info varchar,
+ configuration varchar(10000000),
+ name varchar(255),
+ first_rule_node_id varchar(31),
+ root boolean,
+ debug_mode boolean,
+ search_text varchar(255),
+ tenant_id varchar(31)
+);
+
+CREATE TABLE IF NOT EXISTS rule_node (
+ id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
+ rule_chain_id varchar(31),
+ additional_info varchar,
+ configuration varchar(10000000),
+ type varchar(255),
+ name varchar(255),
+ debug_mode boolean,
+ search_text varchar(255)
+);
+
+DROP TABLE rule;
+DROP TABLE plugin;
+
+DELETE FROM alarm WHERE originator_type = 3 OR originator_type = 4;
+UPDATE alarm SET originator_type = (originator_type - 2) where originator_type > 2;
diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql
new file mode 100644
index 0000000000000000000000000000000000000000..49aa012d70642d1b58f437fb011bde0f289b6951
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.1/schema_update.cql
@@ -0,0 +1,74 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
+ id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ tenant_id timeuuid,
+ customer_id timeuuid,
+ name text,
+ keys text,
+ start_ts bigint,
+ end_ts bigint,
+ search_text text,
+ additional_info text,
+ PRIMARY KEY (id, entity_id, tenant_id, customer_id)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND name IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, name, id, customer_id, entity_id)
+ WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id)
+ WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
+ WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id)
+ WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.sql b/application/src/main/data/upgrade/2.1.1/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..a840af7e4fada8d0506701722eb87fa0b09c00be
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.1/schema_update.sql
@@ -0,0 +1,29 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS entity_views (
+ id varchar(31) NOT NULL CONSTRAINT entity_views_pkey PRIMARY KEY,
+ entity_id varchar(31),
+ entity_type varchar(255),
+ tenant_id varchar(31),
+ customer_id varchar(31),
+ name varchar(255),
+ keys varchar(255),
+ start_ts bigint,
+ end_ts bigint,
+ search_text varchar(255),
+ additional_info varchar
+);
diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.cql b/application/src/main/data/upgrade/2.1.2/schema_update.cql
new file mode 100644
index 0000000000000000000000000000000000000000..0a27a912a02551d5f3ada4ba4c35eafdf5213966
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.2/schema_update.cql
@@ -0,0 +1,110 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id;
+
+DROP TABLE IF EXISTS thingsboard.entity_views;
+
+CREATE TABLE IF NOT EXISTS thingsboard.entity_view (
+ id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ tenant_id timeuuid,
+ customer_id timeuuid,
+ name text,
+ type text,
+ keys text,
+ start_ts bigint,
+ end_ts bigint,
+ search_text text,
+ additional_info text,
+ PRIMARY KEY (id, entity_id, tenant_id, customer_id, type)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND type IS NOT NULL
+ AND name IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, name, id, customer_id, entity_id, type)
+ WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id, type)
+ WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_by_type_and_search_text AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, type, search_text, id, customer_id, entity_id)
+ WITH CLUSTERING ORDER BY (type ASC, search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id, type)
+ WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer_and_type AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, type, customer_id, search_text, id, entity_id)
+ WITH CLUSTERING ORDER BY (type ASC, customer_id DESC, search_text ASC, id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type)
+ WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.sql b/application/src/main/data/upgrade/2.1.2/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..d7baf2ec7e4cc894f324defb109202cfcb04dc58
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.2/schema_update.sql
@@ -0,0 +1,32 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP TABLE IF EXISTS entity_views;
+
+CREATE TABLE IF NOT EXISTS entity_view (
+ id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY,
+ entity_id varchar(31),
+ entity_type varchar(255),
+ tenant_id varchar(31),
+ customer_id varchar(31),
+ type varchar(255),
+ name varchar(255),
+ keys varchar(255),
+ start_ts bigint,
+ end_ts bigint,
+ search_text varchar(255),
+ additional_info varchar
+);
diff --git a/application/src/main/data/upgrade/2.2.0/schema_update.sql b/application/src/main/data/upgrade/2.2.0/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9fc8d11189360d9f189084f2c797ac334d396dad
--- /dev/null
+++ b/application/src/main/data/upgrade/2.2.0/schema_update.sql
@@ -0,0 +1,19 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE component_descriptor ADD UNIQUE (clazz);
+
+ALTER TABLE entity_view ALTER COLUMN keys SET DATA TYPE varchar(10000000);
diff --git a/application/src/main/data/upgrade/2.3.1/schema_update.sql b/application/src/main/data/upgrade/2.3.1/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..7fd717e253d0e52c3f86b50720667b4b8e1959f7
--- /dev/null
+++ b/application/src/main/data/upgrade/2.3.1/schema_update.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE event ALTER COLUMN body SET DATA TYPE varchar(10000000);
diff --git a/application/src/main/data/upgrade/2.4.0/schema_update.sql b/application/src/main/data/upgrade/2.4.0/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..835b1940ea374d3c2161f13ef8ce14c7ad7def9c
--- /dev/null
+++ b/application/src/main/data/upgrade/2.4.0/schema_update.sql
@@ -0,0 +1,23 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(tenant_id, type, originator_type, originator_id);
+
+CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id);
+
+CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id);
+
+CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id);
diff --git a/application/src/main/data/upgrade/2.4.2/schema_update.sql b/application/src/main/data/upgrade/2.4.2/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..d99aaa4d94c977880922be76b925f7b0c3637f36
--- /dev/null
+++ b/application/src/main/data/upgrade/2.4.2/schema_update.sql
@@ -0,0 +1,31 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP INDEX IF EXISTS idx_alarm_originator_alarm_type;
+
+CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC);
+
+CREATE INDEX IF NOT EXISTS idx_device_customer_id ON device(tenant_id, customer_id);
+
+CREATE INDEX IF NOT EXISTS idx_device_customer_id_and_type ON device(tenant_id, customer_id, type);
+
+CREATE INDEX IF NOT EXISTS idx_device_type ON device(tenant_id, type);
+
+CREATE INDEX IF NOT EXISTS idx_asset_customer_id ON asset(tenant_id, customer_id);
+
+CREATE INDEX IF NOT EXISTS idx_asset_customer_id_and_type ON asset(tenant_id, customer_id, type);
+
+CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql
new file mode 100644
index 0000000000000000000000000000000000000000..936911b6a1d89a91c16b8a771755594fbc584d12
--- /dev/null
+++ b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql
@@ -0,0 +1,209 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ max_tenant_ttl bigint;
+ max_customer_ttl bigint;
+ max_ttl bigint;
+ date timestamp;
+ partition_by_max_ttl_date varchar;
+ partition_by_max_ttl_month varchar;
+ partition_by_max_ttl_day varchar;
+ partition_by_max_ttl_year varchar;
+ partition varchar;
+ partition_year integer;
+ partition_month integer;
+ partition_day integer;
+
+BEGIN
+ SELECT max(attribute_kv.long_v)
+ FROM tenant
+ INNER JOIN attribute_kv ON tenant.id = attribute_kv.entity_id
+ WHERE attribute_kv.attribute_key = 'TTL'
+ into max_tenant_ttl;
+ SELECT max(attribute_kv.long_v)
+ FROM customer
+ INNER JOIN attribute_kv ON customer.id = attribute_kv.entity_id
+ WHERE attribute_kv.attribute_key = 'TTL'
+ into max_customer_ttl;
+ max_ttl := GREATEST(system_ttl, max_customer_ttl, max_tenant_ttl);
+ if max_ttl IS NOT NULL AND max_ttl > 0 THEN
+ date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl);
+ partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date);
+ RAISE NOTICE 'Date by max ttl: %', date;
+ RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date;
+ IF partition_by_max_ttl_date IS NOT NULL THEN
+ CASE
+ WHEN partition_type = 'DAYS' THEN
+ partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
+ partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
+ partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5);
+ WHEN partition_type = 'MONTHS' THEN
+ partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
+ partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
+ ELSE
+ partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
+ END CASE;
+ IF partition_by_max_ttl_year IS NULL THEN
+ RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!';
+ ELSE
+ IF partition_type = 'YEARS' THEN
+ FOR partition IN SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename like 'ts_kv_' || '%'
+ AND tablename != 'ts_kv_latest'
+ AND tablename != 'ts_kv_dictionary'
+ AND tablename != 'ts_kv_indefinite'
+ AND tablename != partition_by_max_ttl_date
+ LOOP
+ partition_year := SPLIT_PART(partition, '_', 3)::integer;
+ IF partition_year < partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ END IF;
+ END LOOP;
+ ELSE
+ IF partition_type = 'MONTHS' THEN
+ IF partition_by_max_ttl_month IS NULL THEN
+ RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!';
+ ELSE
+ FOR partition IN SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename like 'ts_kv_' || '%'
+ AND tablename != 'ts_kv_latest'
+ AND tablename != 'ts_kv_dictionary'
+ AND tablename != 'ts_kv_indefinite'
+ AND tablename != partition_by_max_ttl_date
+ LOOP
+ partition_year := SPLIT_PART(partition, '_', 3)::integer;
+ IF partition_year > partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_year < partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ ELSE
+ partition_month := SPLIT_PART(partition, '_', 4)::integer;
+ IF partition_year = partition_by_max_ttl_year::integer THEN
+ IF partition_month >= partition_by_max_ttl_month::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END LOOP;
+ END IF;
+ ELSE
+ IF partition_type = 'DAYS' THEN
+ IF partition_by_max_ttl_month IS NULL THEN
+ RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!';
+ ELSE
+ IF partition_by_max_ttl_day IS NULL THEN
+ RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!';
+ ELSE
+ FOR partition IN SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename like 'ts_kv_' || '%'
+ AND tablename != 'ts_kv_latest'
+ AND tablename != 'ts_kv_dictionary'
+ AND tablename != 'ts_kv_indefinite'
+ AND tablename != partition_by_max_ttl_date
+ LOOP
+ partition_year := SPLIT_PART(partition, '_', 3)::integer;
+ IF partition_year > partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_year < partition_by_max_ttl_year::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ ELSE
+ partition_month := SPLIT_PART(partition, '_', 4)::integer;
+ IF partition_month > partition_by_max_ttl_month::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_month < partition_by_max_ttl_month::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ ELSE
+ partition_day := SPLIT_PART(partition, '_', 5)::integer;
+ IF partition_day >= partition_by_max_ttl_day::integer THEN
+ RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
+ CONTINUE;
+ ELSE
+ IF partition_day < partition_by_max_ttl_day::integer THEN
+ RAISE NOTICE 'Partition to delete by max ttl: %', partition;
+ EXECUTE format('DROP TABLE IF EXISTS %I', partition);
+ deleted := deleted + 1;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END LOOP;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+ END IF;
+END
+$$;
+
+CREATE OR REPLACE FUNCTION get_partition_by_max_ttl_date(IN partition_type varchar, IN date timestamp, OUT partition varchar) AS
+$$
+BEGIN
+ CASE
+ WHEN partition_type = 'DAYS' THEN
+ partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM') || '_' || to_char(date, 'dd');
+ WHEN partition_type = 'MONTHS' THEN
+ partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM');
+ WHEN partition_type = 'YEARS' THEN
+ partition := 'ts_kv_' || to_char(date, 'yyyy');
+ ELSE
+ partition := NULL;
+ END CASE;
+ IF partition IS NOT NULL THEN
+ IF NOT EXISTS(SELECT
+ FROM pg_tables
+ WHERE schemaname = 'public'
+ AND tablename = partition) THEN
+ partition := NULL;
+ RAISE NOTICE 'Failed to found partition by ttl';
+ END IF;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql
new file mode 100644
index 0000000000000000000000000000000000000000..5cf51d13edbb0e4d8c421e968fc885084ce3d6b4
--- /dev/null
+++ b/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql
@@ -0,0 +1,359 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+-- call create_partition_ts_kv_table();
+
+CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table()
+ LANGUAGE plpgsql AS
+$$
+
+BEGIN
+ ALTER TABLE ts_kv
+ DROP CONSTRAINT IF EXISTS ts_kv_unq_key;
+ ALTER TABLE ts_kv
+ DROP CONSTRAINT IF EXISTS ts_kv_pkey;
+ ALTER TABLE ts_kv
+ ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_type, entity_id, key, ts);
+ ALTER TABLE ts_kv
+ RENAME TO ts_kv_old;
+ ALTER TABLE ts_kv_old
+ RENAME CONSTRAINT ts_kv_pkey TO ts_kv_pkey_old;
+ CREATE TABLE IF NOT EXISTS ts_kv
+ (
+ LIKE ts_kv_old
+ )
+ PARTITION BY RANGE (ts);
+ ALTER TABLE ts_kv
+ DROP COLUMN entity_type;
+ ALTER TABLE ts_kv
+ ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
+ ALTER TABLE ts_kv
+ ALTER COLUMN key TYPE integer USING key::integer;
+ ALTER TABLE ts_kv
+ ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts);
+ CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;
+END;
+$$;
+
+-- call create_new_ts_kv_latest_table();
+
+CREATE OR REPLACE PROCEDURE create_new_ts_kv_latest_table()
+ LANGUAGE plpgsql AS
+$$
+
+BEGIN
+ IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest_old') THEN
+ ALTER TABLE ts_kv_latest
+ DROP CONSTRAINT IF EXISTS ts_kv_latest_unq_key;
+ ALTER TABLE ts_kv_latest
+ DROP CONSTRAINT IF EXISTS ts_kv_latest_pkey;
+ ALTER TABLE ts_kv_latest
+ ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_type, entity_id, key);
+ ALTER TABLE ts_kv_latest
+ RENAME TO ts_kv_latest_old;
+ ALTER TABLE ts_kv_latest_old
+ RENAME CONSTRAINT ts_kv_latest_pkey TO ts_kv_latest_pkey_old;
+ CREATE TABLE IF NOT EXISTS ts_kv_latest
+ (
+ LIKE ts_kv_latest_old
+ );
+ ALTER TABLE ts_kv_latest
+ DROP COLUMN entity_type;
+ ALTER TABLE ts_kv_latest
+ ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
+ ALTER TABLE ts_kv_latest
+ ALTER COLUMN key TYPE integer USING key::integer;
+ ALTER TABLE ts_kv_latest
+ ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key);
+ ELSE
+ RAISE NOTICE 'ts_kv_latest_old table already exists!';
+ IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest') THEN
+ CREATE TABLE IF NOT EXISTS ts_kv_latest
+ (
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
+ );
+ END IF;
+ END IF;
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION get_partitions_data(IN partition_type varchar)
+ RETURNS
+ TABLE
+ (
+ partition_date text,
+ from_ts bigint,
+ to_ts bigint
+ )
+AS
+$$
+BEGIN
+ CASE
+ WHEN partition_type = 'DAYS' THEN
+ RETURN QUERY SELECT day_date.day AS partition_date,
+ (extract(epoch from (day_date.day)::timestamp) * 1000)::bigint AS from_ts,
+ (extract(epoch from (day_date.day::date + INTERVAL '1 DAY')::timestamp) *
+ 1000)::bigint AS to_ts
+ FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_MM_DD') AS day
+ FROM ts_kv_old) AS day_date;
+ WHEN partition_type = 'MONTHS' THEN
+ RETURN QUERY SELECT SUBSTRING(month_date.first_date, 1, 7) AS partition_date,
+ (extract(epoch from (month_date.first_date)::timestamp) * 1000)::bigint AS from_ts,
+ (extract(epoch from (month_date.first_date::date + INTERVAL '1 MONTH')::timestamp) *
+ 1000)::bigint AS to_ts
+ FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_MM_01') AS first_date
+ FROM ts_kv_old) AS month_date;
+ WHEN partition_type = 'YEARS' THEN
+ RETURN QUERY SELECT SUBSTRING(year_date.year, 1, 4) AS partition_date,
+ (extract(epoch from (year_date.year)::timestamp) * 1000)::bigint AS from_ts,
+ (extract(epoch from (year_date.year::date + INTERVAL '1 YEAR')::timestamp) *
+ 1000)::bigint AS to_ts
+ FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_01_01') AS year
+ FROM ts_kv_old) AS year_date;
+ ELSE
+ RAISE EXCEPTION 'Failed to parse partitioning property: % !', partition_type;
+ END CASE;
+END;
+$$ LANGUAGE plpgsql;
+
+-- call create_partitions();
+
+CREATE OR REPLACE PROCEDURE create_partitions(IN partition_type varchar)
+ LANGUAGE plpgsql AS
+$$
+
+DECLARE
+ partition_date varchar;
+ from_ts bigint;
+ to_ts bigint;
+ partitions_cursor CURSOR FOR SELECT *
+ FROM get_partitions_data(partition_type);
+BEGIN
+ OPEN partitions_cursor;
+ LOOP
+ FETCH partitions_cursor INTO partition_date, from_ts, to_ts;
+ EXIT WHEN NOT FOUND;
+ EXECUTE 'CREATE TABLE IF NOT EXISTS ts_kv_' || partition_date ||
+ ' PARTITION OF ts_kv FOR VALUES FROM (' || from_ts ||
+ ') TO (' || to_ts || ');';
+ RAISE NOTICE 'A partition % has been created!',CONCAT('ts_kv_', partition_date);
+ END LOOP;
+
+ CLOSE partitions_cursor;
+END;
+$$;
+
+-- call create_ts_kv_dictionary_table();
+
+CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table()
+ LANGUAGE plpgsql AS
+$$
+
+BEGIN
+ CREATE TABLE IF NOT EXISTS ts_kv_dictionary
+ (
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+ );
+END;
+$$;
+
+-- call insert_into_dictionary();
+
+CREATE OR REPLACE PROCEDURE insert_into_dictionary()
+ LANGUAGE plpgsql AS
+$$
+
+DECLARE
+ insert_record RECORD;
+ key_cursor CURSOR FOR SELECT DISTINCT key
+ FROM ts_kv_old
+ ORDER BY key;
+BEGIN
+ OPEN key_cursor;
+ LOOP
+ FETCH key_cursor INTO insert_record;
+ EXIT WHEN NOT FOUND;
+ IF NOT EXISTS(SELECT key FROM ts_kv_dictionary WHERE key = insert_record.key) THEN
+ INSERT INTO ts_kv_dictionary(key) VALUES (insert_record.key);
+ RAISE NOTICE 'Key: % has been inserted into the dictionary!',insert_record.key;
+ ELSE
+ RAISE NOTICE 'Key: % already exists in the dictionary!',insert_record.key;
+ END IF;
+ END LOOP;
+ CLOSE key_cursor;
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar)
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id,
+ ts_kv_records.key AS key,
+ ts_kv_records.ts AS ts,
+ ts_kv_records.bool_v AS bool_v,
+ ts_kv_records.str_v AS str_v,
+ ts_kv_records.long_v AS long_v,
+ ts_kv_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM ts_kv_old
+ INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records) TO %L;',
+ path_to_file);
+ EXECUTE format('COPY ts_kv FROM %L', path_to_file);
+END
+$$;
+
+-- call insert_into_ts_kv_latest();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest(IN path_to_file varchar)
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id,
+ ts_kv_latest_records.key AS key,
+ ts_kv_latest_records.ts AS ts,
+ ts_kv_latest_records.bool_v AS bool_v,
+ ts_kv_latest_records.str_v AS str_v,
+ ts_kv_latest_records.long_v AS long_v,
+ ts_kv_latest_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM ts_kv_latest_old
+ INNER JOIN ts_kv_dictionary ON (ts_kv_latest_old.key = ts_kv_dictionary.key)) AS ts_kv_latest_records) TO %L;',
+ path_to_file);
+ EXECUTE format('COPY ts_kv_latest FROM %L', path_to_file);
+END;
+$$;
+
+-- call insert_into_ts_kv_cursor();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ insert_size CONSTANT integer := 10000;
+ insert_counter integer DEFAULT 0;
+ insert_record RECORD;
+ insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
+ ts_kv_records.key AS key,
+ ts_kv_records.ts AS ts,
+ ts_kv_records.bool_v AS bool_v,
+ ts_kv_records.str_v AS str_v,
+ ts_kv_records.long_v AS long_v,
+ ts_kv_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM ts_kv_old
+ INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records;
+BEGIN
+ OPEN insert_cursor;
+ LOOP
+ insert_counter := insert_counter + 1;
+ FETCH insert_cursor INTO insert_record;
+ IF NOT FOUND THEN
+ RAISE NOTICE '% records have been inserted into the partitioned ts_kv!',insert_counter - 1;
+ EXIT;
+ END IF;
+ INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
+ VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
+ insert_record.long_v, insert_record.dbl_v);
+ IF MOD(insert_counter, insert_size) = 0 THEN
+ RAISE NOTICE '% records have been inserted into the partitioned ts_kv!',insert_counter;
+ END IF;
+ END LOOP;
+ CLOSE insert_cursor;
+END;
+$$;
+
+-- call insert_into_ts_kv_latest_cursor();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest_cursor()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ insert_size CONSTANT integer := 10000;
+ insert_counter integer DEFAULT 0;
+ insert_record RECORD;
+ insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
+ ts_kv_latest_records.key AS key,
+ ts_kv_latest_records.ts AS ts,
+ ts_kv_latest_records.bool_v AS bool_v,
+ ts_kv_latest_records.str_v AS str_v,
+ ts_kv_latest_records.long_v AS long_v,
+ ts_kv_latest_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM ts_kv_latest_old
+ INNER JOIN ts_kv_dictionary ON (ts_kv_latest_old.key = ts_kv_dictionary.key)) AS ts_kv_latest_records;
+BEGIN
+ OPEN insert_cursor;
+ LOOP
+ insert_counter := insert_counter + 1;
+ FETCH insert_cursor INTO insert_record;
+ IF NOT FOUND THEN
+ RAISE NOTICE '% records have been inserted into the ts_kv_latest!',insert_counter - 1;
+ EXIT;
+ END IF;
+ INSERT INTO ts_kv_latest(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
+ VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
+ insert_record.long_v, insert_record.dbl_v);
+ IF MOD(insert_counter, insert_size) = 0 THEN
+ RAISE NOTICE '% records have been inserted into the ts_kv_latest!',insert_counter;
+ END IF;
+ END LOOP;
+ CLOSE insert_cursor;
+END;
+$$;
+
diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql b/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql
new file mode 100644
index 0000000000000000000000000000000000000000..58f69d034dfbf29b3aacaa9ff0d42e01e37a4994
--- /dev/null
+++ b/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql
@@ -0,0 +1,208 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+-- call create_new_ts_kv_table();
+
+CREATE OR REPLACE PROCEDURE create_new_ts_kv_table() LANGUAGE plpgsql AS $$
+
+BEGIN
+ ALTER TABLE tenant_ts_kv
+ RENAME TO tenant_ts_kv_old;
+ CREATE TABLE IF NOT EXISTS ts_kv
+ (
+ LIKE tenant_ts_kv_old
+ );
+ ALTER TABLE ts_kv ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
+ ALTER TABLE ts_kv ALTER COLUMN key TYPE integer USING key::integer;
+ ALTER INDEX ts_kv_pkey RENAME TO tenant_ts_kv_pkey_old;
+ ALTER INDEX idx_tenant_ts_kv RENAME TO idx_tenant_ts_kv_old;
+ ALTER INDEX tenant_ts_kv_ts_idx RENAME TO tenant_ts_kv_ts_idx_old;
+ ALTER TABLE ts_kv ADD CONSTRAINT ts_kv_pkey PRIMARY KEY(entity_id, key, ts);
+-- CREATE INDEX IF NOT EXISTS ts_kv_ts_idx ON ts_kv(ts DESC);
+ ALTER TABLE ts_kv DROP COLUMN IF EXISTS tenant_id;
+END;
+$$;
+
+
+-- call create_ts_kv_latest_table();
+
+CREATE OR REPLACE PROCEDURE create_ts_kv_latest_table() LANGUAGE plpgsql AS $$
+
+BEGIN
+ CREATE TABLE IF NOT EXISTS ts_kv_latest
+ (
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
+ );
+END;
+$$;
+
+
+-- call create_ts_kv_dictionary_table();
+
+CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table() LANGUAGE plpgsql AS $$
+
+BEGIN
+ CREATE TABLE IF NOT EXISTS ts_kv_dictionary
+ (
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+ );
+END;
+$$;
+
+-- call insert_into_dictionary();
+
+CREATE OR REPLACE PROCEDURE insert_into_dictionary() LANGUAGE plpgsql AS $$
+
+DECLARE
+ insert_record RECORD;
+ key_cursor CURSOR FOR SELECT DISTINCT key
+ FROM tenant_ts_kv_old
+ ORDER BY key;
+BEGIN
+ OPEN key_cursor;
+ LOOP
+ FETCH key_cursor INTO insert_record;
+ EXIT WHEN NOT FOUND;
+ IF NOT EXISTS(SELECT key FROM ts_kv_dictionary WHERE key = insert_record.key) THEN
+ INSERT INTO ts_kv_dictionary(key) VALUES (insert_record.key);
+ RAISE NOTICE 'Key: % has been inserted into the dictionary!',insert_record.key;
+ ELSE
+ RAISE NOTICE 'Key: % already exists in the dictionary!',insert_record.key;
+ END IF;
+ END LOOP;
+ CLOSE key_cursor;
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
+
+-- call insert_into_ts_kv();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar) LANGUAGE plpgsql AS $$
+BEGIN
+
+ EXECUTE format ('COPY (SELECT to_uuid(entity_id) AS entity_id,
+ new_ts_kv_records.key AS key,
+ new_ts_kv_records.ts AS ts,
+ new_ts_kv_records.bool_v AS bool_v,
+ new_ts_kv_records.str_v AS str_v,
+ new_ts_kv_records.long_v AS long_v,
+ new_ts_kv_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM tenant_ts_kv_old
+ INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records) TO %L;', path_to_file);
+ EXECUTE format ('COPY ts_kv FROM %L', path_to_file);
+END;
+$$;
+
+-- call insert_into_ts_kv_latest();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest() LANGUAGE plpgsql AS $$
+
+DECLARE
+ insert_size CONSTANT integer := 10000;
+ insert_counter integer DEFAULT 0;
+ latest_record RECORD;
+ insert_record RECORD;
+ insert_cursor CURSOR FOR SELECT
+ latest_records.key AS key,
+ latest_records.entity_id AS entity_id,
+ latest_records.ts AS ts
+ FROM (SELECT DISTINCT key AS key, entity_id AS entity_id, MAX(ts) AS ts FROM ts_kv GROUP BY key, entity_id) AS latest_records;
+BEGIN
+ OPEN insert_cursor;
+ LOOP
+ insert_counter := insert_counter + 1;
+ FETCH insert_cursor INTO latest_record;
+ IF NOT FOUND THEN
+ RAISE NOTICE '% records have been inserted into the ts_kv_latest table!',insert_counter - 1;
+ EXIT;
+ END IF;
+ SELECT entity_id AS entity_id, key AS key, ts AS ts, bool_v AS bool_v, str_v AS str_v, long_v AS long_v, dbl_v AS dbl_v INTO insert_record FROM ts_kv WHERE entity_id = latest_record.entity_id AND key = latest_record.key AND ts = latest_record.ts;
+ INSERT INTO ts_kv_latest(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
+ VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v, insert_record.long_v, insert_record.dbl_v);
+ IF MOD(insert_counter, insert_size) = 0 THEN
+ RAISE NOTICE '% records have been inserted into the ts_kv_latest table!',insert_counter;
+ END IF;
+ END LOOP;
+ CLOSE insert_cursor;
+END;
+$$;
+
+-- call insert_into_ts_kv_cursor();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor() LANGUAGE plpgsql AS $$
+
+DECLARE
+ insert_size CONSTANT integer := 10000;
+ insert_counter integer DEFAULT 0;
+ insert_record RECORD;
+ insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
+ new_ts_kv_records.key AS key,
+ new_ts_kv_records.ts AS ts,
+ new_ts_kv_records.bool_v AS bool_v,
+ new_ts_kv_records.str_v AS str_v,
+ new_ts_kv_records.long_v AS long_v,
+ new_ts_kv_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM tenant_ts_kv_old
+ INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records;
+BEGIN
+ OPEN insert_cursor;
+ LOOP
+ insert_counter := insert_counter + 1;
+ FETCH insert_cursor INTO insert_record;
+ IF NOT FOUND THEN
+ RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter - 1;
+ EXIT;
+ END IF;
+ INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
+ VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
+ insert_record.long_v, insert_record.dbl_v);
+ IF MOD(insert_counter, insert_size) = 0 THEN
+ RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter;
+ END IF;
+ END LOOP;
+ CLOSE insert_cursor;
+END;
+$$;
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_ttl.sql b/application/src/main/data/upgrade/2.4.3/schema_update_ttl.sql
new file mode 100644
index 0000000000000000000000000000000000000000..2a5ca580927b56effd3409c24c10abee6f1af01f
--- /dev/null
+++ b/application/src/main/data/upgrade/2.4.3/schema_update_ttl.sql
@@ -0,0 +1,150 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_device_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT device.id as entity_id FROM device WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_asset_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT asset.id as entity_id FROM asset WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_customer_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint,
+ OUT deleted bigint) AS
+$$
+BEGIN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT customer.id as entity_id FROM customer WHERE tenant_id = %L and id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted',
+ tenant_id, customer_id, ttl) into deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid,
+ IN system_ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ tenant_cursor CURSOR FOR select tenant.id as tenant_id
+ from tenant;
+ tenant_id_record uuid;
+ customer_id_record uuid;
+ tenant_ttl bigint;
+ customer_ttl bigint;
+ deleted_for_entities bigint;
+ tenant_ttl_ts bigint;
+ customer_ttl_ts bigint;
+BEGIN
+ OPEN tenant_cursor;
+ FETCH tenant_cursor INTO tenant_id_record;
+ WHILE FOUND
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ tenant_id_record, 'TTL') INTO tenant_ttl;
+ if tenant_ttl IS NULL THEN
+ tenant_ttl := system_ttl;
+ END IF;
+ IF tenant_ttl > 0 THEN
+ tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint;
+ deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record;
+ END IF;
+ FOR customer_id_record IN
+ SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ customer_id_record, 'TTL') INTO customer_ttl;
+ IF customer_ttl IS NULL THEN
+ customer_ttl_ts := tenant_ttl_ts;
+ ELSE
+ IF customer_ttl > 0 THEN
+ customer_ttl_ts :=
+ (EXTRACT(EPOCH FROM current_timestamp) * 1000 -
+ customer_ttl::bigint * 1000)::bigint;
+ END IF;
+ END IF;
+ IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN
+ deleted_for_entities :=
+ delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record;
+ deleted_for_entities :=
+ delete_device_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record,
+ customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+ END IF;
+ END LOOP;
+ FETCH tenant_cursor INTO tenant_id_record;
+ END LOOP;
+END
+$$;
+
+CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ ttl_ts bigint;
+ debug_ttl_ts bigint;
+ ttl_deleted_count bigint DEFAULT 0;
+ debug_ttl_deleted_count bigint DEFAULT 0;
+BEGIN
+ IF ttl > 0 THEN
+ ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type != %L::varchar AND event_type != %L::varchar) RETURNING *) SELECT count(*) FROM deleted', ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count;
+ END IF;
+ IF debug_ttl > 0 THEN
+ debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint;
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type = %L::varchar OR event_type = %L::varchar) RETURNING *) SELECT count(*) FROM deleted', debug_ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count;
+ END IF;
+ RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;
+ RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;
+ deleted := ttl_deleted_count + debug_ttl_deleted_count;
+END
+$$;
diff --git a/application/src/main/data/upgrade/3.0.1/schema_ts_latest.sql b/application/src/main/data/upgrade/3.0.1/schema_ts_latest.sql
new file mode 100644
index 0000000000000000000000000000000000000000..a270af1d53758010e33134ed5888aa999b71809d
--- /dev/null
+++ b/application/src/main/data/upgrade/3.0.1/schema_ts_latest.sql
@@ -0,0 +1,35 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS ts_kv_latest
+(
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
+);
+
+CREATE TABLE IF NOT EXISTS ts_kv_dictionary
+(
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+);
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql b/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql
new file mode 100644
index 0000000000000000000000000000000000000000..01f443151dba60d9dceb0db91b66749b7cce62c8
--- /dev/null
+++ b/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql
@@ -0,0 +1,878 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION extract_ts(uuid UUID) RETURNS BIGINT AS
+$$
+DECLARE
+ bytes bytea;
+BEGIN
+ bytes := uuid_send(uuid);
+ RETURN
+ (
+ (
+ (get_byte(bytes, 0)::bigint << 24) |
+ (get_byte(bytes, 1)::bigint << 16) |
+ (get_byte(bytes, 2)::bigint << 8) |
+ (get_byte(bytes, 3)::bigint << 0)
+ ) + (
+ ((get_byte(bytes, 4)::bigint << 8 |
+ get_byte(bytes, 5)::bigint)) << 32
+ ) + (
+ (((get_byte(bytes, 6)::bigint & 15) << 8 | get_byte(bytes, 7)::bigint) & 4095) << 48
+ ) - 122192928000000000
+ ) / 10000::double precision;
+END
+$$ LANGUAGE plpgsql
+ IMMUTABLE
+ PARALLEL SAFE
+ RETURNS NULL ON NULL INPUT;
+
+
+CREATE OR REPLACE FUNCTION column_type_to_uuid(table_name varchar, column_name varchar) RETURNS VOID
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ execute format('ALTER TABLE %s RENAME COLUMN %s TO old_%s;', table_name, column_name, column_name);
+ execute format('ALTER TABLE %s ADD COLUMN %s UUID;', table_name, column_name);
+ execute format('UPDATE %s SET %s = to_uuid(old_%s) WHERE old_%s is not null;', table_name, column_name, column_name, column_name);
+ execute format('ALTER TABLE %s DROP COLUMN old_%s;', table_name, column_name);
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION get_column_type(table_name varchar, column_name varchar, OUT data_type varchar) RETURNS varchar
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ execute (format('SELECT data_type from information_schema.columns where table_name = %L and column_name = %L',
+ table_name, column_name)) INTO data_type;
+END;
+$$;
+
+
+CREATE OR REPLACE PROCEDURE drop_all_idx()
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ DROP INDEX IF EXISTS idx_alarm_originator_alarm_type;
+ DROP INDEX IF EXISTS idx_alarm_originator_created_time;
+ DROP INDEX IF EXISTS idx_alarm_tenant_created_time;
+ DROP INDEX IF EXISTS idx_event_type_entity_id;
+ DROP INDEX IF EXISTS idx_relation_to_id;
+ DROP INDEX IF EXISTS idx_relation_from_id;
+ DROP INDEX IF EXISTS idx_device_customer_id;
+ DROP INDEX IF EXISTS idx_device_customer_id_and_type;
+ DROP INDEX IF EXISTS idx_device_type;
+ DROP INDEX IF EXISTS idx_asset_customer_id;
+ DROP INDEX IF EXISTS idx_asset_customer_id_and_type;
+ DROP INDEX IF EXISTS idx_asset_type;
+ DROP INDEX IF EXISTS idx_attribute_kv_by_key_and_last_update_ts;
+END;
+$$;
+
+CREATE OR REPLACE PROCEDURE create_all_idx()
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC);
+ CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC);
+ CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC);
+ CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id);
+ CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id);
+ CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id);
+ CREATE INDEX IF NOT EXISTS idx_device_customer_id ON device(tenant_id, customer_id);
+ CREATE INDEX IF NOT EXISTS idx_device_customer_id_and_type ON device(tenant_id, customer_id, type);
+ CREATE INDEX IF NOT EXISTS idx_device_type ON device(tenant_id, type);
+ CREATE INDEX IF NOT EXISTS idx_asset_customer_id ON asset(tenant_id, customer_id);
+ CREATE INDEX IF NOT EXISTS idx_asset_customer_id_and_type ON asset(tenant_id, customer_id, type);
+ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
+ CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
+END;
+$$;
+
+
+-- admin_settings
+CREATE OR REPLACE PROCEDURE update_admin_settings()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'admin_settings';
+ column_id varchar := 'id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE admin_settings DROP CONSTRAINT admin_settings_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE admin_settings ADD CONSTRAINT admin_settings_pkey PRIMARY KEY (id);
+ ALTER TABLE admin_settings ADD COLUMN created_time BIGINT;
+ UPDATE admin_settings SET created_time = extract_ts(id) WHERE id is not null;
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+END;
+$$;
+
+
+-- alarm
+CREATE OR REPLACE PROCEDURE update_alarm()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'alarm';
+ column_id varchar := 'id';
+ column_originator_id varchar := 'originator_id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE alarm DROP CONSTRAINT alarm_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE alarm ADD COLUMN created_time BIGINT;
+ UPDATE alarm SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE alarm ADD CONSTRAINT alarm_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_originator_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_originator_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_originator_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_originator_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+
+-- asset
+CREATE OR REPLACE PROCEDURE update_asset()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'asset';
+ column_id varchar := 'id';
+ column_customer_id varchar := 'customer_id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE asset DROP CONSTRAINT asset_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE asset ADD COLUMN created_time BIGINT;
+ UPDATE asset SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE asset ADD CONSTRAINT asset_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_customer_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_customer_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE asset DROP CONSTRAINT asset_name_unq_key;
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ ALTER TABLE asset ADD CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+ END;
+$$;
+
+-- attribute_kv
+CREATE OR REPLACE PROCEDURE update_attribute_kv()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'attribute_kv';
+ column_entity_id varchar := 'entity_id';
+BEGIN
+ data_type := get_column_type(table_name, column_entity_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE attribute_kv DROP CONSTRAINT attribute_kv_pkey;
+ PERFORM column_type_to_uuid(table_name, column_entity_id);
+ ALTER TABLE attribute_kv ADD CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id;
+ END IF;
+END;
+$$;
+
+-- audit_log
+CREATE OR REPLACE PROCEDURE update_audit_log()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'audit_log';
+ column_id varchar := 'id';
+ column_customer_id varchar := 'customer_id';
+ column_tenant_id varchar := 'tenant_id';
+ column_entity_id varchar := 'entity_id';
+ column_user_id varchar := 'user_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE audit_log DROP CONSTRAINT audit_log_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE audit_log ADD COLUMN created_time BIGINT;
+ UPDATE audit_log SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE audit_log ADD CONSTRAINT audit_log_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_customer_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_customer_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_entity_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_entity_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_user_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_user_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_user_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_user_id;
+ END IF;
+END;
+$$;
+
+
+-- component_descriptor
+CREATE OR REPLACE PROCEDURE update_component_descriptor()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'component_descriptor';
+ column_id varchar := 'id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE component_descriptor DROP CONSTRAINT component_descriptor_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE component_descriptor ADD CONSTRAINT component_descriptor_pkey PRIMARY KEY (id);
+ ALTER TABLE component_descriptor ADD COLUMN created_time BIGINT;
+ UPDATE component_descriptor SET created_time = extract_ts(id) WHERE id is not null;
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+END;
+$$;
+
+-- customer
+CREATE OR REPLACE PROCEDURE update_customer()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'customer';
+ column_id varchar := 'id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE customer DROP CONSTRAINT customer_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE customer ADD CONSTRAINT customer_pkey PRIMARY KEY (id);
+ ALTER TABLE customer ADD COLUMN created_time BIGINT;
+ UPDATE customer SET created_time = extract_ts(id) WHERE id is not null;
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+
+-- dashboard
+CREATE OR REPLACE PROCEDURE update_dashboard()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'dashboard';
+ column_id varchar := 'id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE dashboard DROP CONSTRAINT dashboard_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE dashboard ADD CONSTRAINT dashboard_pkey PRIMARY KEY (id);
+ ALTER TABLE dashboard ADD COLUMN created_time BIGINT;
+ UPDATE dashboard SET created_time = extract_ts(id) WHERE id is not null;
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+-- device
+CREATE OR REPLACE PROCEDURE update_device()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'device';
+ column_id varchar := 'id';
+ column_customer_id varchar := 'customer_id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE device DROP CONSTRAINT device_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE device ADD COLUMN created_time BIGINT;
+ UPDATE device SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE device ADD CONSTRAINT device_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_customer_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_customer_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE device DROP CONSTRAINT device_name_unq_key;
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ ALTER TABLE device ADD CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+
+-- device_credentials
+CREATE OR REPLACE PROCEDURE update_device_credentials()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'device_credentials';
+ column_id varchar := 'id';
+ column_device_id varchar := 'device_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE device_credentials DROP CONSTRAINT device_credentials_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE device_credentials ADD COLUMN created_time BIGINT;
+ UPDATE device_credentials SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE device_credentials ADD CONSTRAINT device_credentials_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_device_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE device_credentials DROP CONSTRAINT IF EXISTS device_credentials_device_id_unq_key;
+ PERFORM column_type_to_uuid(table_name, column_device_id);
+ -- remove duplicate credentials with same device_id
+ DELETE from device_credentials where id in (
+ select dc.id
+ from (
+ SELECT id, device_id,
+ ROW_NUMBER() OVER (
+ PARTITION BY
+ device_id
+ ORDER BY
+ created_time DESC
+ ) row_num
+ FROM
+ device_credentials
+ ) as dc
+ WHERE dc.row_num > 1
+ );
+ ALTER TABLE device_credentials ADD CONSTRAINT device_credentials_device_id_unq_key UNIQUE (device_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_device_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_device_id;
+ END IF;
+END;
+$$;
+
+
+-- event
+CREATE OR REPLACE PROCEDURE update_event()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'event';
+ column_id varchar := 'id';
+ column_entity_id varchar := 'entity_id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE event DROP CONSTRAINT event_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE event ADD COLUMN created_time BIGINT;
+ UPDATE event SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE event ADD CONSTRAINT event_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ ALTER TABLE event DROP CONSTRAINT event_unq_key;
+
+ data_type := get_column_type(table_name, column_entity_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_entity_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+
+ ALTER TABLE event ADD CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid);
+END;
+$$;
+
+
+-- relation
+CREATE OR REPLACE PROCEDURE update_relation()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'relation';
+ column_from_id varchar := 'from_id';
+ column_to_id varchar := 'to_id';
+BEGIN
+ ALTER TABLE relation DROP CONSTRAINT relation_pkey;
+
+ data_type := get_column_type(table_name, column_from_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_from_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_from_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_from_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_to_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_to_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_to_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_to_id;
+ END IF;
+
+ ALTER TABLE relation ADD CONSTRAINT relation_pkey PRIMARY KEY (from_id, from_type, relation_type_group, relation_type, to_id, to_type);
+END;
+$$;
+
+
+-- tb_user
+CREATE OR REPLACE PROCEDURE update_tb_user()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'tb_user';
+ column_id varchar := 'id';
+ column_customer_id varchar := 'customer_id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE tb_user DROP CONSTRAINT tb_user_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE tb_user ADD COLUMN created_time BIGINT;
+ UPDATE tb_user SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE tb_user ADD CONSTRAINT tb_user_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_customer_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_customer_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+
+-- tenant
+CREATE OR REPLACE PROCEDURE update_tenant()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'tenant';
+ column_id varchar := 'id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE tenant DROP CONSTRAINT tenant_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE tenant ADD COLUMN created_time BIGINT;
+ UPDATE tenant SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE tenant ADD CONSTRAINT tenant_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+END;
+$$;
+
+
+-- user_credentials
+CREATE OR REPLACE PROCEDURE update_user_credentials()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'user_credentials';
+ column_id varchar := 'id';
+ column_user_id varchar := 'user_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE user_credentials DROP CONSTRAINT user_credentials_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE user_credentials ADD COLUMN created_time BIGINT;
+ UPDATE user_credentials SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE user_credentials ADD CONSTRAINT user_credentials_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_user_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE user_credentials DROP CONSTRAINT user_credentials_user_id_key;
+ ALTER TABLE user_credentials RENAME COLUMN user_id TO old_user_id;
+ ALTER TABLE user_credentials ADD COLUMN user_id UUID UNIQUE;
+ UPDATE user_credentials SET user_id = to_uuid(old_user_id) WHERE old_user_id is not null;
+ ALTER TABLE user_credentials DROP COLUMN old_user_id;
+ RAISE NOTICE 'Table % column % updated!', table_name, column_user_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_user_id;
+ END IF;
+END;
+$$;
+
+
+-- widget_type
+CREATE OR REPLACE PROCEDURE update_widget_type()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'widget_type';
+ column_id varchar := 'id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE widget_type DROP CONSTRAINT widget_type_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE widget_type ADD COLUMN created_time BIGINT;
+ UPDATE widget_type SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE widget_type ADD CONSTRAINT widget_type_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+
+-- widgets_bundle
+CREATE OR REPLACE PROCEDURE update_widgets_bundle()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'widgets_bundle';
+ column_id varchar := 'id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE widgets_bundle DROP CONSTRAINT widgets_bundle_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE widgets_bundle ADD COLUMN created_time BIGINT;
+ UPDATE widgets_bundle SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+
+-- rule_chain
+CREATE OR REPLACE PROCEDURE update_rule_chain()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'rule_chain';
+ column_id varchar := 'id';
+ column_first_rule_node_id varchar := 'first_rule_node_id';
+ column_tenant_id varchar := 'tenant_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE rule_chain DROP CONSTRAINT rule_chain_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE rule_chain ADD COLUMN created_time BIGINT;
+ UPDATE rule_chain SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_first_rule_node_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_first_rule_node_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_first_rule_node_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_first_rule_node_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+END;
+$$;
+
+
+-- rule_node
+CREATE OR REPLACE PROCEDURE update_rule_node()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'rule_node';
+ column_id varchar := 'id';
+ column_rule_chain_id varchar := 'rule_chain_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE rule_node DROP CONSTRAINT rule_node_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE rule_node ADD COLUMN created_time BIGINT;
+ UPDATE rule_node SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE rule_node ADD CONSTRAINT rule_node_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_rule_chain_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_rule_chain_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_rule_chain_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_rule_chain_id;
+ END IF;
+END;
+$$;
+
+
+-- entity_view
+CREATE OR REPLACE PROCEDURE update_entity_view()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ data_type varchar;
+ table_name varchar := 'entity_view';
+ column_id varchar := 'id';
+ column_entity_id varchar := 'entity_id';
+ column_tenant_id varchar := 'tenant_id';
+ column_customer_id varchar := 'customer_id';
+BEGIN
+ data_type := get_column_type(table_name, column_id);
+ IF data_type = 'character varying' THEN
+ ALTER TABLE entity_view DROP CONSTRAINT entity_view_pkey;
+ PERFORM column_type_to_uuid(table_name, column_id);
+ ALTER TABLE entity_view ADD COLUMN created_time BIGINT;
+ UPDATE entity_view SET created_time = extract_ts(id) WHERE id is not null;
+ ALTER TABLE entity_view ADD CONSTRAINT entity_view_pkey PRIMARY KEY (id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_entity_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_entity_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_tenant_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_tenant_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id;
+ END IF;
+
+ data_type := get_column_type(table_name, column_customer_id);
+ IF data_type = 'character varying' THEN
+ PERFORM column_type_to_uuid(table_name, column_customer_id);
+ RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id;
+ ELSE
+ RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id;
+ END IF;
+END;
+$$;
+
+CREATE TABLE IF NOT EXISTS ts_kv_latest
+(
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
+);
+
+CREATE TABLE IF NOT EXISTS ts_kv_dictionary
+(
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+);
diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..a23150cab9300e0a9490166941154fe8b56c8761
--- /dev/null
+++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time ON alarm(tenant_id, type, created_time DESC);
diff --git a/application/src/main/data/upgrade/3.1.1/schema_update_after.sql b/application/src/main/data/upgrade/3.1.1/schema_update_after.sql
new file mode 100644
index 0000000000000000000000000000000000000000..75cb865b69c2928c0089a63de8093f93ed1f89c2
--- /dev/null
+++ b/application/src/main/data/upgrade/3.1.1/schema_update_after.sql
@@ -0,0 +1,28 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP PROCEDURE IF EXISTS update_tenant_profiles;
+DROP PROCEDURE IF EXISTS update_device_profiles;
+
+ALTER TABLE tenant ALTER COLUMN tenant_profile_id SET NOT NULL;
+ALTER TABLE tenant DROP CONSTRAINT IF EXISTS fk_tenant_profile;
+ALTER TABLE tenant ADD CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id);
+ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_core;
+ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_rule_engine;
+
+ALTER TABLE device ALTER COLUMN device_profile_id SET NOT NULL;
+ALTER TABLE device DROP CONSTRAINT IF EXISTS fk_device_profile;
+ALTER TABLE device ADD CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id);
diff --git a/application/src/main/data/upgrade/3.1.1/schema_update_before.sql b/application/src/main/data/upgrade/3.1.1/schema_update_before.sql
new file mode 100644
index 0000000000000000000000000000000000000000..0d74063bb1c5cbc7eee4cd23a398e9b3f5c5b698
--- /dev/null
+++ b/application/src/main/data/upgrade/3.1.1/schema_update_before.sql
@@ -0,0 +1,154 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS oauth2_client_registration_info (
+ id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY,
+ enabled boolean,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ client_id varchar(255),
+ client_secret varchar(255),
+ authorization_uri varchar(255),
+ token_uri varchar(255),
+ scope varchar(255),
+ user_info_uri varchar(255),
+ user_name_attribute_name varchar(255),
+ jwk_set_uri varchar(255),
+ client_authentication_method varchar(255),
+ login_button_label varchar(255),
+ login_button_icon varchar(255),
+ allow_user_creation boolean,
+ activate_user boolean,
+ type varchar(31),
+ basic_email_attribute_key varchar(31),
+ basic_first_name_attribute_key varchar(31),
+ basic_last_name_attribute_key varchar(31),
+ basic_tenant_name_strategy varchar(31),
+ basic_tenant_name_pattern varchar(255),
+ basic_customer_name_pattern varchar(255),
+ basic_default_dashboard_name varchar(255),
+ basic_always_full_screen boolean,
+ custom_url varchar(255),
+ custom_username varchar(255),
+ custom_password varchar(255),
+ custom_send_token boolean
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_client_registration (
+ id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ domain_name varchar(255),
+ domain_scheme varchar(31),
+ client_registration_info_id uuid
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_client_registration_template (
+ id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ provider_id varchar(255),
+ authorization_uri varchar(255),
+ token_uri varchar(255),
+ scope varchar(255),
+ user_info_uri varchar(255),
+ user_name_attribute_name varchar(255),
+ jwk_set_uri varchar(255),
+ client_authentication_method varchar(255),
+ type varchar(31),
+ basic_email_attribute_key varchar(31),
+ basic_first_name_attribute_key varchar(31),
+ basic_last_name_attribute_key varchar(31),
+ basic_tenant_name_strategy varchar(31),
+ basic_tenant_name_pattern varchar(255),
+ basic_customer_name_pattern varchar(255),
+ basic_default_dashboard_name varchar(255),
+ basic_always_full_screen boolean,
+ comment varchar,
+ login_button_icon varchar(255),
+ login_button_label varchar(255),
+ help_link varchar(255),
+ CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id)
+);
+
+CREATE TABLE IF NOT EXISTS device_profile (
+ id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ name varchar(255),
+ type varchar(255),
+ transport_type varchar(255),
+ provision_type varchar(255),
+ profile_data jsonb,
+ description varchar,
+ search_text varchar(255),
+ is_default boolean,
+ tenant_id uuid,
+ default_rule_chain_id uuid,
+ default_queue_name varchar(255),
+ provision_device_key varchar,
+ CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
+ CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id)
+);
+
+CREATE TABLE IF NOT EXISTS tenant_profile (
+ id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ name varchar(255),
+ profile_data jsonb,
+ description varchar,
+ search_text varchar(255),
+ is_default boolean,
+ isolated_tb_core boolean,
+ isolated_tb_rule_engine boolean,
+ CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
+);
+
+CREATE OR REPLACE PROCEDURE update_tenant_profiles()
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ UPDATE tenant as t SET tenant_profile_id = p.id
+ FROM
+ (SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = false) as p
+ WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = false;
+
+ UPDATE tenant as t SET tenant_profile_id = p.id
+ FROM
+ (SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = false) as p
+ WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = false;
+
+ UPDATE tenant as t SET tenant_profile_id = p.id
+ FROM
+ (SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = true) as p
+ WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = true;
+
+ UPDATE tenant as t SET tenant_profile_id = p.id
+ FROM
+ (SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = true) as p
+ WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = true;
+END;
+$$;
+
+CREATE OR REPLACE PROCEDURE update_device_profiles()
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ UPDATE device as d SET device_profile_id = p.id, device_data = '{"configuration":{"type":"DEFAULT"}, "transportConfiguration":{"type":"DEFAULT"}}'
+ FROM
+ (SELECT id, tenant_id, name from device_profile) as p
+ WHERE d.device_profile_id IS NULL AND p.tenant_id = d.tenant_id AND d.type = p.name;
+END;
+$$;
diff --git a/application/src/main/data/upgrade/3.2.1/schema_update.sql b/application/src/main/data/upgrade/3.2.1/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..04dc38a28da16979677fb5f066401e4e51fdac3c
--- /dev/null
+++ b/application/src/main/data/upgrade/3.2.1/schema_update.sql
@@ -0,0 +1,23 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE widget_type
+ ADD COLUMN IF NOT EXISTS image varchar (1000000),
+ ADD COLUMN IF NOT EXISTS description varchar (255);
+
+ALTER TABLE widgets_bundle
+ ADD COLUMN IF NOT EXISTS image varchar (1000000),
+ ADD COLUMN IF NOT EXISTS description varchar (255);
diff --git a/application/src/main/data/upgrade/3.2.1/schema_update_ttl.sql b/application/src/main/data/upgrade/3.2.1/schema_update_ttl.sql
new file mode 100644
index 0000000000000000000000000000000000000000..cecbf5aff17fefe269b86ae6f3e97b50ad6ddb7f
--- /dev/null
+++ b/application/src/main/data/upgrade/3.2.1/schema_update_ttl.sql
@@ -0,0 +1,87 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid,
+ IN system_ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+tenant_cursor CURSOR FOR select tenant.id as tenant_id
+ from tenant;
+ tenant_id_record uuid;
+ customer_id_record uuid;
+ tenant_ttl bigint;
+ customer_ttl bigint;
+ deleted_for_entities bigint;
+ tenant_ttl_ts bigint;
+ customer_ttl_ts bigint;
+BEGIN
+OPEN tenant_cursor;
+FETCH tenant_cursor INTO tenant_id_record;
+WHILE FOUND
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ tenant_id_record, 'TTL') INTO tenant_ttl;
+ if tenant_ttl IS NULL THEN
+ tenant_ttl := system_ttl;
+END IF;
+ IF tenant_ttl > 0 THEN
+ tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint;
+ deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record;
+END IF;
+FOR customer_id_record IN
+SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record
+ LOOP
+ EXECUTE format(
+ 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L',
+ customer_id_record, 'TTL') INTO customer_ttl;
+IF customer_ttl IS NULL THEN
+ customer_ttl_ts := tenant_ttl_ts;
+ELSE
+ IF customer_ttl > 0 THEN
+ customer_ttl_ts :=
+ (EXTRACT(EPOCH FROM current_timestamp) * 1000 -
+ customer_ttl::bigint * 1000)::bigint;
+END IF;
+END IF;
+ IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN
+ deleted_for_entities :=
+ delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record;
+ deleted_for_entities :=
+ delete_device_records_from_ts_kv(tenant_id_record, customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+ deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record,
+ customer_id_record,
+ customer_ttl_ts);
+ deleted := deleted + deleted_for_entities;
+ RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
+END IF;
+END LOOP;
+FETCH tenant_cursor INTO tenant_id_record;
+END LOOP;
+END
+$$;
diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..8685bf3ccf33469eb68effaeabe9e36ee4abd3a6
--- /dev/null
+++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql
@@ -0,0 +1,216 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS edge (
+ id uuid NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ customer_id uuid,
+ root_rule_chain_id uuid,
+ type varchar(255),
+ name varchar(255),
+ label varchar(255),
+ routing_key varchar(255),
+ secret varchar(255),
+ edge_license_key varchar(30),
+ cloud_endpoint varchar(255),
+ search_text varchar(255),
+ tenant_id uuid,
+ CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key)
+ );
+
+CREATE TABLE IF NOT EXISTS edge_event (
+ id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ edge_id uuid,
+ edge_event_type varchar(255),
+ edge_event_uid varchar(255),
+ entity_id uuid,
+ edge_event_action varchar(255),
+ body varchar(10000000),
+ tenant_id uuid,
+ ts bigint NOT NULL
+ );
+
+CREATE TABLE IF NOT EXISTS resource (
+ id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid NOT NULL,
+ title varchar(255) NOT NULL,
+ resource_type varchar(32) NOT NULL,
+ resource_key varchar(255) NOT NULL,
+ search_text varchar(255),
+ file_name varchar(255) NOT NULL,
+ data varchar,
+ CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)
+);
+
+CREATE TABLE IF NOT EXISTS ota_package (
+ id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid NOT NULL,
+ device_profile_id uuid,
+ type varchar(32) NOT NULL,
+ title varchar(255) NOT NULL,
+ version varchar(255) NOT NULL,
+ tag varchar(255),
+ url varchar(255),
+ file_name varchar(255),
+ content_type varchar(255),
+ checksum_algorithm varchar(32),
+ checksum varchar(1020),
+ data oid,
+ data_size bigint,
+ additional_info varchar,
+ search_text varchar(255),
+ CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_params (
+ id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY,
+ enabled boolean,
+ tenant_id uuid,
+ created_time bigint NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_registration (
+ id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY,
+ oauth2_params_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ additional_info varchar,
+ client_id varchar(255),
+ client_secret varchar(2048),
+ authorization_uri varchar(255),
+ token_uri varchar(255),
+ scope varchar(255),
+ platforms varchar(255),
+ user_info_uri varchar(255),
+ user_name_attribute_name varchar(255),
+ jwk_set_uri varchar(255),
+ client_authentication_method varchar(255),
+ login_button_label varchar(255),
+ login_button_icon varchar(255),
+ allow_user_creation boolean,
+ activate_user boolean,
+ type varchar(31),
+ basic_email_attribute_key varchar(31),
+ basic_first_name_attribute_key varchar(31),
+ basic_last_name_attribute_key varchar(31),
+ basic_tenant_name_strategy varchar(31),
+ basic_tenant_name_pattern varchar(255),
+ basic_customer_name_pattern varchar(255),
+ basic_default_dashboard_name varchar(255),
+ basic_always_full_screen boolean,
+ custom_url varchar(255),
+ custom_username varchar(255),
+ custom_password varchar(255),
+ custom_send_token boolean,
+ CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_domain (
+ id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY,
+ oauth2_params_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ domain_name varchar(255),
+ domain_scheme varchar(31),
+ CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
+ CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme)
+);
+
+CREATE TABLE IF NOT EXISTS oauth2_mobile (
+ id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY,
+ oauth2_params_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ pkg_name varchar(255),
+ app_secret varchar(2048),
+ CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
+ CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name)
+);
+
+ALTER TABLE dashboard
+ ADD COLUMN IF NOT EXISTS image varchar(1000000),
+ ADD COLUMN IF NOT EXISTS mobile_hide boolean DEFAULT false,
+ ADD COLUMN IF NOT EXISTS mobile_order int;
+
+ALTER TABLE device_profile
+ ADD COLUMN IF NOT EXISTS image varchar(1000000),
+ ADD COLUMN IF NOT EXISTS firmware_id uuid,
+ ADD COLUMN IF NOT EXISTS software_id uuid,
+ ADD COLUMN IF NOT EXISTS default_dashboard_id uuid;
+
+ALTER TABLE device
+ ADD COLUMN IF NOT EXISTS firmware_id uuid,
+ ADD COLUMN IF NOT EXISTS software_id uuid;
+
+ALTER TABLE alarm
+ ADD COLUMN IF NOT EXISTS customer_id uuid;
+
+DELETE FROM relation WHERE from_type = 'TENANT' AND relation_type_group = 'RULE_CHAIN';
+
+DO $$
+ BEGIN
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN
+ ALTER TABLE device_profile
+ ADD CONSTRAINT fk_firmware_device_profile
+ FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
+ END IF;
+
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN
+ ALTER TABLE device_profile
+ ADD CONSTRAINT fk_software_device_profile
+ FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
+ END IF;
+
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_dashboard_device_profile') THEN
+ ALTER TABLE device_profile
+ ADD CONSTRAINT fk_default_dashboard_device_profile
+ FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id);
+ END IF;
+
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
+ ALTER TABLE device
+ ADD CONSTRAINT fk_firmware_device
+ FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
+ END IF;
+
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN
+ ALTER TABLE device
+ ADD CONSTRAINT fk_software_device
+ FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
+ END IF;
+ END;
+$$;
+
+
+ALTER TABLE api_usage_state
+ ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32);
+UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL;
+
+CREATE TABLE IF NOT EXISTS rpc (
+ id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid NOT NULL,
+ device_id uuid NOT NULL,
+ expiration_time bigint NOT NULL,
+ request varchar(10000000) NOT NULL,
+ response varchar(10000000),
+ additional_info varchar(10000000),
+ status varchar(255) NOT NULL
+);
+
+CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id);
diff --git a/application/src/main/data/upgrade/3.2.2/schema_update_event.sql b/application/src/main/data/upgrade/3.2.2/schema_update_event.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9e2e6cfd337dc044df74be245693ff9abec9d6ff
--- /dev/null
+++ b/application/src/main/data/upgrade/3.2.2/schema_update_event.sql
@@ -0,0 +1,90 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+-- PROCEDURE: public.cleanup_events_by_ttl(bigint, bigint, bigint)
+
+DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint);
+
+CREATE OR REPLACE PROCEDURE public.cleanup_events_by_ttl(
+ ttl bigint,
+ debug_ttl bigint,
+ INOUT deleted bigint)
+LANGUAGE 'plpgsql'
+AS $BODY$
+DECLARE
+ ttl_ts bigint;
+ debug_ttl_ts bigint;
+ ttl_deleted_count bigint DEFAULT 0;
+ debug_ttl_deleted_count bigint DEFAULT 0;
+BEGIN
+ IF ttl > 0 THEN
+ ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
+
+ DELETE FROM event
+ WHERE ts < ttl_ts
+ AND NOT event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION');
+
+ GET DIAGNOSTICS ttl_deleted_count = ROW_COUNT;
+ END IF;
+
+ IF debug_ttl > 0 THEN
+ debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint;
+
+ DELETE FROM event
+ WHERE ts < debug_ttl_ts
+ AND event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION');
+
+ GET DIAGNOSTICS debug_ttl_deleted_count = ROW_COUNT;
+ END IF;
+
+ RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;
+ RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;
+ deleted := ttl_deleted_count + debug_ttl_deleted_count;
+END
+$BODY$;
+
+
+-- Index: idx_event_ts
+
+DROP INDEX IF EXISTS public.idx_event_ts;
+
+-- Hint: add CONCURRENTLY to CREATE INDEX query in case of more then 1 million records or during live update
+-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_ts
+CREATE INDEX IF NOT EXISTS idx_event_ts
+ ON public.event
+ (ts DESC NULLS LAST)
+ WITH (FILLFACTOR=95);
+
+COMMENT ON INDEX public.idx_event_ts
+ IS 'This index helps to delete events by TTL using timestamp';
+
+
+-- Index: idx_event_tenant_entity_type_entity_event_type_created_time_des
+
+DROP INDEX IF EXISTS public.idx_event_tenant_entity_type_entity_event_type_created_time_des;
+
+-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des
+CREATE INDEX IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des
+ ON public.event
+ (tenant_id ASC, entity_type ASC, entity_id ASC, event_type ASC, created_time DESC NULLS LAST)
+ WITH (FILLFACTOR=95);
+
+COMMENT ON INDEX public.idx_event_tenant_entity_type_entity_event_type_created_time_des
+ IS 'This index helps to open latest events on UI fast';
+
+-- Index: idx_event_type_entity_id
+-- Description: replaced with more suitable idx_event_tenant_entity_type_entity_event_type_created_time_des
+DROP INDEX IF EXISTS public.idx_event_type_entity_id;
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/3.2.2/schema_update_ttl.sql b/application/src/main/data/upgrade/3.2.2/schema_update_ttl.sql
new file mode 100644
index 0000000000000000000000000000000000000000..42a7e8b277928f584a20a72b45b16d787ce31b8d
--- /dev/null
+++ b/application/src/main/data/upgrade/3.2.2/schema_update_ttl.sql
@@ -0,0 +1,32 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ ttl_ts bigint;
+ ttl_deleted_count bigint DEFAULT 0;
+BEGIN
+ IF ttl > 0 THEN
+ ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count;
+ END IF;
+ RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count;
+ deleted := ttl_deleted_count;
+END
+$$;
diff --git a/application/src/main/data/upgrade/3.3.2/schema_update.sql b/application/src/main/data/upgrade/3.3.2/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..5501c359cb6070da801577980a52e87cc8099461
--- /dev/null
+++ b/application/src/main/data/upgrade/3.3.2/schema_update.sql
@@ -0,0 +1,71 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS entity_alarm (
+ tenant_id uuid NOT NULL,
+ entity_type varchar(32),
+ entity_id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ alarm_type varchar(255) NOT NULL,
+ customer_id uuid,
+ alarm_id uuid,
+ CONSTRAINT entity_alarm_pkey PRIMARY KEY (entity_id, alarm_id),
+ CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE
+);
+
+CREATE INDEX IF NOT EXISTS idx_alarm_tenant_status_created_time ON alarm(tenant_id, status, created_time DESC);
+CREATE INDEX IF NOT EXISTS idx_entity_alarm_created_time ON entity_alarm(tenant_id, entity_id, created_time DESC);
+CREATE INDEX IF NOT EXISTS idx_entity_alarm_alarm_id ON entity_alarm(alarm_id);
+
+INSERT INTO entity_alarm(tenant_id, entity_type, entity_id, created_time, alarm_type, customer_id, alarm_id)
+SELECT tenant_id,
+ CASE
+ WHEN originator_type = 0 THEN 'TENANT'
+ WHEN originator_type = 1 THEN 'CUSTOMER'
+ WHEN originator_type = 2 THEN 'USER'
+ WHEN originator_type = 3 THEN 'DASHBOARD'
+ WHEN originator_type = 4 THEN 'ASSET'
+ WHEN originator_type = 5 THEN 'DEVICE'
+ WHEN originator_type = 6 THEN 'ALARM'
+ WHEN originator_type = 7 THEN 'RULE_CHAIN'
+ WHEN originator_type = 8 THEN 'RULE_NODE'
+ WHEN originator_type = 9 THEN 'ENTITY_VIEW'
+ WHEN originator_type = 10 THEN 'WIDGETS_BUNDLE'
+ WHEN originator_type = 11 THEN 'WIDGET_TYPE'
+ WHEN originator_type = 12 THEN 'TENANT_PROFILE'
+ WHEN originator_type = 13 THEN 'DEVICE_PROFILE'
+ WHEN originator_type = 14 THEN 'API_USAGE_STATE'
+ WHEN originator_type = 15 THEN 'TB_RESOURCE'
+ WHEN originator_type = 16 THEN 'OTA_PACKAGE'
+ WHEN originator_type = 17 THEN 'EDGE'
+ WHEN originator_type = 18 THEN 'RPC'
+ else 'UNKNOWN'
+ END,
+ originator_id,
+ created_time,
+ type,
+ customer_id,
+ id
+FROM alarm
+ON CONFLICT DO NOTHING;
+
+INSERT INTO entity_alarm(tenant_id, entity_type, entity_id, created_time, alarm_type, customer_id, alarm_id)
+SELECT a.tenant_id, r.from_type, r.from_id, created_time, type, customer_id, id
+FROM alarm a
+ INNER JOIN relation r ON r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id
+ON CONFLICT DO NOTHING;
+
+DELETE FROM relation r WHERE r.relation_type_group = 'ALARM';
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql b/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql
new file mode 100644
index 0000000000000000000000000000000000000000..dec4f269f3f58f21e42fa4d60e57de7cff8cda3b
--- /dev/null
+++ b/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql
@@ -0,0 +1,213 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+
+CREATE OR REPLACE PROCEDURE update_profile_bootstrap()
+ LANGUAGE plpgsql AS
+$$
+
+BEGIN
+
+ UPDATE device_profile
+ SET profile_data = jsonb_set(
+ profile_data,
+ '{transportConfiguration}',
+ get_bootstrap(
+ profile_data::jsonb #> '{transportConfiguration}',
+ subquery.publickey_bs,
+ subquery.publickey_lw,
+ profile_data::json #>> '{transportConfiguration, bootstrap, bootstrapServer, securityMode}',
+ profile_data::json #>> '{transportConfiguration, bootstrap, lwm2mServer, securityMode}'),
+ true)
+ FROM (
+ SELECT id,
+ encode(
+ decode(profile_data::json #> '{transportConfiguration,bootstrap,bootstrapServer}' ->>
+ 'serverPublicKey', 'hex')::bytea, 'base64') AS publickey_bs,
+ encode(
+ decode(profile_data::json #> '{transportConfiguration,bootstrap,lwm2mServer}' ->>
+ 'serverPublicKey', 'hex')::bytea, 'base64') AS publickey_lw
+ FROM device_profile
+ WHERE transport_type = 'LWM2M'
+ ) AS subquery
+ WHERE device_profile.id = subquery.id
+ AND subquery.publickey_bs IS NOT NULL
+ AND subquery.publickey_lw IS NOT NULL;
+
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION get_bootstrap(transport_configuration_in jsonb, publickey_bs text,
+ publickey_lw text, security_mode_bs text,
+ security_mode_lw text) RETURNS jsonb AS
+$$
+
+DECLARE
+ bootstrap_new jsonb;
+ bootstrap_in jsonb;
+
+BEGIN
+
+ IF security_mode_lw IS NULL THEN
+ security_mode_lw := 'NO_SEC';
+ END IF;
+
+ IF security_mode_bs IS NULL THEN
+ security_mode_bs := 'NO_SEC';
+ END IF;
+
+ bootstrap_in := transport_configuration_in::jsonb #> '{bootstrap}';
+ bootstrap_new := json_build_array(
+ json_build_object('shortServerId', bootstrap_in::json #> '{bootstrapServer}' -> 'serverId',
+ 'securityMode', security_mode_bs,
+ 'binding', bootstrap_in::json #> '{servers}' ->> 'binding',
+ 'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime',
+ 'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled',
+ 'defaultMinPeriod', bootstrap_in::json #> '{servers}' -> 'defaultMinPeriod',
+ 'host', bootstrap_in::json #> '{bootstrapServer}' ->> 'host',
+ 'port', bootstrap_in::json #> '{bootstrapServer}' -> 'port',
+ 'serverPublicKey', publickey_bs,
+ 'bootstrapServerIs', true,
+ 'clientHoldOffTime', bootstrap_in::json #> '{bootstrapServer}' -> 'clientHoldOffTime',
+ 'bootstrapServerAccountTimeout',
+ bootstrap_in::json #> '{bootstrapServer}' -> 'bootstrapServerAccountTimeout'
+ ),
+ json_build_object('shortServerId', bootstrap_in::json #> '{lwm2mServer}' -> 'serverId',
+ 'securityMode', security_mode_lw,
+ 'binding', bootstrap_in::json #> '{servers}' ->> 'binding',
+ 'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime',
+ 'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled',
+ 'defaultMinPeriod', bootstrap_in::json #> '{servers}' -> 'defaultMinPeriod',
+ 'host', bootstrap_in::json #> '{lwm2mServer}' ->> 'host',
+ 'port', bootstrap_in::json #> '{lwm2mServer}' -> 'port',
+ 'serverPublicKey', publickey_lw,
+ 'bootstrapServerIs', false,
+ 'clientHoldOffTime', bootstrap_in::json #> '{lwm2mServer}' -> 'clientHoldOffTime',
+ 'bootstrapServerAccountTimeout',
+ bootstrap_in::json #> '{lwm2mServer}' -> 'bootstrapServerAccountTimeout'
+ )
+ );
+ RETURN jsonb_set(
+ transport_configuration_in,
+ '{bootstrap}',
+ bootstrap_new,
+ true) || '{"bootstrapServerUpdateEnable": true}';
+
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE PROCEDURE update_device_credentials_to_base64_and_bootstrap()
+ LANGUAGE plpgsql AS
+$$
+
+BEGIN
+
+ UPDATE device_credentials
+ SET credentials_value = get_device_and_bootstrap(credentials_value::text)
+ WHERE credentials_type = 'LWM2M_CREDENTIALS';
+END;
+$$;
+
+CREATE OR REPLACE FUNCTION get_device_and_bootstrap(IN credentials_value text, OUT credentials_value_new text)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ client_secret_key text;
+ client_public_key_or_id text;
+ client_key_value_object jsonb;
+ client_bootstrap_server_value_object jsonb;
+ client_bootstrap_server_object jsonb;
+ client_bootstrap_object jsonb;
+
+BEGIN
+ credentials_value_new := credentials_value;
+ IF credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode' = 'RPK' AND
+ NULLIF((credentials_value::jsonb #> '{client}' ->> 'key' ~ '^[0-9a-fA-F]+$')::text, 'false') = 'true' THEN
+ client_public_key_or_id := encode(decode(credentials_value::jsonb #> '{client}' ->> 'key', 'hex')::bytea, 'base64');
+ client_key_value_object := json_build_object(
+ 'endpoint', credentials_value::jsonb #> '{client}' ->> 'endpoint',
+ 'securityConfigClientMode', credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode',
+ 'key', client_public_key_or_id);
+ credentials_value_new :=
+ credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb;
+ END IF;
+ IF credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode' = 'X509' AND
+ NULLIF((credentials_value::jsonb #> '{client}' ->> 'cert' ~ '^[0-9a-fA-F]+$')::text, 'false') = 'true' THEN
+ client_public_key_or_id :=
+ encode(decode(credentials_value::jsonb #> '{client}' ->> 'cert', 'hex')::bytea, 'base64');
+ client_key_value_object := json_build_object(
+ 'endpoint', credentials_value::jsonb #> '{client}' ->> 'endpoint',
+ 'securityConfigClientMode', credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode',
+ 'cert', client_public_key_or_id);
+ credentials_value_new :=
+ credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb;
+ END IF;
+
+ IF credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'RPK' OR
+ credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'X509' THEN
+ IF NULLIF((credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientSecretKey' ~ '^[0-9a-fA-F]+$')::text,
+ 'false') = 'true' AND
+ NULLIF(
+ (credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientPublicKeyOrId' ~ '^[0-9a-fA-F]+$')::text,
+ 'false') = 'true' THEN
+ client_secret_key :=
+ encode(decode(credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientSecretKey', 'hex')::bytea,
+ 'base64');
+ client_public_key_or_id := encode(
+ decode(credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientPublicKeyOrId', 'hex')::bytea,
+ 'base64');
+ client_bootstrap_server_value_object := jsonb_build_object(
+ 'securityMode', credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode',
+ 'clientPublicKeyOrId', client_public_key_or_id,
+ 'clientSecretKey', client_secret_key
+ );
+ client_bootstrap_server_object := jsonb_build_object('lwm2mServer', client_bootstrap_server_value_object::jsonb);
+ client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb;
+ credentials_value_new :=
+ jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb;
+ END IF;
+ END IF;
+
+ IF credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'RPK' OR
+ credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'X509' THEN
+ IF NULLIF(
+ (credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientSecretKey' ~ '^[0-9a-fA-F]+$')::text,
+ 'false') = 'true' AND
+ NULLIF(
+ (credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientPublicKeyOrId' ~ '^[0-9a-fA-F]+$')::text,
+ 'false') = 'true' THEN
+ client_secret_key :=
+ encode(
+ decode(credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientSecretKey', 'hex')::bytea,
+ 'base64');
+ client_public_key_or_id := encode(
+ decode(credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientPublicKeyOrId', 'hex')::bytea,
+ 'base64');
+ client_bootstrap_server_value_object := jsonb_build_object(
+ 'securityMode', credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode',
+ 'clientPublicKeyOrId', client_public_key_or_id,
+ 'clientSecretKey', client_secret_key
+ );
+ client_bootstrap_server_object :=
+ jsonb_build_object('bootstrapServer', client_bootstrap_server_value_object::jsonb);
+ client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb;
+ credentials_value_new :=
+ jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb;
+ END IF;
+ END IF;
+
+END;
+$$;
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/3.3.3/schema_event_ttl_procedure.sql b/application/src/main/data/upgrade/3.3.3/schema_event_ttl_procedure.sql
new file mode 100644
index 0000000000000000000000000000000000000000..432fca87861bce834ce1d3c48c73c25bf71d80c3
--- /dev/null
+++ b/application/src/main/data/upgrade/3.3.3/schema_event_ttl_procedure.sql
@@ -0,0 +1,50 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+
+DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint);
+
+CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(
+ IN regular_events_start_ts bigint,
+ IN regular_events_end_ts bigint,
+ IN debug_events_start_ts bigint,
+ IN debug_events_end_ts bigint,
+ INOUT deleted bigint)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ ttl_deleted_count bigint DEFAULT 0;
+ debug_ttl_deleted_count bigint DEFAULT 0;
+BEGIN
+ IF regular_events_start_ts > 0 AND regular_events_end_ts > 0 THEN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM event WHERE id in (SELECT id from event WHERE ts > %L::bigint AND ts < %L::bigint AND ' ||
+ '(event_type != %L::varchar AND event_type != %L::varchar)) RETURNING *) ' ||
+ 'SELECT count(*) FROM deleted', regular_events_start_ts, regular_events_end_ts,
+ 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count;
+ END IF;
+ IF debug_events_start_ts > 0 AND debug_events_end_ts > 0 THEN
+ EXECUTE format(
+ 'WITH deleted AS (DELETE FROM event WHERE id in (SELECT id from event WHERE ts > %L::bigint AND ts < %L::bigint AND ' ||
+ '(event_type = %L::varchar OR event_type = %L::varchar)) RETURNING *) ' ||
+ 'SELECT count(*) FROM deleted', debug_events_start_ts, debug_events_end_ts,
+ 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count;
+ END IF;
+ RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;
+ RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;
+ deleted := ttl_deleted_count + debug_ttl_deleted_count;
+END
+$$;
diff --git a/application/src/main/data/upgrade/3.3.3/schema_update.sql b/application/src/main/data/upgrade/3.3.3/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..eed1cab1ea2a0c8d3db46dc0cc539e0ee36c9239
--- /dev/null
+++ b/application/src/main/data/upgrade/3.3.3/schema_update.sql
@@ -0,0 +1,29 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DELETE from ota_package as op WHERE NOT EXISTS(SELECT * FROM device_profile dp where op.device_profile_id = dp.id);
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_device_profile_ota_package') THEN
+ ALTER TABLE ota_package
+ ADD CONSTRAINT fk_device_profile_ota_package
+ FOREIGN KEY (device_profile_id) REFERENCES device_profile (id)
+ ON DELETE CASCADE;
+ END IF;
+ END;
+$$;
diff --git a/application/src/main/data/upgrade/3.3.4/schema_update.sql b/application/src/main/data/upgrade/3.3.4/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..c86a4fe1f41025f560989018f01f465fdcad73e8
--- /dev/null
+++ b/application/src/main/data/upgrade/3.3.4/schema_update.sql
@@ -0,0 +1,140 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE device
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE device_profile
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE asset
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE rule_chain
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE rule_node
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE dashboard
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE customer
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE widgets_bundle
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+ALTER TABLE entity_view
+ ADD COLUMN IF NOT EXISTS external_id UUID;
+
+CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id);
+CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type);
+
+ALTER TABLE admin_settings
+ ADD COLUMN IF NOT EXISTS tenant_id uuid NOT NULL DEFAULT '13814000-1dd2-11b2-8080-808080808080';
+
+CREATE TABLE IF NOT EXISTS queue (
+ id uuid NOT NULL CONSTRAINT queue_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid,
+ name varchar(255),
+ topic varchar(255),
+ poll_interval int,
+ partitions int,
+ consumer_per_partition boolean,
+ pack_processing_timeout bigint,
+ submit_strategy varchar(255),
+ processing_strategy varchar(255),
+ additional_info varchar
+);
+
+CREATE TABLE IF NOT EXISTS user_auth_settings (
+ id uuid NOT NULL CONSTRAINT user_auth_settings_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ user_id uuid UNIQUE NOT NULL CONSTRAINT fk_user_auth_settings_user_id REFERENCES tb_user(id),
+ two_fa_settings varchar
+);
+
+CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);
+
+ALTER TABLE tenant_profile DROP COLUMN IF EXISTS isolated_tb_core;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_external_id_unq_key') THEN
+ ALTER TABLE device ADD CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_profile_external_id_unq_key') THEN
+ ALTER TABLE device_profile ADD CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'asset_external_id_unq_key') THEN
+ ALTER TABLE asset ADD CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'rule_chain_external_id_unq_key') THEN
+ ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'dashboard_external_id_unq_key') THEN
+ ALTER TABLE dashboard ADD CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'customer_external_id_unq_key') THEN
+ ALTER TABLE customer ADD CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widgets_bundle_external_id_unq_key') THEN
+ ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'entity_view_external_id_unq_key') THEN
+ ALTER TABLE entity_view ADD CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
diff --git a/application/src/main/data/upgrade/3.4.0/schema_update.sql b/application/src/main/data/upgrade/3.4.0/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..71588d88aca2739e0e32dda6509544a4407002d5
--- /dev/null
+++ b/application/src/main/data/upgrade/3.4.0/schema_update.sql
@@ -0,0 +1,234 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS rule_node_debug_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL ,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar,
+ e_type varchar,
+ e_entity_id uuid,
+ e_entity_type varchar,
+ e_msg_id uuid,
+ e_msg_type varchar,
+ e_data_type varchar,
+ e_relation_type varchar,
+ e_data varchar,
+ e_metadata varchar,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS rule_chain_debug_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_message varchar,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS stats_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_messages_processed bigint NOT NULL,
+ e_errors_occurred bigint NOT NULL
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS lc_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_type varchar NOT NULL,
+ e_success boolean NOT NULL,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS error_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_method varchar NOT NULL,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE INDEX IF NOT EXISTS idx_rule_node_debug_event_main
+ ON rule_node_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_rule_chain_debug_event_main
+ ON rule_chain_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_stats_event_main
+ ON stats_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_lc_event_main
+ ON lc_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_error_event_main
+ ON error_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE OR REPLACE FUNCTION to_safe_json(p_json text) RETURNS json
+LANGUAGE plpgsql AS
+$$
+BEGIN
+ return REPLACE(p_json, '\u0000', '' )::json;
+EXCEPTION
+ WHEN OTHERS THEN
+ return '{}'::json;
+END;
+$$;
+
+-- Useful to migrate old events to the new table structure;
+CREATE OR REPLACE PROCEDURE migrate_regular_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ partition_size_in_ms bigint;
+ p record;
+ table_name varchar;
+BEGIN
+ partition_size_in_ms = partition_size_in_hours * 3600 * 1000;
+
+ FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('STATS', 'LC_EVENT', 'ERROR') and ts >= start_ts_in_ms and ts < end_ts_in_ms
+ LOOP
+ IF p.event_type = 'STATS' THEN
+ table_name := 'stats_event';
+ ELSEIF p.event_type = 'LC_EVENT' THEN
+ table_name := 'lc_event';
+ ELSEIF p.event_type = 'ERROR' THEN
+ table_name := 'error_event';
+ END IF;
+ RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms);
+ EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms));
+ END LOOP;
+
+ INSERT INTO stats_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ (body ->> 'messagesProcessed')::bigint,
+ (body ->> 'errorsOccurred')::bigint
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'STATS' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+ INSERT INTO lc_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'event',
+ (body ->> 'success')::boolean,
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'LC_EVENT' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+ INSERT INTO error_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'method',
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'ERROR' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+END
+$$;
+
+-- Useful to migrate old debug events to the new table structure;
+CREATE OR REPLACE PROCEDURE migrate_debug_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ partition_size_in_ms bigint;
+ p record;
+ table_name varchar;
+BEGIN
+ partition_size_in_ms = partition_size_in_hours * 3600 * 1000;
+
+ FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') and ts >= start_ts_in_ms and ts < end_ts_in_ms
+ LOOP
+ IF p.event_type = 'DEBUG_RULE_NODE' THEN
+ table_name := 'rule_node_debug_event';
+ ELSEIF p.event_type = 'DEBUG_RULE_CHAIN' THEN
+ table_name := 'rule_chain_debug_event';
+ END IF;
+ RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms);
+ EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms));
+ END LOOP;
+
+ INSERT INTO rule_node_debug_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'type',
+ (body ->> 'entityId')::uuid,
+ body ->> 'entityName',
+ (body ->> 'msgId')::uuid,
+ body ->> 'msgType',
+ body ->> 'dataType',
+ body ->> 'relationType',
+ body ->> 'data',
+ body ->> 'metadata',
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_NODE' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+ INSERT INTO rule_chain_debug_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'message',
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_CHAIN' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+END
+$$;
+
+UPDATE tb_user
+ SET additional_info = REPLACE(additional_info, '"lang":"ja_JA"', '"lang":"ja_JP"')
+ WHERE additional_info LIKE '%"lang":"ja_JA"%';
diff --git a/application/src/main/data/upgrade/3.4.1/schema_update.sql b/application/src/main/data/upgrade/3.4.1/schema_update.sql
new file mode 100644
index 0000000000000000000000000000000000000000..45a75c6bd9ddab08e9b9565465ecd0313be31be9
--- /dev/null
+++ b/application/src/main/data/upgrade/3.4.1/schema_update.sql
@@ -0,0 +1,142 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+-- AUDIT LOGS MIGRATION START
+DO
+$$
+ DECLARE table_partition RECORD;
+ BEGIN
+ -- in case of running the upgrade script a second time:
+ IF NOT (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_audit_log')) THEN
+ ALTER TABLE audit_log RENAME TO old_audit_log;
+ CREATE INDEX IF NOT EXISTS idx_old_audit_log_created_time ON old_audit_log(created_time);
+
+ ALTER INDEX IF EXISTS idx_audit_log_tenant_id_and_created_time RENAME TO idx_old_audit_log_tenant_id_and_created_time;
+
+ FOR table_partition IN SELECT tablename AS name, split_part(tablename, '_', 3) AS partition_ts
+ FROM pg_tables WHERE tablename LIKE 'audit_log_%'
+ LOOP
+ EXECUTE format('ALTER TABLE %s RENAME TO old_audit_log_%s', table_partition.name, table_partition.partition_ts);
+ END LOOP;
+ ELSE
+ RAISE NOTICE 'Table old_audit_log already exists, leaving as is';
+ END IF;
+ END;
+$$;
+
+CREATE TABLE IF NOT EXISTS audit_log (
+ id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ tenant_id uuid,
+ customer_id uuid,
+ entity_id uuid,
+ entity_type varchar(255),
+ entity_name varchar(255),
+ user_id uuid,
+ user_name varchar(255),
+ action_type varchar(255),
+ action_data varchar(1000000),
+ action_status varchar(255),
+ action_failure_details varchar(1000000)
+) PARTITION BY RANGE (created_time);
+CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time DESC);
+CREATE INDEX IF NOT EXISTS idx_audit_log_id ON audit_log(id);
+
+CREATE OR REPLACE PROCEDURE migrate_audit_logs(IN start_time_ms BIGINT, IN end_time_ms BIGINT, IN partition_size_ms BIGINT)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ p RECORD;
+ partition_end_ts BIGINT;
+BEGIN
+ FOR p IN SELECT DISTINCT (created_time - created_time % partition_size_ms) AS partition_ts FROM old_audit_log
+ WHERE created_time >= start_time_ms AND created_time < end_time_ms
+ LOOP
+ partition_end_ts = p.partition_ts + partition_size_ms;
+ RAISE NOTICE '[audit_log] Partition to create : [%-%]', p.partition_ts, partition_end_ts;
+ EXECUTE format('CREATE TABLE IF NOT EXISTS audit_log_%s PARTITION OF audit_log ' ||
+ 'FOR VALUES FROM ( %s ) TO ( %s )', p.partition_ts, p.partition_ts, partition_end_ts);
+ END LOOP;
+
+ INSERT INTO audit_log
+ SELECT id, created_time, tenant_id, customer_id, entity_id, entity_type, entity_name, user_id, user_name, action_type, action_data, action_status, action_failure_details
+ FROM old_audit_log
+ WHERE created_time >= start_time_ms AND created_time < end_time_ms;
+END;
+$$;
+-- AUDIT LOGS MIGRATION END
+
+
+-- EDGE EVENTS MIGRATION START
+DO
+$$
+ DECLARE table_partition RECORD;
+ BEGIN
+ -- in case of running the upgrade script a second time:
+ IF NOT (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_edge_event')) THEN
+ ALTER TABLE edge_event RENAME TO old_edge_event;
+ CREATE INDEX IF NOT EXISTS idx_old_blob_entity_created_time_tmp ON old_blob_entity(created_time);
+ ALTER INDEX IF EXISTS idx_edge_event_tenant_id_and_created_time RENAME TO idx_old_edge_event_tenant_id_and_created_time;
+
+ FOR table_partition IN SELECT tablename AS name, split_part(tablename, '_', 3) AS partition_ts
+ FROM pg_tables WHERE tablename LIKE 'edge_event_%'
+ LOOP
+ EXECUTE format('ALTER TABLE %s RENAME TO old_edge_event_%s', table_partition.name, table_partition.partition_ts);
+ END LOOP;
+ ELSE
+ RAISE NOTICE 'Table old_edge_event already exists, leaving as is';
+ END IF;
+END;
+$$;
+
+CREATE TABLE IF NOT EXISTS edge_event (
+ id uuid NOT NULL,
+ created_time bigint NOT NULL,
+ edge_id uuid,
+ edge_event_type varchar(255),
+ edge_event_uid varchar(255),
+ entity_id uuid,
+ edge_event_action varchar(255),
+ body varchar(10000000),
+ tenant_id uuid,
+ ts bigint NOT NULL
+ ) PARTITION BY RANGE (created_time);
+CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_and_created_time ON edge_event(tenant_id, created_time DESC);
+CREATE INDEX IF NOT EXISTS idx_edge_event_id ON edge_event(id);
+
+CREATE OR REPLACE PROCEDURE migrate_edge_event(IN start_time_ms BIGINT, IN end_time_ms BIGINT, IN partition_size_ms BIGINT)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ p RECORD;
+ partition_end_ts BIGINT;
+BEGIN
+ FOR p IN SELECT DISTINCT (created_time - created_time % partition_size_ms) AS partition_ts FROM old_edge_event
+ WHERE created_time >= start_time_ms AND created_time < end_time_ms
+ LOOP
+ partition_end_ts = p.partition_ts + partition_size_ms;
+ RAISE NOTICE '[edge_event] Partition to create : [%-%]', p.partition_ts, partition_end_ts;
+ EXECUTE format('CREATE TABLE IF NOT EXISTS edge_event_%s PARTITION OF edge_event ' ||
+ 'FOR VALUES FROM ( %s ) TO ( %s )', p.partition_ts, p.partition_ts, partition_end_ts);
+ END LOOP;
+
+ INSERT INTO edge_event
+ SELECT id, created_time, edge_id, edge_event_type, edge_event_uid, entity_id, edge_event_action, body, tenant_id, ts
+ FROM old_edge_event
+ WHERE created_time >= start_time_ms AND created_time < end_time_ms;
+END;
+$$;
+-- EDGE EVENTS MIGRATION END
diff --git a/application/src/main/data/upgrade/3.4.1/schema_update_after.sql b/application/src/main/data/upgrade/3.4.1/schema_update_after.sql
new file mode 100644
index 0000000000000000000000000000000000000000..78df4d2fd21e5f9aecde63ea6ce59f6b311853b9
--- /dev/null
+++ b/application/src/main/data/upgrade/3.4.1/schema_update_after.sql
@@ -0,0 +1,21 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP PROCEDURE IF EXISTS update_asset_profiles;
+
+ALTER TABLE asset ALTER COLUMN asset_profile_id SET NOT NULL;
+ALTER TABLE asset DROP CONSTRAINT IF EXISTS fk_asset_profile;
+ALTER TABLE asset ADD CONSTRAINT fk_asset_profile FOREIGN KEY (asset_profile_id) REFERENCES asset_profile(id);
diff --git a/application/src/main/data/upgrade/3.4.1/schema_update_before.sql b/application/src/main/data/upgrade/3.4.1/schema_update_before.sql
new file mode 100644
index 0000000000000000000000000000000000000000..27f772aba35f80b380e6dd84de747f78f9d1ce55
--- /dev/null
+++ b/application/src/main/data/upgrade/3.4.1/schema_update_before.sql
@@ -0,0 +1,46 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS asset_profile (
+ id uuid NOT NULL CONSTRAINT asset_profile_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ name varchar(255),
+ image varchar(1000000),
+ description varchar,
+ search_text varchar(255),
+ is_default boolean,
+ tenant_id uuid,
+ default_rule_chain_id uuid,
+ default_dashboard_id uuid,
+ default_queue_name varchar(255),
+ external_id uuid,
+ CONSTRAINT asset_profile_name_unq_key UNIQUE (tenant_id, name),
+ CONSTRAINT asset_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
+ CONSTRAINT fk_default_rule_chain_asset_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
+ CONSTRAINT fk_default_dashboard_asset_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id)
+ );
+
+CREATE OR REPLACE PROCEDURE update_asset_profiles()
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ UPDATE asset a SET asset_profile_id = COALESCE(
+ (SELECT id from asset_profile p WHERE p.tenant_id = a.tenant_id AND a.type = p.name),
+ (SELECT id from asset_profile p WHERE p.tenant_id = a.tenant_id AND p.name = 'default')
+ )
+ WHERE a.asset_profile_id IS NULL;
+END;
+$$;
diff --git a/application/src/main/java/org/apache/kafka/common/network/NetworkReceive.java b/application/src/main/java/org/apache/kafka/common/network/NetworkReceive.java
new file mode 100644
index 0000000000000000000000000000000000000000..38764d08de4733802d1fb9a4f61997a642508238
--- /dev/null
+++ b/application/src/main/java/org/apache/kafka/common/network/NetworkReceive.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Content of this file was modified to addresses the issue https://issues.apache.org/jira/browse/KAFKA-4090
+ *
+ */
+package org.apache.kafka.common.network;
+
+import org.apache.kafka.common.memory.MemoryPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.thingsboard.server.common.data.exception.ThingsboardKafkaClientError;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ScatteringByteChannel;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+/**
+ * A size delimited Receive that consists of a 4 byte network-ordered size N followed by N bytes of content
+ */
+public class NetworkReceive implements Receive {
+
+ public final static String UNKNOWN_SOURCE = "";
+ public final static int UNLIMITED = -1;
+ public final static int TB_MAX_REQUESTED_BUFFER_SIZE = 100 * 1024 * 1024;
+ public final static int TB_LOG_REQUESTED_BUFFER_SIZE = 10 * 1024 * 1024;
+ private static final Logger log = LoggerFactory.getLogger(NetworkReceive.class);
+ private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
+
+ private final String source;
+ private final ByteBuffer size;
+ private final int maxSize;
+ private final MemoryPool memoryPool;
+ private int requestedBufferSize = -1;
+ private ByteBuffer buffer;
+
+
+ public NetworkReceive(String source, ByteBuffer buffer) {
+ this.source = source;
+ this.buffer = buffer;
+ this.size = null;
+ this.maxSize = TB_MAX_REQUESTED_BUFFER_SIZE;
+ this.memoryPool = MemoryPool.NONE;
+ }
+
+ public NetworkReceive(String source) {
+ this.source = source;
+ this.size = ByteBuffer.allocate(4);
+ this.buffer = null;
+ this.maxSize = TB_MAX_REQUESTED_BUFFER_SIZE;
+ this.memoryPool = MemoryPool.NONE;
+ }
+
+ public NetworkReceive(int maxSize, String source) {
+ this.source = source;
+ this.size = ByteBuffer.allocate(4);
+ this.buffer = null;
+ this.maxSize = getMaxSize(maxSize);
+ this.memoryPool = MemoryPool.NONE;
+ }
+
+ public NetworkReceive(int maxSize, String source, MemoryPool memoryPool) {
+ this.source = source;
+ this.size = ByteBuffer.allocate(4);
+ this.buffer = null;
+ this.maxSize = getMaxSize(maxSize);
+ this.memoryPool = memoryPool;
+ }
+
+ public NetworkReceive() {
+ this(UNKNOWN_SOURCE);
+ }
+
+ @Override
+ public String source() {
+ return source;
+ }
+
+ @Override
+ public boolean complete() {
+ return !size.hasRemaining() && buffer != null && !buffer.hasRemaining();
+ }
+
+ public long readFrom(ScatteringByteChannel channel) throws IOException {
+ int read = 0;
+ if (size.hasRemaining()) {
+ int bytesRead = channel.read(size);
+ if (bytesRead < 0)
+ throw new EOFException();
+ read += bytesRead;
+ if (!size.hasRemaining()) {
+ size.rewind();
+ int receiveSize = size.getInt();
+ if (receiveSize < 0)
+ throw new InvalidReceiveException("Invalid receive (size = " + receiveSize + ")");
+ if (maxSize != UNLIMITED && receiveSize > maxSize) {
+ throw new ThingsboardKafkaClientError("Invalid receive (size = " + receiveSize + " larger than " + maxSize + ")");
+ }
+ requestedBufferSize = receiveSize; //may be 0 for some payloads (SASL)
+ if (receiveSize == 0) {
+ buffer = EMPTY_BUFFER;
+ }
+ }
+ }
+ if (buffer == null && requestedBufferSize != -1) { //we know the size we want but havent been able to allocate it yet
+ if (requestedBufferSize > TB_LOG_REQUESTED_BUFFER_SIZE) {
+ String stackTrace = Arrays.stream(Thread.currentThread().getStackTrace()).map(StackTraceElement::toString).collect(Collectors.joining("|"));
+ log.error("Allocating buffer of size {} for source {}", requestedBufferSize, source);
+ log.error("Stack Trace: {}", stackTrace);
+ }
+ buffer = memoryPool.tryAllocate(requestedBufferSize);
+ if (buffer == null)
+ log.trace("Broker low on memory - could not allocate buffer of size {} for source {}", requestedBufferSize, source);
+ }
+ if (buffer != null) {
+ int bytesRead = channel.read(buffer);
+ if (bytesRead < 0)
+ throw new EOFException();
+ read += bytesRead;
+ }
+
+ return read;
+ }
+
+ @Override
+ public boolean requiredMemoryAmountKnown() {
+ return requestedBufferSize != -1;
+ }
+
+ @Override
+ public boolean memoryAllocated() {
+ return buffer != null;
+ }
+
+
+ @Override
+ public void close() throws IOException {
+ if (buffer != null && buffer != EMPTY_BUFFER) {
+ memoryPool.release(buffer);
+ buffer = null;
+ }
+ }
+
+ public ByteBuffer payload() {
+ return this.buffer;
+ }
+
+ public int bytesRead() {
+ if (buffer == null)
+ return size.position();
+ return buffer.position() + size.position();
+ }
+
+ /**
+ * Returns the total size of the receive including payload and size buffer
+ * for use in metrics. This is consistent with {@link NetworkSend#size()}
+ */
+ public int size() {
+ return payload().limit() + size.limit();
+ }
+
+ private int getMaxSize(int maxSize) {
+ return maxSize == UNLIMITED ? TB_MAX_REQUESTED_BUFFER_SIZE : Math.min(maxSize, TB_MAX_REQUESTED_BUFFER_SIZE);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java b/application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4a0e019b6c66017186a4956f8b5f810488b01f9
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.thingsboard.server.install.ThingsboardInstallService;
+
+import java.util.Arrays;
+
+@Slf4j
+@SpringBootConfiguration
+@ComponentScan({"org.thingsboard.server.install",
+ "org.thingsboard.server.service.component",
+ "org.thingsboard.server.service.install",
+ "org.thingsboard.server.service.security.auth.jwt.settings",
+ "org.thingsboard.server.dao",
+ "org.thingsboard.server.common.stats",
+ "org.thingsboard.server.common.transport.config.ssl",
+ "org.thingsboard.server.cache",
+ "org.thingsboard.server.springfox"
+})
+public class ThingsboardInstallApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "thingsboard";
+
+ public static void main(String[] args) {
+ try {
+ SpringApplication application = new SpringApplication(ThingsboardInstallApplication.class);
+ application.setAdditionalProfiles("install");
+ ConfigurableApplicationContext context = application.run(updateArguments(args));
+ context.getBean(ThingsboardInstallService.class).performInstall();
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ System.exit(1);
+ }
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java b/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9c462ee0512e563ec25fdfa121884102652f54e
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.util.Arrays;
+
+@SpringBootConfiguration
+@EnableAsync
+@EnableScheduling
+@ComponentScan({"org.thingsboard.server", "org.thingsboard.script"})
+public class ThingsboardServerApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "thingsboard";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardServerApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b19a6954c78ef73555e8673e958087cdce96e01
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -0,0 +1,674 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.thingsboard.rule.engine.api.MailService;
+import org.thingsboard.rule.engine.api.SmsService;
+import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
+import org.thingsboard.script.api.js.JsInvokeService;
+import org.thingsboard.script.api.tbel.TbelInvokeService;
+import org.thingsboard.server.actors.service.ActorService;
+import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
+import org.thingsboard.server.cluster.TbClusterService;
+import org.thingsboard.server.common.data.event.ErrorEvent;
+import org.thingsboard.server.common.data.event.LifecycleEvent;
+import org.thingsboard.server.common.data.event.RuleChainDebugEvent;
+import org.thingsboard.server.common.data.event.RuleNodeDebugEvent;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.queue.ServiceType;
+import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
+import org.thingsboard.server.common.msg.tools.TbRateLimits;
+import org.thingsboard.server.common.stats.TbApiUsageReportClient;
+import org.thingsboard.server.dao.asset.AssetProfileService;
+import org.thingsboard.server.dao.asset.AssetService;
+import org.thingsboard.server.dao.attributes.AttributesService;
+import org.thingsboard.server.dao.audit.AuditLogService;
+import org.thingsboard.server.dao.cassandra.CassandraCluster;
+import org.thingsboard.server.dao.customer.CustomerService;
+import org.thingsboard.server.dao.dashboard.DashboardService;
+import org.thingsboard.server.dao.device.ClaimDevicesService;
+import org.thingsboard.server.dao.device.DeviceCredentialsService;
+import org.thingsboard.server.dao.device.DeviceProfileService;
+import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.edge.EdgeEventService;
+import org.thingsboard.server.dao.edge.EdgeService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
+import org.thingsboard.server.dao.event.EventService;
+import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor;
+import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor;
+import org.thingsboard.server.dao.ota.OtaPackageService;
+import org.thingsboard.server.dao.queue.QueueService;
+import org.thingsboard.server.dao.relation.RelationService;
+import org.thingsboard.server.dao.resource.ResourceService;
+import org.thingsboard.server.dao.rule.RuleChainService;
+import org.thingsboard.server.dao.rule.RuleNodeStateService;
+import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
+import org.thingsboard.server.dao.tenant.TenantProfileService;
+import org.thingsboard.server.dao.tenant.TenantService;
+import org.thingsboard.server.dao.timeseries.TimeseriesService;
+import org.thingsboard.server.dao.user.UserService;
+import org.thingsboard.server.dao.widget.WidgetTypeService;
+import org.thingsboard.server.dao.widget.WidgetsBundleService;
+import org.thingsboard.server.queue.discovery.PartitionService;
+import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
+import org.thingsboard.server.queue.util.DataDecodingEncodingService;
+import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
+import org.thingsboard.server.service.component.ComponentDiscoveryService;
+import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
+import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService;
+import org.thingsboard.server.service.executors.DbCallbackExecutorService;
+import org.thingsboard.server.service.executors.ExternalCallExecutorService;
+import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
+import org.thingsboard.server.service.mail.MailExecutorService;
+import org.thingsboard.server.service.profile.TbAssetProfileCache;
+import org.thingsboard.server.service.profile.TbDeviceProfileCache;
+import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
+import org.thingsboard.server.service.rpc.TbRpcService;
+import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
+import org.thingsboard.server.service.session.DeviceSessionCacheService;
+import org.thingsboard.server.service.sms.SmsExecutorService;
+import org.thingsboard.server.service.state.DeviceStateService;
+import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
+import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
+import org.thingsboard.server.service.transport.TbCoreToTransportService;
+
+import javax.annotation.Nullable;
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Component
+public class ActorSystemContext {
+
+ private static final FutureCallback RULE_CHAIN_DEBUG_EVENT_ERROR_CALLBACK = new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable Void event) {
+
+ }
+
+ @Override
+ public void onFailure(Throwable th) {
+ log.error("Could not save debug Event for Rule Chain", th);
+ }
+ };
+ private static final FutureCallback RULE_NODE_DEBUG_EVENT_ERROR_CALLBACK = new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable Void event) {
+
+ }
+
+ @Override
+ public void onFailure(Throwable th) {
+ log.error("Could not save debug Event for Node", th);
+ }
+ };
+
+ protected final ObjectMapper mapper = new ObjectMapper();
+
+ private final ConcurrentMap debugPerTenantLimits = new ConcurrentHashMap<>();
+
+ public ConcurrentMap getDebugPerTenantLimits() {
+ return debugPerTenantLimits;
+ }
+
+ @Autowired
+ @Getter
+ private TbApiUsageStateService apiUsageStateService;
+
+ @Autowired
+ @Getter
+ private TbApiUsageReportClient apiUsageClient;
+
+ @Autowired
+ @Getter
+ @Setter
+ private TbServiceInfoProvider serviceInfoProvider;
+
+ @Getter
+ @Setter
+ private ActorService actorService;
+
+ @Autowired
+ @Getter
+ @Setter
+ private ComponentDiscoveryService componentService;
+
+ @Autowired
+ @Getter
+ private DataDecodingEncodingService encodingService;
+
+ @Autowired
+ @Getter
+ private DeviceService deviceService;
+
+ @Autowired
+ @Getter
+ private DeviceProfileService deviceProfileService;
+
+ @Autowired
+ @Getter
+ private AssetProfileService assetProfileService;
+
+ @Autowired
+ @Getter
+ private DeviceCredentialsService deviceCredentialsService;
+
+ @Autowired
+ @Getter
+ private TbTenantProfileCache tenantProfileCache;
+
+ @Autowired
+ @Getter
+ private TbDeviceProfileCache deviceProfileCache;
+
+ @Autowired
+ @Getter
+ private TbAssetProfileCache assetProfileCache;
+
+ @Autowired
+ @Getter
+ private AssetService assetService;
+
+ @Autowired
+ @Getter
+ private DashboardService dashboardService;
+
+ @Autowired
+ @Getter
+ private TenantService tenantService;
+
+ @Autowired
+ @Getter
+ private TenantProfileService tenantProfileService;
+
+ @Autowired
+ @Getter
+ private CustomerService customerService;
+
+ @Autowired
+ @Getter
+ private UserService userService;
+
+ @Autowired
+ @Getter
+ private RuleChainService ruleChainService;
+
+ @Autowired
+ @Getter
+ private RuleNodeStateService ruleNodeStateService;
+
+ @Autowired
+ private PartitionService partitionService;
+
+ @Autowired
+ @Getter
+ private TbClusterService clusterService;
+
+ @Autowired
+ @Getter
+ private TimeseriesService tsService;
+
+ @Autowired
+ @Getter
+ private AttributesService attributesService;
+
+ @Autowired
+ @Getter
+ private EventService eventService;
+
+ @Autowired
+ @Getter
+ private RelationService relationService;
+
+ @Autowired
+ @Getter
+ private AuditLogService auditLogService;
+
+ @Autowired
+ @Getter
+ private EntityViewService entityViewService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private TbEntityViewService tbEntityViewService;
+
+ @Autowired
+ @Getter
+ private TelemetrySubscriptionService tsSubService;
+
+ @Autowired
+ @Getter
+ private AlarmSubscriptionService alarmService;
+
+ @Autowired
+ @Getter
+ private JsInvokeService jsInvokeService;
+
+ @Autowired(required = false)
+ @Getter
+ private TbelInvokeService tbelInvokeService;
+
+ @Autowired
+ @Getter
+ private MailExecutorService mailExecutor;
+
+ @Autowired
+ @Getter
+ private SmsExecutorService smsExecutor;
+
+ @Autowired
+ @Getter
+ private DbCallbackExecutorService dbCallbackExecutor;
+
+ @Autowired
+ @Getter
+ private ExternalCallExecutorService externalCallExecutorService;
+
+ @Autowired
+ @Getter
+ private SharedEventLoopGroupService sharedEventLoopGroupService;
+
+ @Autowired
+ @Getter
+ private MailService mailService;
+
+ @Autowired
+ @Getter
+ private SmsService smsService;
+
+ @Autowired
+ @Getter
+ private SmsSenderFactory smsSenderFactory;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private ClaimDevicesService claimDevicesService;
+
+ @Autowired
+ @Getter
+ private JsInvokeStats jsInvokeStats;
+
+ //TODO: separate context for TbCore and TbRuleEngine
+ @Autowired(required = false)
+ @Getter
+ private DeviceStateService deviceStateService;
+
+ @Autowired(required = false)
+ @Getter
+ private DeviceSessionCacheService deviceSessionCacheService;
+
+ @Autowired(required = false)
+ @Getter
+ private TbCoreToTransportService tbCoreToTransportService;
+
+ /**
+ * The following Service will be null if we operate in tb-core mode
+ */
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private TbRuleEngineDeviceRpcService tbRuleEngineDeviceRpcService;
+
+ /**
+ * The following Service will be null if we operate in tb-rule-engine mode
+ */
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private TbCoreDeviceRpcService tbCoreDeviceRpcService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private EdgeService edgeService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private EdgeEventService edgeEventService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private EdgeRpcService edgeRpcService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private ResourceService resourceService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private OtaPackageService otaPackageService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private TbRpcService tbRpcService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private QueueService queueService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private WidgetsBundleService widgetsBundleService;
+
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private WidgetTypeService widgetTypeService;
+
+ @Value("${actors.session.max_concurrent_sessions_per_device:1}")
+ @Getter
+ private long maxConcurrentSessionsPerDevice;
+
+ @Value("${actors.session.sync.timeout:10000}")
+ @Getter
+ private long syncSessionTimeout;
+
+ @Value("${actors.rule.chain.error_persist_frequency:3000}")
+ @Getter
+ private long ruleChainErrorPersistFrequency;
+
+ @Value("${actors.rule.node.error_persist_frequency:3000}")
+ @Getter
+ private long ruleNodeErrorPersistFrequency;
+
+ @Value("${actors.statistics.enabled:true}")
+ @Getter
+ private boolean statisticsEnabled;
+
+ @Value("${actors.statistics.persist_frequency:3600000}")
+ @Getter
+ private long statisticsPersistFrequency;
+
+ @Value("${edges.enabled:true}")
+ @Getter
+ private boolean edgesEnabled;
+
+ @Value("${cache.type:caffeine}")
+ @Getter
+ private String cacheType;
+
+ @Getter
+ private boolean localCacheType;
+
+ @PostConstruct
+ public void init() {
+ this.localCacheType = "caffeine".equals(cacheType);
+ }
+
+ @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}")
+ public void printStats() {
+ if (statisticsEnabled) {
+ if (jsInvokeStats.getRequests() > 0 || jsInvokeStats.getResponses() > 0 || jsInvokeStats.getFailures() > 0) {
+ log.info("Rule Engine JS Invoke Stats: requests [{}] responses [{}] failures [{}]",
+ jsInvokeStats.getRequests(), jsInvokeStats.getResponses(), jsInvokeStats.getFailures());
+ jsInvokeStats.reset();
+ }
+ }
+ }
+
+ @Value("${actors.tenant.create_components_on_init:true}")
+ @Getter
+ private boolean tenantComponentsInitEnabled;
+
+ @Value("${actors.rule.allow_system_mail_service:true}")
+ @Getter
+ private boolean allowSystemMailService;
+
+ @Value("${actors.rule.allow_system_sms_service:true}")
+ @Getter
+ private boolean allowSystemSmsService;
+
+ @Value("${transport.sessions.inactivity_timeout:300000}")
+ @Getter
+ private long sessionInactivityTimeout;
+
+ @Value("${transport.sessions.report_timeout:3000}")
+ @Getter
+ private long sessionReportTimeout;
+
+ @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled:true}")
+ @Getter
+ private boolean debugPerTenantEnabled;
+
+ @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.configuration:50000:3600}")
+ @Getter
+ private String debugPerTenantLimitsConfiguration;
+
+ @Value("${actors.rpc.sequential:false}")
+ @Getter
+ private boolean rpcSequential;
+
+ @Value("${actors.rpc.max_retries:5}")
+ @Getter
+ private int maxRpcRetries;
+
+ @Getter
+ @Setter
+ private TbActorSystem actorSystem;
+
+ @Setter
+ private TbActorRef appActor;
+
+ @Getter
+ @Setter
+ private TbActorRef statsActor;
+
+ @Autowired(required = false)
+ @Getter
+ private CassandraCluster cassandraCluster;
+
+ @Autowired(required = false)
+ @Getter
+ private CassandraBufferedRateReadExecutor cassandraBufferedRateReadExecutor;
+
+ @Autowired(required = false)
+ @Getter
+ private CassandraBufferedRateWriteExecutor cassandraBufferedRateWriteExecutor;
+
+ @Autowired(required = false)
+ @Getter
+ private RedisTemplate redisTemplate;
+
+ public ScheduledExecutorService getScheduler() {
+ return actorSystem.getScheduler();
+ }
+
+ public void persistError(TenantId tenantId, EntityId entityId, String method, Exception e) {
+ eventService.saveAsync(ErrorEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .method(method)
+ .error(toString(e)).build());
+ }
+
+ public void persistLifecycleEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent lcEvent, Exception e) {
+ LifecycleEvent.LifecycleEventBuilder event = LifecycleEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .lcEventType(lcEvent.name());
+
+ if (e != null) {
+ event.success(false).error(toString(e));
+ } else {
+ event.success(true);
+ }
+
+ eventService.saveAsync(event.build());
+ }
+
+ private String toString(Throwable e) {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ return sw.toString();
+ }
+
+ public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) {
+ return partitionService.resolve(serviceType, tenantId, entityId);
+ }
+
+ public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) {
+ return partitionService.resolve(serviceType, queueName, tenantId, entityId);
+ }
+
+ public String getServiceId() {
+ return serviceInfoProvider.getServiceId();
+ }
+
+ public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
+ persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, null, null);
+ }
+
+ public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) {
+ persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, error, null);
+ }
+
+ public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error, String failureMessage) {
+ persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, error, failureMessage);
+ }
+
+ public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) {
+ persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, error, null);
+ }
+
+ public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
+ persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, null, null);
+ }
+
+ private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error, String failureMessage) {
+ if (checkLimits(tenantId, tbMsg, error)) {
+ try {
+ RuleNodeDebugEvent.RuleNodeDebugEventBuilder event = RuleNodeDebugEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .eventType(type)
+ .eventEntity(tbMsg.getOriginator())
+ .msgId(tbMsg.getId())
+ .msgType(tbMsg.getType())
+ .dataType(tbMsg.getDataType().name())
+ .relationType(relationType)
+ .data(tbMsg.getData())
+ .metadata(mapper.writeValueAsString(tbMsg.getMetaData().getData()));
+
+ if (error != null) {
+ event.error(toString(error));
+ } else if (failureMessage != null) {
+ event.error(failureMessage);
+ }
+
+ ListenableFuture future = eventService.saveAsync(event.build());
+ Futures.addCallback(future, RULE_NODE_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
+ } catch (IOException ex) {
+ log.warn("Failed to persist rule node debug message", ex);
+ }
+ }
+ }
+
+ private boolean checkLimits(TenantId tenantId, TbMsg tbMsg, Throwable error) {
+ if (debugPerTenantEnabled) {
+ DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.computeIfAbsent(tenantId, id ->
+ new DebugTbRateLimits(new TbRateLimits(debugPerTenantLimitsConfiguration), false));
+
+ if (!debugTbRateLimits.getTbRateLimits().tryConsume()) {
+ if (!debugTbRateLimits.isRuleChainEventSaved()) {
+ persistRuleChainDebugModeEvent(tenantId, tbMsg.getRuleChainId(), error);
+ debugTbRateLimits.setRuleChainEventSaved(true);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("[{}] Tenant level debug mode rate limit detected: {}", tenantId, tbMsg);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void persistRuleChainDebugModeEvent(TenantId tenantId, EntityId entityId, Throwable error) {
+ RuleChainDebugEvent.RuleChainDebugEventBuilder event = RuleChainDebugEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .message("Reached debug mode rate limit!");
+ if (error != null) {
+ event.error(toString(error));
+ }
+
+ ListenableFuture future = eventService.saveAsync(event.build());
+ Futures.addCallback(future, RULE_CHAIN_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
+ }
+
+ public static Exception toException(Throwable error) {
+ return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
+ }
+
+ public void tell(TbActorMsg tbActorMsg) {
+ appActor.tell(tbActorMsg);
+ }
+
+ public void tellWithHighPriority(TbActorMsg tbActorMsg) {
+ appActor.tellWithHighPriority(tbActorMsg);
+ }
+
+ public void schedulePeriodicMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs, long periodInMs) {
+ log.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs);
+ getScheduler().scheduleWithFixedDelay(() -> ctx.tell(msg), delayInMs, periodInMs, TimeUnit.MILLISECONDS);
+ }
+
+ public void scheduleMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs) {
+ log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs);
+ if (delayInMs > 0) {
+ getScheduler().schedule(() -> ctx.tell(msg), delayInMs, TimeUnit.MILLISECONDS);
+ } else {
+ ctx.tell(msg);
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java b/application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a8232ebdbcc70a6de4a35e4b86b5a912085880a
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors;
+
+import lombok.RequiredArgsConstructor;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EntityId;
+
+import java.util.function.Predicate;
+
+@RequiredArgsConstructor
+public class TbEntityTypeActorIdPredicate implements Predicate {
+
+ private final EntityType entityType;
+
+ @Override
+ public boolean test(TbActorId actorId) {
+ return actorId instanceof TbEntityActorId && testEntityId(((TbEntityActorId) actorId).getEntityId());
+ }
+
+ protected boolean testEntityId(EntityId entityId) {
+ return entityId.getEntityType().equals(entityType);
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..eeb6d8e82efd4f45fdc74c2557d462ee3bea78c6
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -0,0 +1,229 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.app;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorException;
+import org.thingsboard.server.actors.TbActorId;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.actors.TbEntityActorId;
+import org.thingsboard.server.actors.device.SessionTimeoutCheckMsg;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.actors.tenant.TenantActor;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.PageDataIterable;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
+import org.thingsboard.server.common.msg.edge.EdgeSessionMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
+import org.thingsboard.server.common.msg.queue.RuleEngineException;
+import org.thingsboard.server.common.msg.queue.ServiceType;
+import org.thingsboard.server.dao.model.ModelConstants;
+import org.thingsboard.server.dao.tenant.TenantService;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Slf4j
+public class AppActor extends ContextAwareActor {
+
+ private final TenantService tenantService;
+ private final Set deletedTenants;
+ private volatile boolean ruleChainsInitialized;
+
+ private AppActor(ActorSystemContext systemContext) {
+ super(systemContext);
+ this.tenantService = systemContext.getTenantService();
+ this.deletedTenants = new HashSet<>();
+ }
+
+ @Override
+ public void init(TbActorCtx ctx) throws TbActorException {
+ super.init(ctx);
+ if (systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE)) {
+ systemContext.schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(),
+ systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout());
+ }
+ }
+
+ @Override
+ protected boolean doProcess(TbActorMsg msg) {
+ if (!ruleChainsInitialized) {
+ initTenantActors();
+ ruleChainsInitialized = true;
+ if (msg.getMsgType() != MsgType.APP_INIT_MSG && msg.getMsgType() != MsgType.PARTITION_CHANGE_MSG) {
+ log.warn("Rule Chains initialized by unexpected message: {}", msg);
+ }
+ }
+ switch (msg.getMsgType()) {
+ case APP_INIT_MSG:
+ break;
+ case PARTITION_CHANGE_MSG:
+ ctx.broadcastToChildren(msg);
+ break;
+ case COMPONENT_LIFE_CYCLE_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case QUEUE_TO_RULE_ENGINE_MSG:
+ onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg);
+ break;
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
+ onToDeviceActorMsg((TenantAwareMsg) msg, false);
+ break;
+ case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
+ onToDeviceActorMsg((TenantAwareMsg) msg, true);
+ break;
+ case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
+ case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG:
+ case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG:
+ onToEdgeSessionMsg((EdgeSessionMsg) msg);
+ break;
+ case SESSION_TIMEOUT_MSG:
+ ctx.broadcastToChildrenByType(msg, EntityType.TENANT);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private void initTenantActors() {
+ log.info("Starting main system actor.");
+ try {
+ if (systemContext.isTenantComponentsInitEnabled()) {
+ PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
+ for (Tenant tenant : tenantIterator) {
+ log.debug("[{}] Creating tenant actor", tenant.getId());
+ getOrCreateTenantActor(tenant.getId());
+ log.debug("[{}] Tenant actor created.", tenant.getId());
+ }
+ }
+ log.info("Main system actor started.");
+ } catch (Exception e) {
+ log.warn("Unknown failure", e);
+ }
+ }
+
+ private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) {
+ if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) {
+ msg.getMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!"));
+ } else {
+ if (!deletedTenants.contains(msg.getTenantId())) {
+ getOrCreateTenantActor(msg.getTenantId()).tell(msg);
+ } else {
+ msg.getMsg().getCallback().onSuccess();
+ }
+ }
+ }
+
+ private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
+ TbActorRef target = null;
+ if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) {
+ if (!EntityType.TENANT_PROFILE.equals(msg.getEntityId().getEntityType())) {
+ log.warn("Message has system tenant id: {}", msg);
+ }
+ } else {
+ if (EntityType.TENANT.equals(msg.getEntityId().getEntityType())) {
+ TenantId tenantId = TenantId.fromUUID(msg.getEntityId().getId());
+ if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
+ log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
+ deletedTenants.add(tenantId);
+ ctx.stop(new TbEntityActorId(tenantId));
+ } else {
+ target = getOrCreateTenantActor(msg.getTenantId());
+ }
+ } else {
+ target = getOrCreateTenantActor(msg.getTenantId());
+ }
+ }
+ if (target != null) {
+ target.tellWithHighPriority(msg);
+ } else {
+ log.debug("[{}] Invalid component lifecycle msg: {}", msg.getTenantId(), msg);
+ }
+ }
+
+ private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) {
+ if (!deletedTenants.contains(msg.getTenantId())) {
+ TbActorRef tenantActor = getOrCreateTenantActor(msg.getTenantId());
+ if (priority) {
+ tenantActor.tellWithHighPriority(msg);
+ } else {
+ tenantActor.tell(msg);
+ }
+ } else {
+ if (msg instanceof TransportToDeviceActorMsgWrapper) {
+ ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess();
+ }
+ }
+ }
+
+ private TbActorRef getOrCreateTenantActor(TenantId tenantId) {
+ return ctx.getOrCreateChildActor(new TbEntityActorId(tenantId),
+ () -> DefaultActorService.TENANT_DISPATCHER_NAME,
+ () -> new TenantActor.ActorCreator(systemContext, tenantId));
+ }
+
+ private void onToEdgeSessionMsg(EdgeSessionMsg msg) {
+ TbActorRef target = null;
+ if (ModelConstants.SYSTEM_TENANT.equals(msg.getTenantId())) {
+ log.warn("Message has system tenant id: {}", msg);
+ } else {
+ target = getOrCreateTenantActor(msg.getTenantId());
+ }
+ if (target != null) {
+ target.tellWithHighPriority(msg);
+ } else {
+ log.debug("[{}] Invalid edge session msg: {}", msg.getTenantId(), msg);
+ }
+ }
+
+ public static class ActorCreator extends ContextBasedCreator {
+
+ public ActorCreator(ActorSystemContext context) {
+ super(context);
+ }
+
+ @Override
+ public TbActorId createActorId() {
+ return new TbEntityActorId(TenantId.SYS_TENANT_ID);
+ }
+
+ @Override
+ public TbActor createActor() {
+ return new AppActor(context);
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppInitMsg.java b/application/src/main/java/org/thingsboard/server/actors/app/AppInitMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..772c9ae5b53590a13ce3e8be3501df00d69d6c47
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppInitMsg.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.app;
+
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+public class AppInitMsg implements TbActorMsg {
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.APP_INIT_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0f49ba62f7cbd76601a91e0a15d06b7f5d49f01
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorException;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
+import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
+import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
+
+@Slf4j
+public class DeviceActor extends ContextAwareActor {
+
+ private final DeviceActorMessageProcessor processor;
+
+ DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
+ super(systemContext);
+ this.processor = new DeviceActorMessageProcessor(systemContext, tenantId, deviceId);
+ }
+
+ @Override
+ public void init(TbActorCtx ctx) throws TbActorException {
+ super.init(ctx);
+ log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId);
+ try {
+ processor.init(ctx);
+ log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId);
+ } catch (Exception e) {
+ log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e);
+ throw new TbActorException("Failed to initialize device actor", e);
+ }
+ }
+
+ @Override
+ protected boolean doProcess(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
+ processor.process(ctx, (TransportToDeviceActorMsgWrapper) msg);
+ break;
+ case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
+ processor.processAttributesUpdate(ctx, (DeviceAttributesEventNotificationMsg) msg);
+ break;
+ case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
+ processor.processCredentialsUpdate(msg);
+ break;
+ case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg);
+ break;
+ case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
+ processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg);
+ break;
+ case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ processor.processRpcResponsesFromEdge(ctx, (FromDeviceRpcResponseActorMsg) msg);
+ break;
+ case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
+ processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg);
+ break;
+ case SESSION_TIMEOUT_MSG:
+ processor.checkSessionsTimeout();
+ break;
+ case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ processor.processEdgeUpdate((DeviceEdgeUpdateMsg) msg);
+ break;
+ case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
+ processor.processRemoveRpc(ctx, (RemoveRpcActorMsg) msg);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a369a57e987189225bf69d3978816f29b547e37
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorId;
+import org.thingsboard.server.actors.TbEntityActorId;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.TenantId;
+
+public class DeviceActorCreator extends ContextBasedCreator {
+
+ private final TenantId tenantId;
+ private final DeviceId deviceId;
+
+ public DeviceActorCreator(ActorSystemContext context, TenantId tenantId, DeviceId deviceId) {
+ super(context);
+ this.tenantId = tenantId;
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public TbActorId createActorId() {
+ return new TbEntityActorId(deviceId);
+ }
+
+ @Override
+ public TbActor createActor() {
+ return new DeviceActor(context, tenantId, deviceId);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a365b4e56a3cbd746f5b8d4eccdbbdd0349e16d
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -0,0 +1,1003 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.protobuf.InvalidProtocolBufferException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.thingsboard.common.util.JacksonUtil;
+import org.thingsboard.common.util.LinkedHashMapRemoveEldest;
+import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.EdgeUtils;
+import org.thingsboard.server.common.data.StringUtils;
+import org.thingsboard.server.common.data.edge.EdgeEvent;
+import org.thingsboard.server.common.data.edge.EdgeEventActionType;
+import org.thingsboard.server.common.data.edge.EdgeEventType;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.EdgeId;
+import org.thingsboard.server.common.data.id.RpcId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.kv.AttributeKey;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.kv.KvEntry;
+import org.thingsboard.server.common.data.page.PageData;
+import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.page.SortOrder;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
+import org.thingsboard.server.common.data.rpc.Rpc;
+import org.thingsboard.server.common.data.rpc.RpcError;
+import org.thingsboard.server.common.data.rpc.RpcStatus;
+import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
+import org.thingsboard.server.common.data.security.DeviceCredentials;
+import org.thingsboard.server.common.data.security.DeviceCredentialsType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.queue.TbCallback;
+import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
+import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
+import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionSubscriptionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseStatusMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
+import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
+import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
+import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+
+/**
+ * @author Andrew Shvayka
+ */
+@Slf4j
+class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
+
+ static final String SESSION_TIMEOUT_MESSAGE = "session timeout!";
+ final TenantId tenantId;
+ final DeviceId deviceId;
+ final LinkedHashMapRemoveEldest sessions;
+ private final Map attributeSubscriptions;
+ private final Map rpcSubscriptions;
+ private final Map toDeviceRpcPendingMap;
+ private final boolean rpcSequential;
+
+ private int rpcSeq = 0;
+ private String deviceName;
+ private String deviceType;
+ private TbMsgMetaData defaultMetaData;
+ private EdgeId edgeId;
+
+ DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
+ super(systemContext);
+ this.tenantId = tenantId;
+ this.deviceId = deviceId;
+ this.rpcSequential = systemContext.isRpcSequential();
+ this.attributeSubscriptions = new HashMap<>();
+ this.rpcSubscriptions = new HashMap<>();
+ this.toDeviceRpcPendingMap = new LinkedHashMap<>();
+ this.sessions = new LinkedHashMapRemoveEldest<>(systemContext.getMaxConcurrentSessionsPerDevice(), this::notifyTransportAboutClosedSessionMaxSessionsLimit);
+ if (initAttributes()) {
+ restoreSessions();
+ }
+ }
+
+ boolean initAttributes() {
+ Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId);
+ if (device != null) {
+ this.deviceName = device.getName();
+ this.deviceType = device.getType();
+ this.defaultMetaData = new TbMsgMetaData();
+ this.defaultMetaData.putValue("deviceName", deviceName);
+ this.defaultMetaData.putValue("deviceType", deviceType);
+ if (systemContext.isEdgesEnabled()) {
+ this.edgeId = findRelatedEdgeId();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private EdgeId findRelatedEdgeId() {
+ List result =
+ systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON);
+ if (result != null && result.size() > 0) {
+ EntityRelation relationToEdge = result.get(0);
+ if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) {
+ log.trace("[{}][{}] found edge [{}] for device", tenantId, deviceId, relationToEdge.getFrom().getId());
+ return new EdgeId(relationToEdge.getFrom().getId());
+ } else {
+ log.trace("[{}][{}] edge relation is empty {}", tenantId, deviceId, relationToEdge);
+ }
+ } else {
+ log.trace("[{}][{}] device doesn't have any related edge", tenantId, deviceId);
+ }
+ return null;
+ }
+
+ void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
+ ToDeviceRpcRequest request = msg.getMsg();
+ ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request);
+
+ long timeout = request.getExpirationTime() - System.currentTimeMillis();
+ boolean persisted = request.isPersisted();
+
+ if (timeout <= 0) {
+ log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime());
+ if (persisted) {
+ createRpc(request, RpcStatus.EXPIRED);
+ }
+ return;
+ } else if (persisted) {
+ createRpc(request, RpcStatus.QUEUED);
+ }
+
+ boolean sent = false;
+ if (systemContext.isEdgesEnabled() && edgeId != null) {
+ log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId());
+ try {
+ saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId()).get();
+ sent = true;
+ } catch (InterruptedException | ExecutionException e) {
+ log.error("[{}][{}][{}] Failed to save rpc request to edge queue {}", tenantId, deviceId, edgeId.getId(), request, e);
+ }
+ } else if (isSendNewRpcAvailable()) {
+ sent = rpcSubscriptions.size() > 0;
+ Set syncSessionSet = new HashSet<>();
+ rpcSubscriptions.forEach((key, value) -> {
+ sendToTransport(rpcRequest, key, value.getNodeId());
+ if (SessionType.SYNC == value.getType()) {
+ syncSessionSet.add(key);
+ }
+ });
+ log.trace("Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions);
+ syncSessionSet.forEach(rpcSubscriptions::remove);
+ }
+
+ if (persisted) {
+ ObjectNode response = JacksonUtil.newObjectNode();
+ response.put("rpcId", request.getId().toString());
+ systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null));
+ }
+
+ if (!persisted && request.isOneway() && sent) {
+ log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
+ systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
+ } else {
+ registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
+ }
+ if (sent) {
+ log.debug("[{}] RPC request {} is sent!", deviceId, request.getId());
+ } else {
+ log.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId());
+ }
+ }
+
+ private boolean isSendNewRpcAvailable() {
+ return !rpcSequential || toDeviceRpcPendingMap.values().stream().filter(md -> !md.isDelivered()).findAny().isEmpty();
+ }
+
+ private Rpc createRpc(ToDeviceRpcRequest request, RpcStatus status) {
+ Rpc rpc = new Rpc(new RpcId(request.getId()));
+ rpc.setCreatedTime(System.currentTimeMillis());
+ rpc.setTenantId(tenantId);
+ rpc.setDeviceId(deviceId);
+ rpc.setExpirationTime(request.getExpirationTime());
+ rpc.setRequest(JacksonUtil.valueToTree(request));
+ rpc.setStatus(status);
+ rpc.setAdditionalInfo(JacksonUtil.toJsonNode(request.getAdditionalInfo()));
+ return systemContext.getTbRpcService().save(tenantId, rpc);
+ }
+
+ private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) {
+ ToDeviceRpcRequestBody body = request.getBody();
+ return ToDeviceRpcRequestMsg.newBuilder()
+ .setRequestId(rpcSeq++)
+ .setMethodName(body.getMethod())
+ .setParams(body.getParams())
+ .setExpirationTime(request.getExpirationTime())
+ .setRequestIdMSB(request.getId().getMostSignificantBits())
+ .setRequestIdLSB(request.getId().getLeastSignificantBits())
+ .setOneway(request.isOneway())
+ .setPersisted(request.isPersisted())
+ .build();
+ }
+
+ void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {
+ log.debug("[{}] Processing rpc command response from edge session", deviceId);
+ ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
+ boolean success = requestMd != null;
+ if (success) {
+ systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(responseMsg.getMsg());
+ } else {
+ log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
+ }
+ }
+
+ void processRemoveRpc(TbActorCtx context, RemoveRpcActorMsg msg) {
+ log.debug("[{}] Processing remove rpc command", msg.getRequestId());
+ Map.Entry entry = null;
+ for (Map.Entry e : toDeviceRpcPendingMap.entrySet()) {
+ if (e.getValue().getMsg().getMsg().getId().equals(msg.getRequestId())) {
+ entry = e;
+ break;
+ }
+ }
+
+ if (entry != null) {
+ if (entry.getValue().isDelivered()) {
+ toDeviceRpcPendingMap.remove(entry.getKey());
+ } else {
+ Optional> firstRpc = getFirstRpc();
+ if (firstRpc.isPresent() && entry.getKey().equals(firstRpc.get().getKey())) {
+ toDeviceRpcPendingMap.remove(entry.getKey());
+ sendNextPendingRequest(context);
+ } else {
+ toDeviceRpcPendingMap.remove(entry.getKey());
+ }
+ }
+ }
+ }
+
+ private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
+ toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
+ DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
+ scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout());
+ }
+
+ void processServerSideRpcTimeout(TbActorCtx context, DeviceActorServerSideRpcTimeoutMsg msg) {
+ ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
+ if (requestMd != null) {
+ log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
+ if (requestMd.getMsg().getMsg().isPersisted()) {
+ systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.EXPIRED, null);
+ }
+ systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
+ null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
+ if (!requestMd.isDelivered()) {
+ sendNextPendingRequest(context);
+ }
+ }
+ }
+
+ private void sendPendingRequests(TbActorCtx context, UUID sessionId, String nodeId) {
+ SessionType sessionType = getSessionType(sessionId);
+ if (!toDeviceRpcPendingMap.isEmpty()) {
+ log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
+ if (sessionType == SessionType.SYNC) {
+ log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
+ rpcSubscriptions.remove(sessionId);
+ }
+ } else {
+ log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
+ }
+ Set sentOneWayIds = new HashSet<>();
+
+ if (rpcSequential) {
+ getFirstRpc().ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds));
+ } else if (sessionType == SessionType.ASYNC) {
+ toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, nodeId, sentOneWayIds));
+ } else {
+ toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds));
+ }
+
+ sentOneWayIds.stream().filter(id -> !toDeviceRpcPendingMap.get(id).getMsg().getMsg().isPersisted()).forEach(toDeviceRpcPendingMap::remove);
+ }
+
+ private Optional> getFirstRpc() {
+ return toDeviceRpcPendingMap.entrySet().stream().filter(e -> !e.getValue().isDelivered()).findFirst();
+ }
+
+ private void sendNextPendingRequest(TbActorCtx context) {
+ if (rpcSequential) {
+ rpcSubscriptions.forEach((id, s) -> sendPendingRequests(context, id, s.getNodeId()));
+ }
+ }
+
+ private Consumer> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set sentOneWayIds) {
+ return entry -> {
+ ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
+ ToDeviceRpcRequestBody body = request.getBody();
+ if (request.isOneway() && !rpcSequential) {
+ sentOneWayIds.add(entry.getKey());
+ systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null));
+ }
+ ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder()
+ .setRequestId(entry.getKey())
+ .setMethodName(body.getMethod())
+ .setParams(body.getParams())
+ .setExpirationTime(request.getExpirationTime())
+ .setRequestIdMSB(request.getId().getMostSignificantBits())
+ .setRequestIdLSB(request.getId().getLeastSignificantBits())
+ .setOneway(request.isOneway())
+ .setPersisted(request.isPersisted())
+ .build();
+ sendToTransport(rpcRequest, sessionId, nodeId);
+ };
+ }
+
+ void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) {
+ TransportToDeviceActorMsg msg = wrapper.getMsg();
+ TbCallback callback = wrapper.getCallback();
+ var sessionInfo = msg.getSessionInfo();
+
+ if (msg.hasSessionEvent()) {
+ processSessionStateMsgs(sessionInfo, msg.getSessionEvent());
+ }
+ if (msg.hasSubscribeToAttributes()) {
+ processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes());
+ }
+ if (msg.hasSubscribeToRPC()) {
+ processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC());
+ }
+ if (msg.hasSendPendingRPC()) {
+ sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo.getNodeId());
+ }
+ if (msg.hasGetAttributes()) {
+ handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes());
+ }
+ if (msg.hasToDeviceRPCCallResponse()) {
+ processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse());
+ }
+ if (msg.hasSubscriptionInfo()) {
+ handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo());
+ }
+ if (msg.hasClaimDevice()) {
+ handleClaimDeviceMsg(context, sessionInfo, msg.getClaimDevice());
+ }
+ if (msg.hasRpcResponseStatusMsg()) {
+ processRpcResponseStatus(context, sessionInfo, msg.getRpcResponseStatusMsg());
+ }
+ if (msg.hasUplinkNotificationMsg()) {
+ processUplinkNotificationMsg(context, sessionInfo, msg.getUplinkNotificationMsg());
+ }
+ callback.onSuccess();
+ }
+
+ private void processUplinkNotificationMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.UplinkNotificationMsg uplinkNotificationMsg) {
+ String nodeId = sessionInfo.getNodeId();
+ sessions.entrySet().stream()
+ .filter(kv -> kv.getValue().getSessionInfo().getNodeId().equals(nodeId) && (kv.getValue().isSubscribedToAttributes() || kv.getValue().isSubscribedToRPC()))
+ .forEach(kv -> {
+ ToTransportMsg msg = ToTransportMsg.newBuilder()
+ .setSessionIdMSB(kv.getKey().getMostSignificantBits())
+ .setSessionIdLSB(kv.getKey().getLeastSignificantBits())
+ .setUplinkNotificationMsg(uplinkNotificationMsg)
+ .build();
+ systemContext.getTbCoreToTransportService().process(kv.getValue().getSessionInfo().getNodeId(), msg);
+ });
+ }
+
+ private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {
+ DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
+ systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
+ }
+
+ private void reportSessionOpen() {
+ systemContext.getDeviceStateService().onDeviceConnect(tenantId, deviceId);
+ }
+
+ private void reportSessionClose() {
+ systemContext.getDeviceStateService().onDeviceDisconnect(tenantId, deviceId);
+ }
+
+ private void handleGetAttributesRequest(TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
+ int requestId = request.getRequestId();
+ if (request.getOnlyShared()) {
+ Futures.addCallback(findAllAttributesByScope(DataConstants.SHARED_SCOPE), new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable List result) {
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setRequestId(requestId)
+ .setSharedStateMsg(true)
+ .addAllSharedAttributeList(toTsKvProtos(result))
+ .setIsMultipleAttributesRequest(request.getSharedAttributeNamesCount() > 1)
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setError(t.getMessage())
+ .setSharedStateMsg(true)
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
+ }
+ }, MoreExecutors.directExecutor());
+ } else {
+ Futures.addCallback(getAttributesKvEntries(request), new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable List> result) {
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setRequestId(requestId)
+ .addAllClientAttributeList(toTsKvProtos(result.get(0)))
+ .addAllSharedAttributeList(toTsKvProtos(result.get(1)))
+ .setIsMultipleAttributesRequest(
+ request.getSharedAttributeNamesCount() + request.getClientAttributeNamesCount() > 1)
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setError(t.getMessage())
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
+ }
+ }, MoreExecutors.directExecutor());
+ }
+ }
+
+ private ListenableFuture>> getAttributesKvEntries(GetAttributeRequestMsg request) {
+ ListenableFuture> clientAttributesFuture;
+ ListenableFuture> sharedAttributesFuture;
+ if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
+ clientAttributesFuture = findAllAttributesByScope(DataConstants.CLIENT_SCOPE);
+ sharedAttributesFuture = findAllAttributesByScope(DataConstants.SHARED_SCOPE);
+ } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
+ clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
+ sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
+ } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
+ clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());
+ sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
+ } else {
+ sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());
+ clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
+ }
+ return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture));
+ }
+
+ private ListenableFuture> findAllAttributesByScope(String scope) {
+ return systemContext.getAttributesService().findAll(tenantId, deviceId, scope);
+ }
+
+ private ListenableFuture> findAttributesByScope(Set attributesSet, String scope) {
+ return systemContext.getAttributesService().find(tenantId, deviceId, scope, attributesSet);
+ }
+
+ private Set toSet(List strings) {
+ return new HashSet<>(strings);
+ }
+
+ private SessionType getSessionType(UUID sessionId) {
+ return sessions.containsKey(sessionId) ? SessionType.ASYNC : SessionType.SYNC;
+ }
+
+ void processAttributesUpdate(TbActorCtx context, DeviceAttributesEventNotificationMsg msg) {
+ if (attributeSubscriptions.size() > 0) {
+ boolean hasNotificationData = false;
+ AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
+ if (msg.isDeleted()) {
+ List sharedKeys = msg.getDeletedKeys().stream()
+ .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
+ .map(AttributeKey::getAttributeKey)
+ .collect(Collectors.toList());
+ if (!sharedKeys.isEmpty()) {
+ notification.addAllSharedDeleted(sharedKeys);
+ hasNotificationData = true;
+ }
+ } else {
+ if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
+ List attributes = new ArrayList<>(msg.getValues());
+ if (attributes.size() > 0) {
+ List sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
+ .collect(Collectors.toList());
+ if (!sharedUpdated.isEmpty()) {
+ notification.addAllSharedUpdated(sharedUpdated);
+ hasNotificationData = true;
+ }
+ } else {
+ log.debug("[{}] No public shared side attributes changed!", deviceId);
+ }
+ }
+ }
+ if (hasNotificationData) {
+ AttributeUpdateNotificationMsg finalNotification = notification.build();
+ attributeSubscriptions.forEach((key, value) -> sendToTransport(finalNotification, key, value.getNodeId()));
+ }
+ } else {
+ log.debug("[{}] No registered attributes subscriptions to process!", deviceId);
+ }
+ }
+
+ private void processRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
+ UUID sessionId = getSessionId(sessionInfo);
+ log.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
+ ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
+ boolean success = requestMd != null;
+ if (success) {
+ boolean hasError = StringUtils.isNotEmpty(responseMsg.getError());
+ try {
+ String payload = hasError ? responseMsg.getError() : responseMsg.getPayload();
+ systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(
+ new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
+ payload, null));
+ if (requestMd.getMsg().getMsg().isPersisted()) {
+ RpcStatus status = hasError ? RpcStatus.FAILED : RpcStatus.SUCCESSFUL;
+ JsonNode response;
+ try {
+ response = JacksonUtil.toJsonNode(payload);
+ } catch (IllegalArgumentException e) {
+ response = JacksonUtil.newObjectNode().put("error", payload);
+ }
+ systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), status, response);
+ }
+ } finally {
+ if (hasError && !requestMd.isDelivered()) {
+ sendNextPendingRequest(context);
+ }
+ }
+ } else {
+ log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
+ }
+ }
+
+ private void processRpcResponseStatus(TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseStatusMsg responseMsg) {
+ UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB());
+ RpcStatus status = RpcStatus.valueOf(responseMsg.getStatus());
+ ToDeviceRpcRequestMetadata md = toDeviceRpcPendingMap.get(responseMsg.getRequestId());
+
+ if (md != null) {
+ JsonNode response = null;
+ if (status.equals(RpcStatus.DELIVERED)) {
+ if (md.getMsg().getMsg().isOneway()) {
+ toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
+ if (rpcSequential) {
+ systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(rpcId, null, null));
+ }
+ } else {
+ md.setDelivered(true);
+ }
+ } else if (status.equals(RpcStatus.TIMEOUT)) {
+ Integer maxRpcRetries = md.getMsg().getMsg().getRetries();
+ maxRpcRetries = maxRpcRetries == null ? systemContext.getMaxRpcRetries() : Math.min(maxRpcRetries, systemContext.getMaxRpcRetries());
+ if (maxRpcRetries <= md.getRetries()) {
+ toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
+ status = RpcStatus.FAILED;
+ response = JacksonUtil.newObjectNode().put("error", "There was a Timeout and all retry attempts have been exhausted. Retry attempts set: " + maxRpcRetries);
+ } else {
+ md.setRetries(md.getRetries() + 1);
+ }
+ }
+
+ if (md.getMsg().getMsg().isPersisted()) {
+ systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, response);
+ }
+ if (status != RpcStatus.SENT) {
+ sendNextPendingRequest(context);
+ }
+ } else {
+ log.info("[{}][{}] Rpc has already removed from pending map.", deviceId, rpcId);
+ }
+ }
+
+ private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (subscribeCmd.getUnsubscribe()) {
+ log.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
+ attributeSubscriptions.remove(sessionId);
+ } else {
+ SessionInfoMetaData sessionMD = sessions.get(sessionId);
+ if (sessionMD == null) {
+ sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId()));
+ }
+ sessionMD.setSubscribedToAttributes(true);
+ log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
+ attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo());
+ dumpSessions();
+ }
+ }
+
+ private UUID getSessionId(SessionInfoProto sessionInfo) {
+ return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
+ }
+
+ private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (subscribeCmd.getUnsubscribe()) {
+ log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);
+ rpcSubscriptions.remove(sessionId);
+ } else {
+ SessionInfoMetaData sessionMD = sessions.get(sessionId);
+ if (sessionMD == null) {
+ sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId()));
+ }
+ sessionMD.setSubscribedToRPC(true);
+ log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
+ rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo());
+ sendPendingRequests(context, sessionId, sessionInfo.getNodeId());
+ dumpSessions();
+ }
+ }
+
+ private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
+ UUID sessionId = getSessionId(sessionInfo);
+ Objects.requireNonNull(sessionId);
+ if (msg.getEvent() == SessionEvent.OPEN) {
+ if (sessions.containsKey(sessionId)) {
+ log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
+ return;
+ }
+ log.debug("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());
+
+ sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));
+ if (sessions.size() == 1) {
+ reportSessionOpen();
+ }
+ systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, System.currentTimeMillis());
+ dumpSessions();
+ } else if (msg.getEvent() == SessionEvent.CLOSED) {
+ log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
+ sessions.remove(sessionId);
+ attributeSubscriptions.remove(sessionId);
+ rpcSubscriptions.remove(sessionId);
+ if (sessions.isEmpty()) {
+ reportSessionClose();
+ }
+ dumpSessions();
+ }
+ }
+
+ private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) {
+ UUID sessionId = getSessionId(sessionInfoProto);
+ Objects.requireNonNull(sessionId);
+
+ SessionInfoMetaData sessionMD = sessions.get(sessionId);
+ if (sessionMD != null) {
+ sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
+ sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
+ sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription());
+ if (subscriptionInfo.getAttributeSubscription()) {
+ attributeSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
+ }
+ if (subscriptionInfo.getRpcSubscription()) {
+ rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
+ }
+ }
+ systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, subscriptionInfo.getLastActivityTime());
+ if (sessionMD != null) {
+ dumpSessions();
+ }
+ }
+
+ void processCredentialsUpdate(TbActorMsg msg) {
+ if (((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials().getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) {
+ sessions.forEach((k, v) -> {
+ notifyTransportAboutDeviceCredentialsUpdate(k, v, ((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials());
+ });
+ } else {
+ sessions.forEach((sessionId, sessionMd) -> notifyTransportAboutClosedSession(sessionId, sessionMd, "device credentials updated!"));
+ attributeSubscriptions.clear();
+ rpcSubscriptions.clear();
+ dumpSessions();
+
+ }
+ }
+
+ private void notifyTransportAboutClosedSessionMaxSessionsLimit(UUID sessionId, SessionInfoMetaData sessionMd) {
+ log.debug("remove eldest session (max concurrent sessions limit reached per device) sessionId [{}] sessionMd [{}]", sessionId, sessionMd);
+ notifyTransportAboutClosedSession(sessionId, sessionMd, "max concurrent sessions limit reached per device!");
+ }
+
+ private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) {
+ SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto
+ .newBuilder()
+ .setMessage(message).build();
+ ToTransportMsg msg = ToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setSessionCloseNotification(sessionCloseNotificationProto)
+ .build();
+ systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);
+ }
+
+ void notifyTransportAboutDeviceCredentialsUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {
+ ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder();
+ notification.addCredentialsId(deviceCredentials.getCredentialsId());
+ notification.addCredentialsValue(deviceCredentials.getCredentialsValue());
+ ToTransportMsg msg = ToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setToTransportUpdateCredentialsNotification(notification).build();
+ systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);
+ }
+
+ void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
+ this.deviceName = msg.getDeviceName();
+ this.deviceType = msg.getDeviceType();
+ this.defaultMetaData = new TbMsgMetaData();
+ this.defaultMetaData.putValue("deviceName", deviceName);
+ this.defaultMetaData.putValue("deviceType", deviceType);
+ }
+
+ void processEdgeUpdate(DeviceEdgeUpdateMsg msg) {
+ log.trace("[{}] Processing edge update {}", deviceId, msg);
+ this.edgeId = msg.getEdgeId();
+ }
+
+ private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
+ ToTransportMsg msg = ToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionInfo.getSessionIdMSB())
+ .setSessionIdLSB(sessionInfo.getSessionIdLSB())
+ .setGetAttributesResponse(responseMsg).build();
+ systemContext.getTbCoreToTransportService().process(sessionInfo.getNodeId(), msg);
+ }
+
+ private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {
+ ToTransportMsg msg = ToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setAttributeUpdateNotification(notificationMsg).build();
+ systemContext.getTbCoreToTransportService().process(nodeId, msg);
+ }
+
+ private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {
+ ToTransportMsg msg = ToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setToDeviceRequest(rpcMsg).build();
+ systemContext.getTbCoreToTransportService().process(nodeId, msg);
+ }
+
+ private void sendToTransport(ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {
+ ToTransportMsg msg = ToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setToServerResponse(rpcMsg).build();
+ systemContext.getTbCoreToTransportService().process(nodeId, msg);
+ }
+
+ private ListenableFuture saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
+ ObjectNode body = mapper.createObjectNode();
+ body.put("requestId", requestId);
+ body.put("requestUUID", msg.getId().toString());
+ body.put("oneway", msg.isOneway());
+ body.put("expirationTime", msg.getExpirationTime());
+ body.put("method", msg.getBody().getMethod());
+ body.put("params", msg.getBody().getParams());
+ body.put("persisted", msg.isPersisted());
+ body.put("retries", msg.getRetries());
+ body.put("additionalInfo", msg.getAdditionalInfo());
+
+ EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.DEVICE, EdgeEventActionType.RPC_CALL, deviceId, body);
+
+ return Futures.transform(systemContext.getEdgeEventService().saveAsync(edgeEvent), unused -> {
+ systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
+ return null;
+ }, systemContext.getDbCallbackExecutor());
+ }
+
+ private List toTsKvProtos(@Nullable List result) {
+ List clientAttributes;
+ if (result == null || result.isEmpty()) {
+ clientAttributes = Collections.emptyList();
+ } else {
+ clientAttributes = new ArrayList<>(result.size());
+ for (AttributeKvEntry attrEntry : result) {
+ clientAttributes.add(toTsKvProto(attrEntry));
+ }
+ }
+ return clientAttributes;
+ }
+
+ private TsKvProto toTsKvProto(AttributeKvEntry attrEntry) {
+ return TsKvProto.newBuilder().setTs(attrEntry.getLastUpdateTs())
+ .setKv(toKeyValueProto(attrEntry)).build();
+ }
+
+ private KeyValueProto toKeyValueProto(KvEntry kvEntry) {
+ KeyValueProto.Builder builder = KeyValueProto.newBuilder();
+ builder.setKey(kvEntry.getKey());
+ switch (kvEntry.getDataType()) {
+ case BOOLEAN:
+ builder.setType(KeyValueType.BOOLEAN_V);
+ builder.setBoolV(kvEntry.getBooleanValue().get());
+ break;
+ case DOUBLE:
+ builder.setType(KeyValueType.DOUBLE_V);
+ builder.setDoubleV(kvEntry.getDoubleValue().get());
+ break;
+ case LONG:
+ builder.setType(KeyValueType.LONG_V);
+ builder.setLongV(kvEntry.getLongValue().get());
+ break;
+ case STRING:
+ builder.setType(KeyValueType.STRING_V);
+ builder.setStringV(kvEntry.getStrValue().get());
+ break;
+ case JSON:
+ builder.setType(KeyValueType.JSON_V);
+ builder.setJsonV(kvEntry.getJsonValue().get());
+ break;
+ }
+ return builder.build();
+ }
+
+ void restoreSessions() {
+ if (systemContext.isLocalCacheType()) {
+ return;
+ }
+ log.debug("[{}] Restoring sessions from cache", deviceId);
+ DeviceSessionsCacheEntry sessionsDump;
+ try {
+ sessionsDump = systemContext.getDeviceSessionCacheService().get(deviceId);
+ } catch (Exception e) {
+ log.warn("[{}] Failed to decode device sessions from cache", deviceId);
+ return;
+ }
+ if (sessionsDump.getSessionsCount() == 0) {
+ log.debug("[{}] No session information found", deviceId);
+ return;
+ }
+ // TODO: Take latest max allowed sessions size from cache
+ for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
+ SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();
+ UUID sessionId = getSessionId(sessionInfoProto);
+ SessionInfo sessionInfo = new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId());
+ SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo();
+ SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime());
+ sessions.put(sessionId, sessionMD);
+ if (subInfo.getAttributeSubscription()) {
+ attributeSubscriptions.put(sessionId, sessionInfo);
+ sessionMD.setSubscribedToAttributes(true);
+ }
+ if (subInfo.getRpcSubscription()) {
+ rpcSubscriptions.put(sessionId, sessionInfo);
+ sessionMD.setSubscribedToRPC(true);
+ }
+ log.debug("[{}] Restored session: {}", deviceId, sessionMD);
+ }
+ log.debug("[{}] Restored sessions: {}, rpc subscriptions: {}, attribute subscriptions: {}", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
+ }
+
+ private void dumpSessions() {
+ if (systemContext.isLocalCacheType()) {
+ return;
+ }
+ log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
+ List sessionsList = new ArrayList<>(sessions.size());
+ sessions.forEach((uuid, sessionMD) -> {
+ if (sessionMD.getSessionInfo().getType() == SessionType.SYNC) {
+ return;
+ }
+ SessionInfo sessionInfo = sessionMD.getSessionInfo();
+ SubscriptionInfoProto subscriptionInfoProto = SubscriptionInfoProto.newBuilder()
+ .setLastActivityTime(sessionMD.getLastActivityTime())
+ .setAttributeSubscription(sessionMD.isSubscribedToAttributes())
+ .setRpcSubscription(sessionMD.isSubscribedToRPC()).build();
+ SessionInfoProto sessionInfoProto = SessionInfoProto.newBuilder()
+ .setSessionIdMSB(uuid.getMostSignificantBits())
+ .setSessionIdLSB(uuid.getLeastSignificantBits())
+ .setNodeId(sessionInfo.getNodeId()).build();
+ sessionsList.add(SessionSubscriptionInfoProto.newBuilder()
+ .setSessionInfo(sessionInfoProto)
+ .setSubscriptionInfo(subscriptionInfoProto).build());
+ log.debug("[{}] Dumping session: {}", deviceId, sessionMD);
+ });
+ systemContext.getDeviceSessionCacheService()
+ .put(deviceId, DeviceSessionsCacheEntry.newBuilder()
+ .addAllSessions(sessionsList).build());
+ }
+
+ void init(TbActorCtx ctx) {
+ PageLink pageLink = new PageLink(1024, 0, null, new SortOrder("createdTime"));
+ PageData pageData;
+ do {
+ pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(tenantId, deviceId, RpcStatus.QUEUED, pageLink);
+ pageData.getData().forEach(rpc -> {
+ ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class);
+ long timeout = rpc.getExpirationTime() - System.currentTimeMillis();
+ if (timeout <= 0) {
+ rpc.setStatus(RpcStatus.EXPIRED);
+ systemContext.getTbRpcService().save(tenantId, rpc);
+ } else {
+ registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout);
+ }
+ });
+ if (pageData.hasNext()) {
+ pageLink = pageLink.nextPageLink();
+ }
+ } while (pageData.hasNext());
+ }
+
+ void checkSessionsTimeout() {
+ final long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout();
+ List expiredIds = null;
+
+ for (Map.Entry kv : sessions.entrySet()) { //entry set are cached for stable sessions
+ if (kv.getValue().getLastActivityTime() < expTime) {
+ final UUID id = kv.getKey();
+ if (expiredIds == null) {
+ expiredIds = new ArrayList<>(1); //most of the expired sessions is a single event
+ }
+ expiredIds.add(id);
+ }
+ }
+
+ if (expiredIds != null) {
+ int removed = 0;
+ for (UUID id : expiredIds) {
+ final SessionInfoMetaData session = sessions.remove(id);
+ rpcSubscriptions.remove(id);
+ attributeSubscriptions.remove(id);
+ if (session != null) {
+ removed++;
+ notifyTransportAboutClosedSession(id, session, SESSION_TIMEOUT_MESSAGE);
+ }
+ }
+ if (removed != 0) {
+ dumpSessions();
+ }
+ }
+
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..01e4765190fad4f4dfa3f63afd1e21756763ed6f
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import lombok.Data;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Data
+public class SessionInfo {
+ private final SessionType type;
+ private final String nodeId;
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java
new file mode 100644
index 0000000000000000000000000000000000000000..a26fe90d659a4e988f8e928405d366c328154e7b
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import lombok.Data;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Data
+class SessionInfoMetaData {
+ private final SessionInfo sessionInfo;
+ private long lastActivityTime;
+ private boolean subscribedToAttributes;
+ private boolean subscribedToRPC;
+
+ SessionInfoMetaData(SessionInfo sessionInfo) {
+ this(sessionInfo, System.currentTimeMillis());
+ }
+
+ SessionInfoMetaData(SessionInfo sessionInfo, long lastActivityTime) {
+ this.sessionInfo = sessionInfo;
+ this.lastActivityTime = lastActivityTime;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..6815aee67508433c570909e9caf5dd12c629f6b8
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+/**
+ * Created by ashvayka on 29.10.18.
+ */
+public class SessionTimeoutCheckMsg implements TbActorMsg {
+
+ private static final SessionTimeoutCheckMsg INSTANCE = new SessionTimeoutCheckMsg();
+
+ private SessionTimeoutCheckMsg() {
+ }
+
+ public static SessionTimeoutCheckMsg instance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.SESSION_TIMEOUT_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..d003656874f4a6be467efff30548936057f0939d
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import lombok.Data;
+import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Data
+public class ToDeviceRpcRequestMetadata {
+ private final ToDeviceRpcRequestActorMsg msg;
+ private final boolean sent;
+ private int retries;
+ private boolean delivered;
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0a68c178d75157729613752248ecd312c122a68
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import lombok.Data;
+import org.thingsboard.server.gen.transport.TransportProtos;
+
+import java.util.UUID;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Data
+public class ToServerRpcRequestMetadata {
+ private final UUID sessionId;
+ private final TransportProtos.SessionType type;
+ private final String nodeId;
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba1c4e9796cdf0372ba6fe6723b7e0488a260098
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -0,0 +1,823 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import io.netty.channel.EventLoopGroup;
+import lombok.extern.slf4j.Slf4j;
+import org.bouncycastle.util.Arrays;
+import org.thingsboard.common.util.JacksonUtil;
+import org.thingsboard.common.util.ListeningExecutor;
+import org.thingsboard.rule.engine.api.MailService;
+import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
+import org.thingsboard.rule.engine.api.RuleEngineApiUsageStateService;
+import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache;
+import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
+import org.thingsboard.rule.engine.api.RuleEngineRpcService;
+import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
+import org.thingsboard.rule.engine.api.ScriptEngine;
+import org.thingsboard.rule.engine.api.SmsService;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.rule.engine.api.TbRelationTypes;
+import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
+import org.thingsboard.rule.engine.util.TenantIdLoader;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.cluster.TbClusterService;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.HasRuleEngineProfile;
+import org.thingsboard.server.common.data.StringUtils;
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.asset.AssetProfile;
+import org.thingsboard.server.common.data.id.AssetId;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.EdgeId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.page.PageData;
+import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.data.rule.RuleNodeState;
+import org.thingsboard.server.common.data.script.ScriptLanguage;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.TbMsgProcessingStackItem;
+import org.thingsboard.server.common.msg.queue.ServiceType;
+import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
+import org.thingsboard.server.dao.asset.AssetProfileService;
+import org.thingsboard.server.dao.asset.AssetService;
+import org.thingsboard.server.dao.attributes.AttributesService;
+import org.thingsboard.server.dao.cassandra.CassandraCluster;
+import org.thingsboard.server.dao.customer.CustomerService;
+import org.thingsboard.server.dao.dashboard.DashboardService;
+import org.thingsboard.server.dao.device.DeviceCredentialsService;
+import org.thingsboard.server.dao.device.DeviceProfileService;
+import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.edge.EdgeEventService;
+import org.thingsboard.server.dao.edge.EdgeService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
+import org.thingsboard.server.dao.nosql.CassandraStatementTask;
+import org.thingsboard.server.dao.nosql.TbResultSetFuture;
+import org.thingsboard.server.dao.ota.OtaPackageService;
+import org.thingsboard.server.dao.queue.QueueService;
+import org.thingsboard.server.dao.relation.RelationService;
+import org.thingsboard.server.dao.resource.ResourceService;
+import org.thingsboard.server.dao.rule.RuleChainService;
+import org.thingsboard.server.dao.tenant.TenantService;
+import org.thingsboard.server.dao.timeseries.TimeseriesService;
+import org.thingsboard.server.dao.user.UserService;
+import org.thingsboard.server.dao.widget.WidgetTypeService;
+import org.thingsboard.server.dao.widget.WidgetsBundleService;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.queue.TbQueueCallback;
+import org.thingsboard.server.queue.TbQueueMsgMetadata;
+import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
+import org.thingsboard.server.service.script.RuleNodeTbelScriptEngine;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@Slf4j
+class DefaultTbContext implements TbContext {
+
+ public final static ObjectMapper mapper = new ObjectMapper();
+
+ private final ActorSystemContext mainCtx;
+ private final String ruleChainName;
+ private final RuleNodeCtx nodeCtx;
+
+ public DefaultTbContext(ActorSystemContext mainCtx, String ruleChainName, RuleNodeCtx nodeCtx) {
+ this.mainCtx = mainCtx;
+ this.ruleChainName = ruleChainName;
+ this.nodeCtx = nodeCtx;
+ }
+
+ @Override
+ public void tellSuccess(TbMsg msg) {
+ tellNext(msg, Collections.singleton(TbRelationTypes.SUCCESS), null);
+ }
+
+ @Override
+ public void tellNext(TbMsg msg, String relationType) {
+ tellNext(msg, Collections.singleton(relationType), null);
+ }
+
+ @Override
+ public void tellNext(TbMsg msg, Set relationTypes) {
+ tellNext(msg, relationTypes, null);
+ }
+
+ private void tellNext(TbMsg msg, Set relationTypes, Throwable th) {
+ if (nodeCtx.getSelf().isDebugMode()) {
+ relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
+ }
+ msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId());
+ nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null));
+ }
+
+ @Override
+ public void tellSelf(TbMsg msg, long delayMs) {
+ //TODO: add persistence layer
+ scheduleMsgWithDelay(new RuleNodeToSelfMsg(this, msg), delayMs, nodeCtx.getSelfActor());
+ }
+
+ @Override
+ public void input(TbMsg msg, RuleChainId ruleChainId) {
+ msg.pushToStack(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
+ nodeCtx.getChainActor().tell(new RuleChainInputMsg(ruleChainId, msg));
+ }
+
+ @Override
+ public void output(TbMsg msg, String relationType) {
+ TbMsgProcessingStackItem item = msg.popFormStack();
+ if (item == null) {
+ ack(msg);
+ } else {
+ if (nodeCtx.getSelf().isDebugMode()) {
+ mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType);
+ }
+ nodeCtx.getChainActor().tell(new RuleChainOutputMsg(item.getRuleChainId(), item.getRuleNodeId(), relationType, msg));
+ }
+ }
+
+ @Override
+ public void enqueue(TbMsg tbMsg, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());
+ enqueue(tpi, tbMsg, onFailure, onSuccess);
+ }
+
+ @Override
+ public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
+ enqueue(tpi, tbMsg, onFailure, onSuccess);
+ }
+
+ private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer onFailure, Runnable onSuccess) {
+ if (!tbMsg.isValid()) {
+ log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg);
+ if (onFailure != null) {
+ onFailure.accept(new IllegalArgumentException("Source message is no longer valid!"));
+ }
+ return;
+ }
+ TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder()
+ .setTenantIdMSB(getTenantId().getId().getMostSignificantBits())
+ .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())
+ .setTbMsg(TbMsg.toByteString(tbMsg)).build();
+ if (nodeCtx.getSelf().isDebugMode()) {
+ mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, "To Root Rule Chain");
+ }
+ mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, new SimpleTbQueueCallback(onSuccess, onFailure));
+ }
+
+ @Override
+ public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg);
+ enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbRelationTypes.FAILURE), failureMessage, null, null);
+ }
+
+ @Override
+ public void enqueueForTellNext(TbMsg tbMsg, String relationType) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg);
+ enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, null, null);
+ }
+
+ @Override
+ public void enqueueForTellNext(TbMsg tbMsg, Set relationTypes) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg);
+ enqueueForTellNext(tpi, tbMsg, relationTypes, null, null, null);
+ }
+
+ @Override
+ public void enqueueForTellNext(TbMsg tbMsg, String relationType, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg);
+ enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
+ }
+
+ @Override
+ public void enqueueForTellNext(TbMsg tbMsg, Set relationTypes, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg);
+ enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure);
+ }
+
+ @Override
+ public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
+ enqueueForTellNext(tpi, queueName, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
+ }
+
+ @Override
+ public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set relationTypes, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
+ enqueueForTellNext(tpi, queueName, tbMsg, relationTypes, null, onSuccess, onFailure);
+ }
+
+ private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) {
+ return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
+ }
+
+ private TopicPartitionInfo resolvePartition(TbMsg tbMsg) {
+ return resolvePartition(tbMsg, tbMsg.getQueueName());
+ }
+
+ private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) {
+ enqueueForTellNext(tpi, source.getQueueName(), source, relationTypes, failureMessage, onSuccess, onFailure);
+ }
+
+ private void enqueueForTellNext(TopicPartitionInfo tpi, String queueName, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) {
+ if (!source.isValid()) {
+ log.trace("[{}] Skip invalid message: {}", getTenantId(), source);
+ if (onFailure != null) {
+ onFailure.accept(new IllegalArgumentException("Source message is no longer valid!"));
+ }
+ return;
+ }
+ RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId();
+ RuleNodeId ruleNodeId = nodeCtx.getSelf().getId();
+ TbMsg tbMsg = TbMsg.newMsg(source, queueName, ruleChainId, ruleNodeId);
+ TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder()
+ .setTenantIdMSB(getTenantId().getId().getMostSignificantBits())
+ .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())
+ .setTbMsg(TbMsg.toByteString(tbMsg))
+ .addAllRelationTypes(relationTypes);
+ if (failureMessage != null) {
+ msg.setFailureMessage(failureMessage);
+ }
+ if (nodeCtx.getSelf().isDebugMode()) {
+ relationTypes.forEach(relationType ->
+ mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, relationType, null, failureMessage));
+ }
+ mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg.build(), new SimpleTbQueueCallback(onSuccess, onFailure));
+ }
+
+ @Override
+ public void ack(TbMsg tbMsg) {
+ if (nodeCtx.getSelf().isDebugMode()) {
+ mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, "ACK", null);
+ }
+ tbMsg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId());
+ tbMsg.getCallback().onSuccess();
+ }
+
+ @Override
+ public boolean isLocalEntity(EntityId entityId) {
+ return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), entityId).isMyPartition();
+ }
+
+ private void scheduleMsgWithDelay(TbActorMsg msg, long delayInMs, TbActorRef target) {
+ mainCtx.scheduleMsgWithDelay(target, msg, delayInMs);
+ }
+
+ @Override
+ public void tellFailure(TbMsg msg, Throwable th) {
+ if (nodeCtx.getSelf().isDebugMode()) {
+ mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th);
+ }
+ String failureMessage;
+ if (th != null) {
+ if (!StringUtils.isEmpty(th.getMessage())) {
+ failureMessage = th.getMessage();
+ } else {
+ failureMessage = th.getClass().getSimpleName();
+ }
+ } else {
+ failureMessage = null;
+ }
+ nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(),
+ nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE),
+ msg, failureMessage));
+ }
+
+ public void updateSelf(RuleNode self) {
+ nodeCtx.setSelf(self);
+ }
+
+ @Override
+ public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
+ return newMsg(queueName, type, originator, null, metaData, data);
+ }
+
+ @Override
+ public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
+ return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
+ }
+
+ @Override
+ public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
+ return TbMsg.transformMsg(origMsg, type, originator, metaData, data);
+ }
+
+ public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) {
+ return entityActionMsg(customer, customer.getId(), ruleNodeId, DataConstants.ENTITY_CREATED);
+ }
+
+ public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) {
+ DeviceProfile deviceProfile = null;
+ if (device.getDeviceProfileId() != null) {
+ deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId());
+ }
+ return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, deviceProfile);
+ }
+
+ public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) {
+ AssetProfile assetProfile = null;
+ if (asset.getAssetProfileId() != null) {
+ assetProfile = mainCtx.getAssetProfileCache().find(asset.getAssetProfileId());
+ }
+ return entityActionMsg(asset, asset.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, assetProfile);
+ }
+
+ public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) {
+ HasRuleEngineProfile profile = null;
+ if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) {
+ DeviceId deviceId = new DeviceId(alarm.getOriginator().getId());
+ profile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
+ } else if (EntityType.ASSET.equals(alarm.getOriginator().getEntityType())) {
+ AssetId assetId = new AssetId(alarm.getOriginator().getId());
+ profile = mainCtx.getAssetProfileCache().get(getTenantId(), assetId);
+ }
+ return entityActionMsg(alarm, alarm.getOriginator(), ruleNodeId, action, profile);
+ }
+
+ public TbMsg attributesUpdatedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List attributes) {
+ ObjectNode entityNode = JacksonUtil.newObjectNode();
+ if (attributes != null) {
+ attributes.forEach(attributeKvEntry -> JacksonUtil.addKvEntry(entityNode, attributeKvEntry));
+ }
+ return attributesActionMsg(originator, ruleNodeId, scope, DataConstants.ATTRIBUTES_UPDATED, JacksonUtil.toString(entityNode));
+ }
+
+ public TbMsg attributesDeletedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List keys) {
+ ObjectNode entityNode = JacksonUtil.newObjectNode();
+ ArrayNode attrsArrayNode = entityNode.putArray("attributes");
+ if (keys != null) {
+ keys.forEach(attrsArrayNode::add);
+ }
+ return attributesActionMsg(originator, ruleNodeId, scope, DataConstants.ATTRIBUTES_DELETED, JacksonUtil.toString(entityNode));
+ }
+
+ private TbMsg attributesActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, String action, String msgData) {
+ TbMsgMetaData tbMsgMetaData = getActionMetaData(ruleNodeId);
+ tbMsgMetaData.putValue("scope", scope);
+ HasRuleEngineProfile profile = null;
+ if (EntityType.DEVICE.equals(originator.getEntityType())) {
+ DeviceId deviceId = new DeviceId(originator.getId());
+ profile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
+ } else if (EntityType.ASSET.equals(originator.getEntityType())) {
+ AssetId assetId = new AssetId(originator.getId());
+ profile = mainCtx.getAssetProfileCache().get(getTenantId(), assetId);
+ }
+ return entityActionMsg(originator, tbMsgMetaData, msgData, action, profile);
+ }
+
+ @Override
+ public void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId) {
+ mainCtx.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
+ }
+
+ public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) {
+ return entityActionMsg(entity, id, ruleNodeId, action, null);
+ }
+
+ public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, K profile) {
+ try {
+ return entityActionMsg(id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), action, profile);
+ } catch (JsonProcessingException | IllegalArgumentException e) {
+ throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e);
+ }
+ }
+
+ private TbMsg entityActionMsg(I id, TbMsgMetaData msgMetaData, String msgData, String action, K profile) {
+ String defaultQueueName = null;
+ RuleChainId defaultRuleChainId = null;
+ if (profile != null) {
+ defaultQueueName = profile.getDefaultQueueName();
+ defaultRuleChainId = profile.getDefaultRuleChainId();
+ }
+ return TbMsg.newMsg(defaultQueueName, action, id, msgMetaData, msgData, defaultRuleChainId, null);
+ }
+
+ @Override
+ public RuleNodeId getSelfId() {
+ return nodeCtx.getSelf().getId();
+ }
+
+ @Override
+ public RuleNode getSelf() {
+ return nodeCtx.getSelf();
+ }
+
+ @Override
+ public String getRuleChainName() {
+ return ruleChainName;
+ }
+
+ @Override
+ public TenantId getTenantId() {
+ return nodeCtx.getTenantId();
+ }
+
+ @Override
+ public ListeningExecutor getMailExecutor() {
+ return mainCtx.getMailExecutor();
+ }
+
+ @Override
+ public ListeningExecutor getSmsExecutor() {
+ return mainCtx.getSmsExecutor();
+ }
+
+ @Override
+ public ListeningExecutor getDbCallbackExecutor() {
+ return mainCtx.getDbCallbackExecutor();
+ }
+
+ @Override
+ public ListeningExecutor getExternalCallExecutor() {
+ return mainCtx.getExternalCallExecutorService();
+ }
+
+ @Override
+ @Deprecated
+ public ScriptEngine createJsScriptEngine(String script, String... argNames) {
+ return new RuleNodeJsScriptEngine(getTenantId(), mainCtx.getJsInvokeService(), script, argNames);
+ }
+
+ private ScriptEngine createTbelScriptEngine(String script, String... argNames) {
+ if (mainCtx.getTbelInvokeService() == null) {
+ throw new RuntimeException("TBEL execution is disabled!");
+ }
+ return new RuleNodeTbelScriptEngine(getTenantId(), mainCtx.getTbelInvokeService(), script, argNames);
+ }
+
+ @Override
+ public ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String script, String... argNames) {
+ if (scriptLang == null) {
+ scriptLang = ScriptLanguage.JS;
+ }
+ if (StringUtils.isBlank(script)) {
+ throw new RuntimeException(scriptLang.name() + " script is blank!");
+ }
+ switch (scriptLang) {
+ case JS:
+ return createJsScriptEngine(script, argNames);
+ case TBEL:
+ if (Arrays.isNullOrEmpty(argNames)) {
+ return createTbelScriptEngine(script, "msg", "metadata", "msgType");
+ } else {
+ return createTbelScriptEngine(script, argNames);
+ }
+ default:
+ throw new RuntimeException("Unsupported script language: " + scriptLang.name());
+ }
+ }
+
+ @Override
+ public void logJsEvalRequest() {
+ if (mainCtx.isStatisticsEnabled()) {
+ mainCtx.getJsInvokeStats().incrementRequests();
+ }
+ }
+
+ @Override
+ public void logJsEvalResponse() {
+ if (mainCtx.isStatisticsEnabled()) {
+ mainCtx.getJsInvokeStats().incrementResponses();
+ }
+ }
+
+ @Override
+ public void logJsEvalFailure() {
+ if (mainCtx.isStatisticsEnabled()) {
+ mainCtx.getJsInvokeStats().incrementFailures();
+ }
+ }
+
+ @Override
+ public String getServiceId() {
+ return mainCtx.getServiceInfoProvider().getServiceId();
+ }
+
+ @Override
+ public AttributesService getAttributesService() {
+ return mainCtx.getAttributesService();
+ }
+
+ @Override
+ public CustomerService getCustomerService() {
+ return mainCtx.getCustomerService();
+ }
+
+ @Override
+ public TenantService getTenantService() {
+ return mainCtx.getTenantService();
+ }
+
+ @Override
+ public UserService getUserService() {
+ return mainCtx.getUserService();
+ }
+
+ @Override
+ public AssetService getAssetService() {
+ return mainCtx.getAssetService();
+ }
+
+ @Override
+ public DeviceService getDeviceService() {
+ return mainCtx.getDeviceService();
+ }
+
+ @Override
+ public DeviceProfileService getDeviceProfileService() {
+ return mainCtx.getDeviceProfileService();
+ }
+
+ @Override
+ public AssetProfileService getAssetProfileService() {
+ return mainCtx.getAssetProfileService();
+ }
+
+ @Override
+ public DeviceCredentialsService getDeviceCredentialsService() {
+ return mainCtx.getDeviceCredentialsService();
+ }
+
+ @Override
+ public TbClusterService getClusterService() {
+ return mainCtx.getClusterService();
+ }
+
+ @Override
+ public DashboardService getDashboardService() {
+ return mainCtx.getDashboardService();
+ }
+
+ @Override
+ public RuleEngineAlarmService getAlarmService() {
+ return mainCtx.getAlarmService();
+ }
+
+ @Override
+ public RuleChainService getRuleChainService() {
+ return mainCtx.getRuleChainService();
+ }
+
+ @Override
+ public TimeseriesService getTimeseriesService() {
+ return mainCtx.getTsService();
+ }
+
+ @Override
+ public RuleEngineTelemetryService getTelemetryService() {
+ return mainCtx.getTsSubService();
+ }
+
+ @Override
+ public RelationService getRelationService() {
+ return mainCtx.getRelationService();
+ }
+
+ @Override
+ public EntityViewService getEntityViewService() {
+ return mainCtx.getEntityViewService();
+ }
+
+ @Override
+ public ResourceService getResourceService() {
+ return mainCtx.getResourceService();
+ }
+
+ @Override
+ public OtaPackageService getOtaPackageService() {
+ return mainCtx.getOtaPackageService();
+ }
+
+ @Override
+ public RuleEngineDeviceProfileCache getDeviceProfileCache() {
+ return mainCtx.getDeviceProfileCache();
+ }
+
+ @Override
+ public RuleEngineAssetProfileCache getAssetProfileCache() {
+ return mainCtx.getAssetProfileCache();
+ }
+
+ @Override
+ public EdgeService getEdgeService() {
+ return mainCtx.getEdgeService();
+ }
+
+ @Override
+ public EdgeEventService getEdgeEventService() {
+ return mainCtx.getEdgeEventService();
+ }
+
+ @Override
+ public QueueService getQueueService() {
+ return mainCtx.getQueueService();
+ }
+
+ @Override
+ public EventLoopGroup getSharedEventLoop() {
+ return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
+ }
+
+ @Override
+ public MailService getMailService(boolean isSystem) {
+ if (!isSystem || mainCtx.isAllowSystemMailService()) {
+ return mainCtx.getMailService();
+ } else {
+ throw new RuntimeException("Access to System Mail Service is forbidden!");
+ }
+ }
+
+ @Override
+ public SmsService getSmsService() {
+ if (mainCtx.isAllowSystemSmsService()) {
+ return mainCtx.getSmsService();
+ } else {
+ throw new RuntimeException("Access to System SMS Service is forbidden!");
+ }
+ }
+
+ @Override
+ public SmsSenderFactory getSmsSenderFactory() {
+ return mainCtx.getSmsSenderFactory();
+ }
+
+ @Override
+ public RuleEngineRpcService getRpcService() {
+ return mainCtx.getTbRuleEngineDeviceRpcService();
+ }
+
+ @Override
+ public CassandraCluster getCassandraCluster() {
+ return mainCtx.getCassandraCluster();
+ }
+
+ @Override
+ public TbResultSetFuture submitCassandraReadTask(CassandraStatementTask task) {
+ return mainCtx.getCassandraBufferedRateReadExecutor().submit(task);
+ }
+
+ @Override
+ public TbResultSetFuture submitCassandraWriteTask(CassandraStatementTask task) {
+ return mainCtx.getCassandraBufferedRateWriteExecutor().submit(task);
+ }
+
+ @Override
+ public PageData findRuleNodeStates(PageLink pageLink) {
+ if (log.isDebugEnabled()) {
+ log.debug("[{}][{}] Fetch Rule Node States.", getTenantId(), getSelfId());
+ }
+ return mainCtx.getRuleNodeStateService().findByRuleNodeId(getTenantId(), getSelfId(), pageLink);
+ }
+
+ @Override
+ public RuleNodeState findRuleNodeStateForEntity(EntityId entityId) {
+ if (log.isDebugEnabled()) {
+ log.debug("[{}][{}][{}] Fetch Rule Node State for entity.", getTenantId(), getSelfId(), entityId);
+ }
+ return mainCtx.getRuleNodeStateService().findByRuleNodeIdAndEntityId(getTenantId(), getSelfId(), entityId);
+ }
+
+ @Override
+ public RuleNodeState saveRuleNodeState(RuleNodeState state) {
+ if (log.isDebugEnabled()) {
+ log.debug("[{}][{}][{}] Persist Rule Node State for entity: {}", getTenantId(), getSelfId(), state.getEntityId(), state.getStateData());
+ }
+ state.setRuleNodeId(getSelfId());
+ return mainCtx.getRuleNodeStateService().save(getTenantId(), state);
+ }
+
+ @Override
+ public void clearRuleNodeStates() {
+ if (log.isDebugEnabled()) {
+ log.debug("[{}][{}] Going to clear rule node states", getTenantId(), getSelfId());
+ }
+ mainCtx.getRuleNodeStateService().removeByRuleNodeId(getTenantId(), getSelfId());
+ }
+
+ @Override
+ public void removeRuleNodeStateForEntity(EntityId entityId) {
+ if (log.isDebugEnabled()) {
+ log.debug("[{}][{}][{}] Remove Rule Node State for entity.", getTenantId(), getSelfId(), entityId);
+ }
+ mainCtx.getRuleNodeStateService().removeByRuleNodeIdAndEntityId(getTenantId(), getSelfId(), entityId);
+ }
+
+ @Override
+ public void addTenantProfileListener(Consumer listener) {
+ mainCtx.getTenantProfileCache().addListener(getTenantId(), getSelfId(), listener);
+ }
+
+ @Override
+ public void addDeviceProfileListeners(Consumer profileListener, BiConsumer deviceListener) {
+ mainCtx.getDeviceProfileCache().addListener(getTenantId(), getSelfId(), profileListener, deviceListener);
+ }
+
+ @Override
+ public void addAssetProfileListeners(Consumer profileListener, BiConsumer assetListener) {
+ mainCtx.getAssetProfileCache().addListener(getTenantId(), getSelfId(), profileListener, assetListener);
+ }
+
+ @Override
+ public void removeListeners() {
+ mainCtx.getDeviceProfileCache().removeListener(getTenantId(), getSelfId());
+ mainCtx.getAssetProfileCache().removeListener(getTenantId(), getSelfId());
+ mainCtx.getTenantProfileCache().removeListener(getTenantId(), getSelfId());
+ }
+
+ @Override
+ public TenantProfile getTenantProfile() {
+ return mainCtx.getTenantProfileCache().get(getTenantId());
+ }
+
+ @Override
+ public WidgetsBundleService getWidgetBundleService() {
+ return mainCtx.getWidgetsBundleService();
+ }
+
+ @Override
+ public WidgetTypeService getWidgetTypeService() {
+ return mainCtx.getWidgetTypeService();
+ }
+
+ @Override
+ public RuleEngineApiUsageStateService getRuleEngineApiUsageStateService() {
+ return mainCtx.getApiUsageStateService();
+ }
+
+ private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) {
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("ruleNodeId", ruleNodeId.toString());
+ return metaData;
+ }
+
+ @Override
+ public void checkTenantEntity(EntityId entityId) {
+ if (!this.getTenantId().equals(TenantIdLoader.findTenantId(this, entityId))) {
+ throw new RuntimeException("Entity with id: '" + entityId + "' specified in the configuration doesn't belong to the current tenant.");
+ }
+ }
+
+ private class SimpleTbQueueCallback implements TbQueueCallback {
+ private final Runnable onSuccess;
+ private final Consumer onFailure;
+
+ public SimpleTbQueueCallback(Runnable onSuccess, Consumer onFailure) {
+ this.onSuccess = onSuccess;
+ this.onFailure = onFailure;
+ }
+
+ @Override
+ public void onSuccess(TbQueueMsgMetadata metadata) {
+ if (onSuccess != null) {
+ onSuccess.run();
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (onFailure != null) {
+ onFailure.accept(t);
+ } else {
+ log.debug("[{}] Failed to put item into queue", nodeCtx.getTenantId(), t);
+ }
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff9e21dedd7cb4038612cf462540f11bfe16d14c
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorId;
+import org.thingsboard.server.actors.TbEntityActorId;
+import org.thingsboard.server.actors.service.ComponentActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
+
+public class RuleChainActor extends ComponentActor {
+
+ private final RuleChain ruleChain;
+
+ private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChain ruleChain) {
+ super(systemContext, tenantId, ruleChain.getId());
+ this.ruleChain = ruleChain;
+ }
+
+ @Override
+ protected RuleChainActorMessageProcessor createProcessor(TbActorCtx ctx) {
+ return new RuleChainActorMessageProcessor(tenantId, ruleChain, systemContext,
+ ctx.getParentRef(), ctx);
+ }
+
+ @Override
+ protected boolean doProcess(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case COMPONENT_LIFE_CYCLE_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case QUEUE_TO_RULE_ENGINE_MSG:
+ processor.onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg);
+ break;
+ case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
+ processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
+ break;
+ case RULE_CHAIN_TO_RULE_CHAIN_MSG:
+ processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg);
+ break;
+ case RULE_CHAIN_INPUT_MSG:
+ processor.onRuleChainInputMsg((RuleChainInputMsg) msg);
+ break;
+ case RULE_CHAIN_OUTPUT_MSG:
+ processor.onRuleChainOutputMsg((RuleChainOutputMsg) msg);
+ break;
+ case PARTITION_CHANGE_MSG:
+ processor.onPartitionChangeMsg((PartitionChangeMsg) msg);
+ break;
+ case STATS_PERSIST_TICK_MSG:
+ onStatsPersistTick(id);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public static class ActorCreator extends ContextBasedCreator {
+ private static final long serialVersionUID = 1L;
+
+ private final TenantId tenantId;
+ private final RuleChain ruleChain;
+
+ public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChain ruleChain) {
+ super(context);
+ this.tenantId = tenantId;
+ this.ruleChain = ruleChain;
+ }
+
+ @Override
+ public TbActorId createActorId() {
+ return new TbEntityActorId(ruleChain.getId());
+ }
+
+ @Override
+ public TbActor createActor() {
+ return new RuleChainActor(context, tenantId, ruleChain);
+ }
+ }
+
+ @Override
+ protected long getErrorPersistFrequency() {
+ return systemContext.getRuleChainErrorPersistFrequency();
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..279d750f6e4cb5dd4797debdcc891eda478f5e02
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
@@ -0,0 +1,404 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.rule.engine.api.TbRelationTypes;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.actors.TbEntityActorId;
+import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.data.rule.RuleChainType;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.plugin.RuleNodeUpdatedMsg;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
+import org.thingsboard.server.common.msg.queue.RuleEngineException;
+import org.thingsboard.server.common.msg.queue.RuleNodeException;
+import org.thingsboard.server.common.msg.queue.ServiceType;
+import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
+import org.thingsboard.server.common.stats.TbApiUsageReportClient;
+import org.thingsboard.server.dao.rule.RuleChainService;
+import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
+import org.thingsboard.server.queue.TbQueueCallback;
+import org.thingsboard.server.queue.common.MultipleTbQueueTbMsgCallbackWrapper;
+import org.thingsboard.server.queue.common.TbQueueTbMsgCallbackWrapper;
+import org.thingsboard.server.cluster.TbClusterService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Slf4j
+public class RuleChainActorMessageProcessor extends ComponentMsgProcessor {
+
+ private static final String NA_RELATION_TYPE = "";
+ private final TbActorRef parent;
+ private final TbActorRef self;
+ private final Map nodeActors;
+ private final Map> nodeRoutes;
+ private final RuleChainService service;
+ private final TbClusterService clusterService;
+ private final TbApiUsageReportClient apiUsageClient;
+ private String ruleChainName;
+
+ private RuleNodeId firstId;
+ private RuleNodeCtx firstNode;
+ private boolean started;
+
+ RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext, TbActorRef parent, TbActorRef self) {
+ super(systemContext, tenantId, ruleChain.getId());
+ this.apiUsageClient = systemContext.getApiUsageClient();
+ this.ruleChainName = ruleChain.getName();
+ this.parent = parent;
+ this.self = self;
+ this.nodeActors = new HashMap<>();
+ this.nodeRoutes = new HashMap<>();
+ this.service = systemContext.getRuleChainService();
+ this.clusterService = systemContext.getClusterService();
+ }
+
+ @Override
+ public String getComponentName() {
+ return null;
+ }
+
+ @Override
+ public void start(TbActorCtx context) {
+ if (!started) {
+ RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
+ if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
+ List ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
+ log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
+ // Creating and starting the actors;
+ for (RuleNode ruleNode : ruleNodeList) {
+ log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
+ TbActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
+ nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
+ }
+ initRoutes(ruleChain, ruleNodeList);
+ started = true;
+ }
+ } else {
+ onUpdate(context);
+ }
+ }
+
+ @Override
+ public void onUpdate(TbActorCtx context) {
+ RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
+ if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
+ ruleChainName = ruleChain.getName();
+ List ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
+ log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
+ for (RuleNode ruleNode : ruleNodeList) {
+ RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
+ if (existing == null) {
+ log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
+ TbActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
+ nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
+ } else {
+ log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
+ existing.setSelf(ruleNode);
+ existing.getSelfActor().tellWithHighPriority(new RuleNodeUpdatedMsg(tenantId, existing.getSelf().getId()));
+ }
+ }
+
+ Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
+ List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
+ removedRules.forEach(ruleNodeId -> {
+ log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);
+ RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
+ removed.getSelfActor().tellWithHighPriority(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED));
+ });
+
+ initRoutes(ruleChain, ruleNodeList);
+ }
+ }
+
+ @Override
+ public void stop(TbActorCtx ctx) {
+ log.trace("[{}][{}] Stopping rule chain with {} nodes", tenantId, entityId, nodeActors.size());
+ nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).map(TbActorRef::getActorId).forEach(ctx::stop);
+ nodeActors.clear();
+ nodeRoutes.clear();
+ started = false;
+ }
+
+ @Override
+ public void onPartitionChangeMsg(PartitionChangeMsg msg) {
+ nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(actorRef -> actorRef.tellWithHighPriority(msg));
+ }
+
+ private TbActorRef createRuleNodeActor(TbActorCtx ctx, RuleNode ruleNode) {
+ return ctx.getOrCreateChildActor(new TbEntityActorId(ruleNode.getId()),
+ () -> DefaultActorService.RULE_DISPATCHER_NAME,
+ () -> new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleChainName, ruleNode.getId()));
+ }
+
+ private void initRoutes(RuleChain ruleChain, List ruleNodeList) {
+ nodeRoutes.clear();
+ // Populating the routes map;
+ for (RuleNode ruleNode : ruleNodeList) {
+ List relations = service.getRuleNodeRelations(TenantId.SYS_TENANT_ID, ruleNode.getId());
+ log.trace("[{}][{}][{}] Processing rule node relations [{}]", tenantId, entityId, ruleNode.getId(), relations.size());
+ if (relations.size() == 0) {
+ nodeRoutes.put(ruleNode.getId(), Collections.emptyList());
+ } else {
+ for (EntityRelation relation : relations) {
+ log.trace("[{}][{}][{}] Processing rule node relation [{}]", tenantId, entityId, ruleNode.getId(), relation.getTo());
+ if (relation.getTo().getEntityType() == EntityType.RULE_NODE) {
+ RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId()));
+ if (ruleNodeCtx == null) {
+ throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]");
+ }
+ }
+ nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>())
+ .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType()));
+ }
+ }
+ }
+
+ firstId = ruleChain.getFirstRuleNodeId();
+ firstNode = nodeActors.get(firstId);
+ state = ComponentLifecycleState.ACTIVE;
+ }
+
+ void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) {
+ TbMsg msg = envelope.getMsg();
+ if (!checkMsgValid(msg)) {
+ return;
+ }
+ log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg);
+ if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) {
+ onTellNext(msg, true);
+ } else {
+ onTellNext(msg, envelope.getMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage());
+ }
+ }
+
+ private void onTellNext(TbMsg msg, boolean useRuleNodeIdFromMsg) {
+ try {
+ checkComponentStateActive(msg);
+ RuleNodeId targetId = useRuleNodeIdFromMsg ? msg.getRuleNodeId() : null;
+ RuleNodeCtx targetCtx;
+ if (targetId == null) {
+ targetCtx = firstNode;
+ msg = msg.copyWithRuleChainId(entityId);
+ } else {
+ targetCtx = nodeActors.get(targetId);
+ }
+ if (targetCtx != null) {
+ log.trace("[{}][{}] Pushing message to target rule node", entityId, targetId);
+ pushMsgToNode(targetCtx, msg, NA_RELATION_TYPE);
+ } else {
+ log.trace("[{}][{}] Rule node does not exist. Probably old message", entityId, targetId);
+ msg.getCallback().onSuccess();
+ }
+ } catch (RuleNodeException rne) {
+ msg.getCallback().onFailure(rne);
+ } catch (Exception e) {
+ msg.getCallback().onFailure(new RuleEngineException(e.getMessage()));
+ }
+ }
+
+ public void onRuleChainInputMsg(RuleChainInputMsg envelope) {
+ var msg = envelope.getMsg();
+ if (!checkMsgValid(msg)) {
+ return;
+ }
+ if (entityId.equals(envelope.getRuleChainId())) {
+ onTellNext(envelope.getMsg(), false);
+ } else {
+ parent.tell(envelope);
+ }
+ }
+
+ public void onRuleChainOutputMsg(RuleChainOutputMsg envelope) {
+ var msg = envelope.getMsg();
+ if (!checkMsgValid(msg)) {
+ return;
+ }
+ if (entityId.equals(envelope.getRuleChainId())) {
+ var originatorNodeId = envelope.getTargetRuleNodeId();
+ RuleNodeCtx ruleNodeCtx = nodeActors.get(originatorNodeId);
+ if (ruleNodeCtx != null && ruleNodeCtx.getSelf().isDebugMode()) {
+ systemContext.persistDebugOutput(tenantId, originatorNodeId, envelope.getMsg(), envelope.getRelationType());
+ }
+ onTellNext(envelope.getMsg(), originatorNodeId, Collections.singleton(envelope.getRelationType()), RuleNodeException.UNKNOWN);
+ } else {
+ parent.tell(envelope);
+ }
+ }
+
+ void onRuleChainToRuleChainMsg(RuleChainToRuleChainMsg envelope) {
+ var msg = envelope.getMsg();
+ if (!checkMsgValid(msg)) {
+ return;
+ }
+ try {
+ checkComponentStateActive(envelope.getMsg());
+ if (firstNode != null) {
+ pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType());
+ } else {
+ envelope.getMsg().getCallback().onSuccess();
+ }
+ } catch (RuleNodeException e) {
+ log.debug("Rule Chain is not active. Current state [{}] for processor [{}][{}] tenant [{}]", state, entityId.getEntityType(), entityId, tenantId);
+ }
+ }
+
+ void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
+ var msg = envelope.getMsg();
+ if (checkMsgValid(msg)) {
+ onTellNext(msg, envelope.getOriginator(), envelope.getRelationTypes(), envelope.getFailureMessage());
+ }
+ }
+
+ private void onTellNext(TbMsg msg, RuleNodeId originatorNodeId, Set relationTypes, String failureMessage) {
+ try {
+ checkComponentStateActive(msg);
+ EntityId entityId = msg.getOriginator();
+ TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, msg.getQueueName(), tenantId, entityId);
+
+ List ruleNodeRelations = nodeRoutes.get(originatorNodeId);
+ if (ruleNodeRelations == null) { // When unchecked, this will cause NullPointerException when rule node doesn't exist anymore
+ log.warn("[{}][{}][{}] No outbound relations (null). Probably rule node does not exist. Probably old message.", tenantId, entityId, msg.getId());
+ ruleNodeRelations = Collections.emptyList();
+ }
+
+ List relationsByTypes = ruleNodeRelations.stream()
+ .filter(r -> contains(relationTypes, r.getType()))
+ .collect(Collectors.toList());
+ int relationsCount = relationsByTypes.size();
+ if (relationsCount == 0) {
+ log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId());
+ if (relationTypes.contains(TbRelationTypes.FAILURE)) {
+ RuleNodeCtx ruleNodeCtx = nodeActors.get(originatorNodeId);
+ if (ruleNodeCtx != null) {
+ msg.getCallback().onFailure(new RuleNodeException(failureMessage, ruleChainName, ruleNodeCtx.getSelf()));
+ } else {
+ log.debug("[{}] Failure during message processing by Rule Node [{}]. Enable and see debug events for more info", entityId, originatorNodeId.getId());
+ msg.getCallback().onFailure(new RuleEngineException("Failure during message processing by Rule Node [" + originatorNodeId.getId().toString() + "]"));
+ }
+ } else {
+ msg.getCallback().onSuccess();
+ }
+ } else if (relationsCount == 1) {
+ for (RuleNodeRelation relation : relationsByTypes) {
+ log.trace("[{}][{}][{}] Pushing message to single target: [{}]", tenantId, entityId, msg.getId(), relation.getOut());
+ pushToTarget(tpi, msg, relation.getOut(), relation.getType());
+ }
+ } else {
+ MultipleTbQueueTbMsgCallbackWrapper callbackWrapper = new MultipleTbQueueTbMsgCallbackWrapper(relationsCount, msg.getCallback());
+ log.trace("[{}][{}][{}] Pushing message to multiple targets: [{}]", tenantId, entityId, msg.getId(), relationsByTypes);
+ for (RuleNodeRelation relation : relationsByTypes) {
+ EntityId target = relation.getOut();
+ putToQueue(tpi, msg, callbackWrapper, target);
+ }
+ }
+ } catch (RuleNodeException rne) {
+ msg.getCallback().onFailure(rne);
+ } catch (Exception e) {
+ log.warn("[" + tenantId + "]" + "[" + entityId + "]" + "[" + msg.getId() + "]" + " onTellNext failure", e);
+ msg.getCallback().onFailure(new RuleEngineException("onTellNext - " + e.getMessage()));
+ }
+ }
+
+ private void putToQueue(TopicPartitionInfo tpi, TbMsg msg, TbQueueCallback callbackWrapper, EntityId target) {
+ switch (target.getEntityType()) {
+ case RULE_NODE:
+ putToQueue(tpi, msg.copyWithRuleNodeId(entityId, new RuleNodeId(target.getId()), UUID.randomUUID()), callbackWrapper);
+ break;
+ case RULE_CHAIN:
+ putToQueue(tpi, msg.copyWithRuleChainId(new RuleChainId(target.getId()), UUID.randomUUID()), callbackWrapper);
+ break;
+ }
+ }
+
+ private void pushToTarget(TopicPartitionInfo tpi, TbMsg msg, EntityId target, String fromRelationType) {
+ if (tpi.isMyPartition()) {
+ switch (target.getEntityType()) {
+ case RULE_NODE:
+ pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg, fromRelationType);
+ break;
+ case RULE_CHAIN:
+ parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, fromRelationType));
+ break;
+ }
+ } else {
+ putToQueue(tpi, msg, new TbQueueTbMsgCallbackWrapper(msg.getCallback()), target);
+ }
+ }
+
+ private void putToQueue(TopicPartitionInfo tpi, TbMsg newMsg, TbQueueCallback callbackWrapper) {
+ ToRuleEngineMsg toQueueMsg = ToRuleEngineMsg.newBuilder()
+ .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
+ .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
+ .setTbMsg(TbMsg.toByteString(newMsg))
+ .build();
+ clusterService.pushMsgToRuleEngine(tpi, newMsg.getId(), toQueueMsg, callbackWrapper);
+ }
+
+ private boolean contains(Set relationTypes, String type) {
+ if (relationTypes == null) {
+ return true;
+ }
+ for (String relationType : relationTypes) {
+ if (relationType.equalsIgnoreCase(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
+ if (nodeCtx != null) {
+ nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, ruleChainName, nodeCtx), msg, fromRelationType));
+ } else {
+ log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
+ msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty"));
+ }
+ }
+
+ @Override
+ protected RuleNodeException getInactiveException() {
+ RuleNode firstRuleNode = firstNode != null ? firstNode.getSelf() : null;
+ return new RuleNodeException("Rule Chain is not active! Failed to initialize.", ruleChainName, firstRuleNode);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainInputMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainInputMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..11614666329bdb58b1b626dd1b6fd4d02cd08dfb
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainInputMsg.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@EqualsAndHashCode(callSuper = true)
+@ToString
+public final class RuleChainInputMsg extends TbToRuleChainActorMsg {
+
+ public RuleChainInputMsg(RuleChainId target, TbMsg tbMsg) {
+ super(tbMsg, target);
+ }
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_CHAIN_INPUT_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..eac35bf835435e91452ea4eb95c42749c5f96be1
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.actors.TbEntityActorId;
+import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.PageDataIterable;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.data.rule.RuleChainType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.dao.rule.RuleChainService;
+
+import java.util.function.Function;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+@Slf4j
+public abstract class RuleChainManagerActor extends ContextAwareActor {
+
+ protected final TenantId tenantId;
+ private final RuleChainService ruleChainService;
+ @Getter
+ protected RuleChain rootChain;
+ @Getter
+ protected TbActorRef rootChainActor;
+
+ public RuleChainManagerActor(ActorSystemContext systemContext, TenantId tenantId) {
+ super(systemContext);
+ this.tenantId = tenantId;
+ this.ruleChainService = systemContext.getRuleChainService();
+ }
+
+ protected void initRuleChains() {
+ for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
+ RuleChainId ruleChainId = ruleChain.getId();
+ log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId());
+ TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain);
+ visit(ruleChain, actorRef);
+ log.debug("[{}|{}] Rule Chain actor created.", ruleChainId.getEntityType(), ruleChainId.getId());
+ }
+ }
+
+ protected void destroyRuleChains() {
+ for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
+ ctx.stop(new TbEntityActorId(ruleChain.getId()));
+ }
+ }
+
+ protected void visit(RuleChain entity, TbActorRef actorRef) {
+ if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.CORE)) {
+ rootChain = entity;
+ rootChainActor = actorRef;
+ }
+ }
+
+ protected TbActorRef getOrCreateActor(RuleChainId ruleChainId) {
+ return getOrCreateActor(ruleChainId, eId -> ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, eId));
+ }
+
+ protected TbActorRef getOrCreateActor(RuleChainId ruleChainId, Function provider) {
+ return ctx.getOrCreateChildActor(new TbEntityActorId(ruleChainId),
+ () -> DefaultActorService.RULE_DISPATCHER_NAME,
+ () -> {
+ RuleChain ruleChain = provider.apply(ruleChainId);
+ return new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain);
+ });
+ }
+
+ protected TbActorRef getEntityActorRef(EntityId entityId) {
+ TbActorRef target = null;
+ if (entityId.getEntityType() == EntityType.RULE_CHAIN) {
+ target = getOrCreateActor((RuleChainId) entityId);
+ }
+ return target;
+ }
+
+ protected void broadcast(TbActorMsg msg) {
+ ctx.broadcastToChildren(msg, new TbEntityTypeActorIdPredicate(EntityType.RULE_CHAIN));
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainOutputMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainOutputMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..d71eeaba0218fda7218247235194f5ea05e5cbaa
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainOutputMsg.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@EqualsAndHashCode(callSuper = true)
+@ToString
+public final class RuleChainOutputMsg extends TbToRuleChainActorMsg {
+
+ @Getter
+ private final RuleNodeId targetRuleNodeId;
+
+ @Getter
+ private final String relationType;
+
+ public RuleChainOutputMsg(RuleChainId target, RuleNodeId targetRuleNodeId, String relationType, TbMsg tbMsg) {
+ super(tbMsg, target);
+ this.targetRuleNodeId = targetRuleNodeId;
+ this.relationType = relationType;
+ }
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_CHAIN_OUTPUT_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..297ca7592a9c8aaf99ff8210e9fbc256ff869709
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorStopReason;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
+import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
+import org.thingsboard.server.common.msg.queue.RuleEngineException;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@EqualsAndHashCode(callSuper = true)
+@ToString
+public final class RuleChainToRuleChainMsg extends TbToRuleChainActorMsg {
+
+ @Getter
+ private final RuleChainId source;
+ @Getter
+ private final String fromRelationType;
+
+ public RuleChainToRuleChainMsg(RuleChainId target, RuleChainId source, TbMsg tbMsg, String fromRelationType) {
+ super(tbMsg, target);
+ this.source = source;
+ this.fromRelationType = fromRelationType;
+ }
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..431cf3ed4fff10f6e372fa73f178aa6fc281975f
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@EqualsAndHashCode(callSuper = true)
+@ToString
+final class RuleChainToRuleNodeMsg extends TbToRuleNodeActorMsg {
+
+ @Getter
+ private final String fromRelationType;
+
+ public RuleChainToRuleNodeMsg(TbContext ctx, TbMsg tbMsg, String fromRelationType) {
+ super(ctx, tbMsg);
+ this.fromRelationType = fromRelationType;
+ }
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_CHAIN_TO_RULE_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..f5847f764bbee427c4e75d02ea5de6b266c9f367
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorId;
+import org.thingsboard.server.actors.TbEntityActorId;
+import org.thingsboard.server.actors.service.ComponentActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+
+@Slf4j
+public class RuleNodeActor extends ComponentActor {
+
+ private final String ruleChainName;
+ private final RuleChainId ruleChainId;
+ private final RuleNodeId ruleNodeId;
+
+ private RuleNodeActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId, String ruleChainName, RuleNodeId ruleNodeId) {
+ super(systemContext, tenantId, ruleNodeId);
+ this.ruleChainName = ruleChainName;
+ this.ruleChainId = ruleChainId;
+ this.ruleNodeId = ruleNodeId;
+ }
+
+ @Override
+ protected RuleNodeActorMessageProcessor createProcessor(TbActorCtx ctx) {
+ return new RuleNodeActorMessageProcessor(tenantId, this.ruleChainName, ruleNodeId, systemContext, ctx.getParentRef(), ctx);
+ }
+
+ @Override
+ protected boolean doProcess(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case COMPONENT_LIFE_CYCLE_MSG:
+ case RULE_NODE_UPDATED_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case RULE_CHAIN_TO_RULE_MSG:
+ onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
+ break;
+ case RULE_TO_SELF_MSG:
+ onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg);
+ break;
+ case STATS_PERSIST_TICK_MSG:
+ onStatsPersistTick(id);
+ break;
+ case PARTITION_CHANGE_MSG:
+ onClusterEventMsg((PartitionChangeMsg) msg);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private void onRuleNodeToSelfMsg(RuleNodeToSelfMsg msg) {
+ if (log.isDebugEnabled()) {
+ log.debug("[{}][{}][{}] Going to process rule msg: {}", ruleChainId, id, processor.getComponentName(), msg.getMsg());
+ }
+ try {
+ processor.onRuleToSelfMsg(msg);
+ increaseMessagesProcessedCount();
+ } catch (Exception e) {
+ logAndPersist("onRuleMsg", e);
+ }
+ }
+
+ private void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg envelope) {
+ TbMsg msg = envelope.getMsg();
+ if (!msg.isValid()) {
+ if (log.isTraceEnabled()) {
+ log.trace("Skip processing of message: {} because it is no longer valid!", msg);
+ }
+ return;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("[{}][{}][{}] Going to process rule engine msg: {}", ruleChainId, id, processor.getComponentName(), msg);
+ }
+ try {
+ processor.onRuleChainToRuleNodeMsg(envelope);
+ increaseMessagesProcessedCount();
+ } catch (Exception e) {
+ logAndPersist("onRuleMsg", e);
+ }
+ }
+
+ public static class ActorCreator extends ContextBasedCreator {
+
+ private final TenantId tenantId;
+ private final RuleChainId ruleChainId;
+ private final String ruleChainName;
+ private final RuleNodeId ruleNodeId;
+
+ public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId ruleChainId, String ruleChainName, RuleNodeId ruleNodeId) {
+ super(context);
+ this.tenantId = tenantId;
+ this.ruleChainId = ruleChainId;
+ this.ruleChainName = ruleChainName;
+ this.ruleNodeId = ruleNodeId;
+
+ }
+
+ @Override
+ public TbActorId createActorId() {
+ return new TbEntityActorId(ruleNodeId);
+ }
+
+ @Override
+ public TbActor createActor() {
+ return new RuleNodeActor(context, tenantId, ruleChainId, ruleChainName, ruleNodeId);
+ }
+ }
+
+ @Override
+ protected long getErrorPersistFrequency() {
+ return systemContext.getRuleNodeErrorPersistFrequency();
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d102337aad64e88c5cebb121e949c8cd2ef39e5
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
@@ -0,0 +1,163 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import org.thingsboard.rule.engine.api.TbNode;
+import org.thingsboard.rule.engine.api.TbNodeConfiguration;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.actors.TbRuleNodeUpdateException;
+import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
+import org.thingsboard.server.common.data.ApiUsageRecordKey;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+import org.thingsboard.server.common.msg.queue.RuleNodeException;
+import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
+import org.thingsboard.server.common.stats.TbApiUsageReportClient;
+
+/**
+ * @author Andrew Shvayka
+ */
+public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor {
+
+ private final String ruleChainName;
+ private final TbActorRef self;
+ private final TbApiUsageReportClient apiUsageClient;
+ private RuleNode ruleNode;
+ private TbNode tbNode;
+ private DefaultTbContext defaultCtx;
+ private RuleNodeInfo info;
+
+ RuleNodeActorMessageProcessor(TenantId tenantId, String ruleChainName, RuleNodeId ruleNodeId, ActorSystemContext systemContext
+ , TbActorRef parent, TbActorRef self) {
+ super(systemContext, tenantId, ruleNodeId);
+ this.apiUsageClient = systemContext.getApiUsageClient();
+ this.ruleChainName = ruleChainName;
+ this.self = self;
+ this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
+ this.defaultCtx = new DefaultTbContext(systemContext, ruleChainName, new RuleNodeCtx(tenantId, parent, self, ruleNode));
+ this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown");
+ }
+
+ @Override
+ public void start(TbActorCtx context) throws Exception {
+ tbNode = initComponent(ruleNode);
+ if (tbNode != null) {
+ state = ComponentLifecycleState.ACTIVE;
+ }
+ }
+
+ @Override
+ public void onUpdate(TbActorCtx context) throws Exception {
+ RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
+ this.info = new RuleNodeInfo(entityId, ruleChainName, newRuleNode != null ? newRuleNode.getName() : "Unknown");
+ boolean restartRequired = state != ComponentLifecycleState.ACTIVE ||
+ !(ruleNode.getType().equals(newRuleNode.getType()) && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
+ this.ruleNode = newRuleNode;
+ this.defaultCtx.updateSelf(newRuleNode);
+ if (restartRequired) {
+ if (tbNode != null) {
+ tbNode.destroy();
+ }
+ try {
+ start(context);
+ } catch (Exception e) {
+ throw new TbRuleNodeUpdateException("Failed to update rule node", e);
+ }
+ }
+ }
+
+ @Override
+ public void stop(TbActorCtx context) {
+ if (tbNode != null) {
+ tbNode.destroy();
+ state = ComponentLifecycleState.SUSPENDED;
+ }
+ }
+
+ @Override
+ public void onPartitionChangeMsg(PartitionChangeMsg msg) {
+ if (tbNode != null) {
+ tbNode.onPartitionChangeMsg(defaultCtx, msg);
+ }
+ }
+
+ public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception {
+ checkComponentStateActive(msg.getMsg());
+ TbMsg tbMsg = msg.getMsg();
+ int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter();
+ int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage();
+ if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
+ apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
+ if (ruleNode.isDebugMode()) {
+ systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self");
+ }
+ try {
+ tbNode.onMsg(defaultCtx, msg.getMsg());
+ } catch (Exception e) {
+ defaultCtx.tellFailure(msg.getMsg(), e);
+ }
+ } else {
+ tbMsg.getCallback().onFailure(new RuleNodeException("Message is processed by more then " + maxRuleNodeExecutionsPerMessage + " rule nodes!", ruleChainName, ruleNode));
+ }
+ }
+
+ void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception {
+ msg.getMsg().getCallback().onProcessingStart(info);
+ checkComponentStateActive(msg.getMsg());
+ TbMsg tbMsg = msg.getMsg();
+ int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter();
+ int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage();
+ if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
+ apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
+ if (ruleNode.isDebugMode()) {
+ systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType());
+ }
+ try {
+ tbNode.onMsg(msg.getCtx(), msg.getMsg());
+ } catch (Exception e) {
+ msg.getCtx().tellFailure(msg.getMsg(), e);
+ }
+ } else {
+ tbMsg.getCallback().onFailure(new RuleNodeException("Message is processed by more then " + maxRuleNodeExecutionsPerMessage + " rule nodes!", ruleChainName, ruleNode));
+ }
+ }
+
+ @Override
+ public String getComponentName() {
+ return ruleNode.getName();
+ }
+
+ private TbNode initComponent(RuleNode ruleNode) throws Exception {
+ TbNode tbNode = null;
+ if (ruleNode != null) {
+ Class> componentClazz = Class.forName(ruleNode.getType());
+ tbNode = (TbNode) (componentClazz.getDeclaredConstructor().newInstance());
+ tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration()));
+ }
+ return tbNode;
+ }
+
+ @Override
+ protected RuleNodeException getInactiveException() {
+ return new RuleNodeException("Rule Node is not active! Failed to initialize.", ruleChainName, ruleNode);
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java
new file mode 100644
index 0000000000000000000000000000000000000000..3123c8f3c6640df12bd50291320b46299d5b4aaf
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.rule.RuleNode;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@Data
+@AllArgsConstructor
+final class RuleNodeCtx {
+ private final TenantId tenantId;
+ private final TbActorRef chainActor;
+ private final TbActorRef selfActor;
+ private RuleNode self;
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..7dfe0c1a872e7951a93fc40361692007c09e13f8
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.id.EntityId;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+
+@Data
+final class RuleNodeRelation {
+
+ private final EntityId in;
+ private final EntityId out;
+ private final String type;
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..f687045e1d638ae1711d8cdee06cd0ec0befa053
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorStopReason;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
+import org.thingsboard.server.common.msg.queue.RuleEngineException;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@EqualsAndHashCode(callSuper = true)
+@ToString
+class RuleNodeToRuleChainTellNextMsg extends TbRuleEngineActorMsg implements Serializable {
+
+ private static final long serialVersionUID = 4577026446412871820L;
+ @Getter
+ private final RuleChainId ruleChainId;
+ @Getter
+ private final RuleNodeId originator;
+ @Getter
+ private final Set relationTypes;
+ @Getter
+ private final String failureMessage;
+
+ public RuleNodeToRuleChainTellNextMsg(RuleChainId ruleChainId, RuleNodeId originator, Set relationTypes, TbMsg tbMsg, String failureMessage) {
+ super(tbMsg);
+ this.ruleChainId = ruleChainId;
+ this.originator = originator;
+ this.relationTypes = relationTypes;
+ this.failureMessage = failureMessage;
+ }
+
+ @Override
+ public void onTbActorStopped(TbActorStopReason reason) {
+ String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", ruleChainId.getId()) : String.format("Failed to initialize rule chain [%s]!", ruleChainId.getId());
+ msg.getCallback().onFailure(new RuleEngineException(message));
+ }
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..2748966128452405407883ca031116fc2059e225
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorStopReason;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
+import org.thingsboard.server.common.msg.queue.RuleNodeException;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@EqualsAndHashCode(callSuper = true)
+@ToString
+final class RuleNodeToSelfMsg extends TbToRuleNodeActorMsg {
+
+ public RuleNodeToSelfMsg(TbContext ctx, TbMsg tbMsg) {
+ super(ctx, tbMsg);
+ }
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_TO_SELF_MSG;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleChainActorMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleChainActorMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..da7489bd0fcae94f6f289fd61a1d8830f0d194d5
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleChainActorMsg.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.msg.TbActorStopReason;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
+import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
+import org.thingsboard.server.common.msg.queue.RuleEngineException;
+
+@EqualsAndHashCode(callSuper = true)
+@ToString
+public abstract class TbToRuleChainActorMsg extends TbRuleEngineActorMsg implements RuleChainAwareMsg {
+
+ @Getter
+ private final RuleChainId target;
+
+ public TbToRuleChainActorMsg(TbMsg msg, RuleChainId target) {
+ super(msg);
+ this.target = target;
+ }
+
+ @Override
+ public RuleChainId getRuleChainId() {
+ return target;
+ }
+
+ @Override
+ public void onTbActorStopped(TbActorStopReason reason) {
+ String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", target.getId()) : String.format("Failed to initialize rule chain [%s]!", target.getId());
+ msg.getCallback().onFailure(new RuleEngineException(message));
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleNodeActorMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleNodeActorMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..33c40528d3fd204f8f89a3e6fd6cc865524251d6
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleNodeActorMsg.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.server.common.msg.TbActorStopReason;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
+import org.thingsboard.server.common.msg.queue.RuleNodeException;
+
+@EqualsAndHashCode(callSuper = true)
+public abstract class TbToRuleNodeActorMsg extends TbRuleEngineActorMsg {
+
+ @Getter
+ private final TbContext ctx;
+
+ public TbToRuleNodeActorMsg(TbContext ctx, TbMsg tbMsg) {
+ super(tbMsg);
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void onTbActorStopped(TbActorStopReason reason) {
+ String message = reason == TbActorStopReason.STOPPED ? "Rule node stopped" : "Failed to initialize rule node!";
+ msg.getCallback().onFailure(new RuleNodeException(message, ctx.getRuleChainName(), ctx.getSelf()));
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
new file mode 100644
index 0000000000000000000000000000000000000000..f747b24d7a626fe088722eb2cdfa728ba13aa75b
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.service;
+
+public interface ActorService {
+
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..f89872d338a5866a9d8240bed900efb4dcae951b
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
@@ -0,0 +1,189 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorException;
+import org.thingsboard.server.actors.TbRuleNodeUpdateException;
+import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
+import org.thingsboard.server.actors.stats.StatsPersistMsg;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Slf4j
+public abstract class ComponentActor> extends ContextAwareActor {
+
+ private long lastPersistedErrorTs = 0L;
+ protected final TenantId tenantId;
+ protected final T id;
+ protected P processor;
+ private long messagesProcessed;
+ private long errorsOccurred;
+
+ public ComponentActor(ActorSystemContext systemContext, TenantId tenantId, T id) {
+ super(systemContext);
+ this.tenantId = tenantId;
+ this.id = id;
+ }
+
+ abstract protected P createProcessor(TbActorCtx ctx);
+
+ @Override
+ public void init(TbActorCtx ctx) throws TbActorException {
+ super.init(ctx);
+ this.processor = createProcessor(ctx);
+ initProcessor(ctx);
+ }
+
+ protected void initProcessor(TbActorCtx ctx) throws TbActorException {
+ try {
+ log.debug("[{}][{}][{}] Starting processor.", tenantId, id, id.getEntityType());
+ processor.start(ctx);
+ logLifecycleEvent(ComponentLifecycleEvent.STARTED);
+ if (systemContext.isStatisticsEnabled()) {
+ scheduleStatsPersistTick();
+ }
+ } catch (Exception e) {
+ log.debug("[{}][{}] Failed to start {} processor.", tenantId, id, id.getEntityType(), e);
+ logAndPersist("OnStart", e, true);
+ logLifecycleEvent(ComponentLifecycleEvent.STARTED, e);
+ throw new TbActorException("Failed to init actor", e);
+ }
+ }
+
+ private void scheduleStatsPersistTick() {
+ try {
+ processor.scheduleStatsPersistTick(ctx, systemContext.getStatisticsPersistFrequency());
+ } catch (Exception e) {
+ log.error("[{}][{}] Failed to schedule statistics store message. No statistics is going to be stored: {}", tenantId, id, e.getMessage());
+ logAndPersist("onScheduleStatsPersistMsg", e);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ try {
+ log.debug("[{}][{}][{}] Stopping processor.", tenantId, id, id.getEntityType());
+ if (processor != null) {
+ processor.stop(ctx);
+ }
+ logLifecycleEvent(ComponentLifecycleEvent.STOPPED);
+ } catch (Exception e) {
+ log.warn("[{}][{}] Failed to stop {} processor: {}", tenantId, id, id.getEntityType(), e.getMessage());
+ logAndPersist("OnStop", e, true);
+ logLifecycleEvent(ComponentLifecycleEvent.STOPPED, e);
+ }
+ }
+
+ protected void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
+ log.debug("[{}][{}][{}] onComponentLifecycleMsg: [{}]", tenantId, id, id.getEntityType(), msg.getEvent());
+ try {
+ switch (msg.getEvent()) {
+ case CREATED:
+ processor.onCreated(ctx);
+ break;
+ case UPDATED:
+ processor.onUpdate(ctx);
+ break;
+ case ACTIVATED:
+ processor.onActivate(ctx);
+ break;
+ case SUSPENDED:
+ processor.onSuspend(ctx);
+ break;
+ case DELETED:
+ processor.onStop(ctx);
+ ctx.stop(ctx.getSelf());
+ break;
+ default:
+ break;
+ }
+ logLifecycleEvent(msg.getEvent());
+ } catch (Exception e) {
+ logAndPersist("onLifecycleMsg", e, true);
+ logLifecycleEvent(msg.getEvent(), e);
+ if (e instanceof TbRuleNodeUpdateException) {
+ throw (TbRuleNodeUpdateException) e;
+ }
+ }
+ }
+
+ protected void onClusterEventMsg(PartitionChangeMsg msg) {
+ try {
+ processor.onPartitionChangeMsg(msg);
+ } catch (Exception e) {
+ logAndPersist("onClusterEventMsg", e);
+ }
+ }
+
+ protected void onStatsPersistTick(EntityId entityId) {
+ try {
+ systemContext.getStatsActor().tell(new StatsPersistMsg(messagesProcessed, errorsOccurred, tenantId, entityId));
+ resetStatsCounters();
+ } catch (Exception e) {
+ logAndPersist("onStatsPersistTick", e);
+ }
+ }
+
+ private void resetStatsCounters() {
+ messagesProcessed = 0;
+ errorsOccurred = 0;
+ }
+
+ protected void increaseMessagesProcessedCount() {
+ messagesProcessed++;
+ }
+
+ protected void logAndPersist(String method, Exception e) {
+ logAndPersist(method, e, false);
+ }
+
+ private void logAndPersist(String method, Exception e, boolean critical) {
+ errorsOccurred++;
+ String componentName = processor != null ? processor.getComponentName() : "Unknown";
+ if (critical) {
+ log.debug("[{}][{}][{}] Failed to process method: {}", id, tenantId, componentName, method);
+ log.debug("Critical Error: ", e);
+ } else {
+ log.trace("[{}][{}][{}] Failed to process method: {}", id, tenantId, componentName, method);
+ log.trace("Debug Error: ", e);
+ }
+ long ts = System.currentTimeMillis();
+ if (ts - lastPersistedErrorTs > getErrorPersistFrequency()) {
+ systemContext.persistError(tenantId, id, method, e);
+ lastPersistedErrorTs = ts;
+ }
+ }
+
+ private void logLifecycleEvent(ComponentLifecycleEvent event) {
+ logLifecycleEvent(event, null);
+ }
+
+ private void logLifecycleEvent(ComponentLifecycleEvent event, Exception e) {
+ systemContext.persistLifecycleEvent(tenantId, id, event, e);
+ }
+
+ protected abstract long getErrorPersistFrequency();
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a6df8b66aeca19b215593645dea767821fc49cc
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.thingsboard.server.actors.AbstractTbActor;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.ProcessFailureStrategy;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+@Slf4j
+public abstract class ContextAwareActor extends AbstractTbActor {
+
+ public static final int ENTITY_PACK_LIMIT = 1024;
+
+ protected final ActorSystemContext systemContext;
+
+ public ContextAwareActor(ActorSystemContext systemContext) {
+ super();
+ this.systemContext = systemContext;
+ }
+
+ @Override
+ public boolean process(TbActorMsg msg) {
+ if (log.isDebugEnabled()) {
+ log.debug("Processing msg: {}", msg);
+ }
+ if (!doProcess(msg)) {
+ log.warn("Unprocessed message: {}!", msg);
+ }
+ return false;
+ }
+
+ protected abstract boolean doProcess(TbActorMsg msg);
+
+ @Override
+ public ProcessFailureStrategy onProcessFailure(Throwable t) {
+ log.debug("[{}] Processing failure: ", getActorRef().getActorId(), t);
+ return doProcessFailure(t);
+ }
+
+ protected ProcessFailureStrategy doProcessFailure(Throwable t) {
+ if (t instanceof Error) {
+ return ProcessFailureStrategy.stop();
+ } else {
+ return ProcessFailureStrategy.resume();
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ContextBasedCreator.java b/application/src/main/java/org/thingsboard/server/actors/service/ContextBasedCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..6445044a792c1f1c5c29e2c513e8281c4e96c7e2
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ContextBasedCreator.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.service;
+
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorCreator;
+
+public abstract class ContextBasedCreator implements TbActorCreator {
+
+ protected final transient ActorSystemContext context;
+
+ public ContextBasedCreator(ActorSystemContext context) {
+ super();
+ this.context = context;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d3bf2bbbb2bcce4a9e0bcf4d8d468f09e4c68ee
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -0,0 +1,138 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+import org.thingsboard.common.util.ThingsBoardExecutors;
+import org.thingsboard.common.util.ThingsBoardThreadFactory;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.DefaultTbActorSystem;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.actors.TbActorSystem;
+import org.thingsboard.server.actors.TbActorSystemSettings;
+import org.thingsboard.server.actors.app.AppActor;
+import org.thingsboard.server.actors.app.AppInitMsg;
+import org.thingsboard.server.actors.stats.StatsActor;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
+import org.thingsboard.server.queue.util.AfterStartUp;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@Service
+@Slf4j
+public class DefaultActorService extends TbApplicationEventListener implements ActorService {
+
+ public static final String APP_DISPATCHER_NAME = "app-dispatcher";
+ public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher";
+ public static final String DEVICE_DISPATCHER_NAME = "device-dispatcher";
+ public static final String RULE_DISPATCHER_NAME = "rule-dispatcher";
+
+ @Autowired
+ private ActorSystemContext actorContext;
+
+ private TbActorSystem system;
+
+ private TbActorRef appActor;
+
+ @Value("${actors.system.throughput:5}")
+ private int actorThroughput;
+
+ @Value("${actors.system.max_actor_init_attempts:10}")
+ private int maxActorInitAttempts;
+
+ @Value("${actors.system.scheduler_pool_size:1}")
+ private int schedulerPoolSize;
+
+ @Value("${actors.system.app_dispatcher_pool_size:1}")
+ private int appDispatcherSize;
+
+ @Value("${actors.system.tenant_dispatcher_pool_size:2}")
+ private int tenantDispatcherSize;
+
+ @Value("${actors.system.device_dispatcher_pool_size:4}")
+ private int deviceDispatcherSize;
+
+ @Value("${actors.system.rule_dispatcher_pool_size:4}")
+ private int ruleDispatcherSize;
+
+ @PostConstruct
+ public void initActorSystem() {
+ log.info("Initializing actor system.");
+ actorContext.setActorService(this);
+ TbActorSystemSettings settings = new TbActorSystemSettings(actorThroughput, schedulerPoolSize, maxActorInitAttempts);
+ system = new DefaultTbActorSystem(settings);
+
+ system.createDispatcher(APP_DISPATCHER_NAME, initDispatcherExecutor(APP_DISPATCHER_NAME, appDispatcherSize));
+ system.createDispatcher(TENANT_DISPATCHER_NAME, initDispatcherExecutor(TENANT_DISPATCHER_NAME, tenantDispatcherSize));
+ system.createDispatcher(DEVICE_DISPATCHER_NAME, initDispatcherExecutor(DEVICE_DISPATCHER_NAME, deviceDispatcherSize));
+ system.createDispatcher(RULE_DISPATCHER_NAME, initDispatcherExecutor(RULE_DISPATCHER_NAME, ruleDispatcherSize));
+
+ actorContext.setActorSystem(system);
+
+ appActor = system.createRootActor(APP_DISPATCHER_NAME, new AppActor.ActorCreator(actorContext));
+ actorContext.setAppActor(appActor);
+
+ TbActorRef statsActor = system.createRootActor(TENANT_DISPATCHER_NAME, new StatsActor.ActorCreator(actorContext, "StatsActor"));
+ actorContext.setStatsActor(statsActor);
+
+ log.info("Actor system initialized.");
+ }
+
+ private ExecutorService initDispatcherExecutor(String dispatcherName, int poolSize) {
+ if (poolSize == 0) {
+ int cores = Runtime.getRuntime().availableProcessors();
+ poolSize = Math.max(1, cores / 2);
+ }
+ if (poolSize == 1) {
+ return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName));
+ } else {
+ return ThingsBoardExecutors.newWorkStealingPool(poolSize, dispatcherName);
+ }
+ }
+
+ @AfterStartUp(order = AfterStartUp.ACTOR_SYSTEM)
+ public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
+ log.info("Received application ready event. Sending application init message to actor system");
+ appActor.tellWithHighPriority(new AppInitMsg());
+ }
+
+ @Override
+ protected void onTbApplicationEvent(PartitionChangeEvent event) {
+ log.info("Received partition change event.");
+ this.appActor.tellWithHighPriority(new PartitionChangeMsg(event.getQueueKey().getType(), event.getPartitions()));
+ }
+
+ @PreDestroy
+ public void stopActorSystem() {
+ if (system != null) {
+ log.info("Stopping actor system.");
+ system.stop();
+ log.info("Actor system stopped.");
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..2cc8766d288ff2020876ac656ce0b525d5f05cf2
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.shared;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+@Slf4j
+public abstract class AbstractContextAwareMsgProcessor {
+
+ protected final static ObjectMapper mapper = new ObjectMapper();
+
+ protected final ActorSystemContext systemContext;
+
+ protected AbstractContextAwareMsgProcessor(ActorSystemContext systemContext) {
+ super();
+ this.systemContext = systemContext;
+ }
+
+ private ScheduledExecutorService getScheduler() {
+ return systemContext.getScheduler();
+ }
+
+ protected void schedulePeriodicMsgWithDelay(TbActorCtx ctx, TbActorMsg msg, long delayInMs, long periodInMs) {
+ systemContext.schedulePeriodicMsgWithDelay(ctx, msg, delayInMs, periodInMs);
+ }
+
+ protected void scheduleMsgWithDelay(TbActorCtx ctx, TbActorMsg msg, long delayInMs) {
+ systemContext.scheduleMsgWithDelay(ctx, msg, delayInMs);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ActorTerminationMsg.java b/application/src/main/java/org/thingsboard/server/actors/shared/ActorTerminationMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..21b1cd98cba275a776d2ba2b933f2453273068e1
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/ActorTerminationMsg.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.shared;
+
+public abstract class ActorTerminationMsg {
+
+ private final T id;
+
+ public ActorTerminationMsg(T id) {
+ super();
+ this.id = id;
+ }
+
+ public T getId() {
+ return id;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..06407d659576a3a698801adbd3204405b2f9c207
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.shared;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.stats.StatsPersistTick;
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+import org.thingsboard.server.common.msg.queue.RuleNodeException;
+
+@Slf4j
+public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgProcessor {
+
+ protected final TenantId tenantId;
+ protected final T entityId;
+ protected ComponentLifecycleState state;
+
+ protected ComponentMsgProcessor(ActorSystemContext systemContext, TenantId tenantId, T id) {
+ super(systemContext);
+ this.tenantId = tenantId;
+ this.entityId = id;
+ }
+
+ protected TenantProfileConfiguration getTenantProfileConfiguration() {
+ return systemContext.getTenantProfileCache().get(tenantId).getProfileData().getConfiguration();
+ }
+
+ public abstract String getComponentName();
+
+ public abstract void start(TbActorCtx context) throws Exception;
+
+ public abstract void stop(TbActorCtx context) throws Exception;
+
+ public abstract void onPartitionChangeMsg(PartitionChangeMsg msg) throws Exception;
+
+ public void onCreated(TbActorCtx context) throws Exception {
+ start(context);
+ }
+
+ public void onUpdate(TbActorCtx context) throws Exception {
+ restart(context);
+ }
+
+ public void onActivate(TbActorCtx context) throws Exception {
+ restart(context);
+ }
+
+ public void onSuspend(TbActorCtx context) throws Exception {
+ stop(context);
+ }
+
+ public void onStop(TbActorCtx context) throws Exception {
+ stop(context);
+ }
+
+ private void restart(TbActorCtx context) throws Exception {
+ stop(context);
+ start(context);
+ }
+
+ public void scheduleStatsPersistTick(TbActorCtx context, long statsPersistFrequency) {
+ schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency);
+ }
+
+ protected boolean checkMsgValid(TbMsg tbMsg) {
+ var valid = tbMsg.isValid();
+ if (!valid) {
+ if (log.isTraceEnabled()) {
+ log.trace("Skip processing of message: {} because it is no longer valid!", tbMsg);
+ }
+ }
+ return valid;
+ }
+
+ protected void checkComponentStateActive(TbMsg tbMsg) throws RuleNodeException {
+ if (state != ComponentLifecycleState.ACTIVE) {
+ log.debug("Component is not active. Current state [{}] for processor [{}][{}] tenant [{}]", state, entityId.getEntityType(), entityId, tenantId);
+ RuleNodeException ruleNodeException = getInactiveException();
+ if (tbMsg != null) {
+ tbMsg.getCallback().onFailure(ruleNodeException);
+ }
+ throw ruleNodeException;
+ }
+ }
+
+ abstract protected RuleNodeException getInactiveException();
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..247a6c62d0a2c03633d5bf798e4d3a0e332f469c
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.stats;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorId;
+import org.thingsboard.server.actors.TbStringActorId;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.EventInfo;
+import org.thingsboard.server.common.data.event.StatisticsEvent;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+@Slf4j
+public class StatsActor extends ContextAwareActor {
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ public StatsActor(ActorSystemContext context) {
+ super(context);
+ }
+
+ @Override
+ protected boolean doProcess(TbActorMsg msg) {
+ log.debug("Received message: {}", msg);
+ if (msg.getMsgType().equals(MsgType.STATS_PERSIST_MSG)) {
+ onStatsPersistMsg((StatsPersistMsg) msg);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void onStatsPersistMsg(StatsPersistMsg msg) {
+ if (msg.isEmpty()) {
+ return;
+ }
+ systemContext.getEventService().saveAsync(StatisticsEvent.builder()
+ .tenantId(msg.getTenantId())
+ .entityId(msg.getEntityId().getId())
+ .serviceId(systemContext.getServiceInfoProvider().getServiceId())
+ .messagesProcessed(msg.getMessagesProcessed())
+ .errorsOccurred(msg.getErrorsOccurred())
+ .build()
+ );
+ }
+
+ private JsonNode toBodyJson(String serviceId, long messagesProcessed, long errorsOccurred) {
+ return mapper.createObjectNode().put("server", serviceId).put("messagesProcessed", messagesProcessed).put("errorsOccurred", errorsOccurred);
+ }
+
+ public static class ActorCreator extends ContextBasedCreator {
+ private final String actorId;
+
+ public ActorCreator(ActorSystemContext context, String actorId) {
+ super(context);
+ this.actorId = actorId;
+ }
+
+ @Override
+ public TbActorId createActorId() {
+ return new TbStringActorId(actorId);
+ }
+
+ @Override
+ public TbActor createActor() {
+ return new StatsActor(context);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2dc74eaa43ee8df903a5eba09aa86c1b4bdd613
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.stats;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+@AllArgsConstructor
+@Getter
+@ToString
+public final class StatsPersistMsg implements TbActorMsg {
+
+ private final long messagesProcessed;
+ private final long errorsOccurred;
+ private final TenantId tenantId;
+ private final EntityId entityId;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.STATS_PERSIST_MSG;
+ }
+
+ public boolean isEmpty() {
+ return messagesProcessed == 0 && errorsOccurred == 0;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java
new file mode 100644
index 0000000000000000000000000000000000000000..671fd0a786659e9043a4cd6ffb870c7472427701
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.stats;
+
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+public final class StatsPersistTick implements TbActorMsg {
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.STATS_PERSIST_TICK_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/DebugTbRateLimits.java b/application/src/main/java/org/thingsboard/server/actors/tenant/DebugTbRateLimits.java
new file mode 100644
index 0000000000000000000000000000000000000000..575e814b5b9be19d80ce42b126ab8f45a28e773b
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/tenant/DebugTbRateLimits.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.tenant;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.thingsboard.server.common.msg.tools.TbRateLimits;
+
+@Data
+@AllArgsConstructor
+public class DebugTbRateLimits {
+
+ private TbRateLimits tbRateLimits;
+ private boolean ruleChainEventSaved;
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c40d40cfb8e79130ae9ec7b13afc26d8dad680e
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
@@ -0,0 +1,305 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.tenant;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbActor;
+import org.thingsboard.server.actors.TbActorCtx;
+import org.thingsboard.server.actors.TbActorException;
+import org.thingsboard.server.actors.TbActorId;
+import org.thingsboard.server.actors.TbActorNotRegisteredException;
+import org.thingsboard.server.actors.TbActorRef;
+import org.thingsboard.server.actors.TbEntityActorId;
+import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
+import org.thingsboard.server.actors.device.DeviceActorCreator;
+import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.common.data.ApiUsageState;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.EdgeId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.data.rule.RuleChainType;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
+import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
+import org.thingsboard.server.common.msg.edge.EdgeSessionMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
+import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
+import org.thingsboard.server.common.msg.queue.RuleEngineException;
+import org.thingsboard.server.common.msg.queue.ServiceType;
+import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
+
+import java.util.List;
+
+@Slf4j
+public class TenantActor extends RuleChainManagerActor {
+
+ private boolean isRuleEngine;
+ private boolean isCore;
+ private ApiUsageState apiUsageState;
+
+ private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
+ super(systemContext, tenantId);
+ }
+
+ boolean cantFindTenant = false;
+
+ @Override
+ public void init(TbActorCtx ctx) throws TbActorException {
+ super.init(ctx);
+ log.debug("[{}] Starting tenant actor.", tenantId);
+ try {
+ Tenant tenant = systemContext.getTenantService().findTenantById(tenantId);
+ if (tenant == null) {
+ cantFindTenant = true;
+ log.info("[{}] Started tenant actor for missing tenant.", tenantId);
+ } else {
+ TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId());
+
+ isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
+ isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
+ if (isRuleEngine) {
+ try {
+ if (getApiUsageState().isReExecEnabled()) {
+ log.debug("[{}] Going to init rule chains", tenantId);
+ initRuleChains();
+ } else {
+ log.info("[{}] Skip init of the rule chains due to API limits", tenantId);
+ }
+ } catch (Exception e) {
+ cantFindTenant = true;
+ }
+ }
+ log.debug("[{}] Tenant actor started.", tenantId);
+ }
+ } catch (Exception e) {
+ log.warn("[{}] Unknown failure", tenantId, e);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ log.info("[{}] Stopping tenant actor.", tenantId);
+ }
+
+ @Override
+ protected boolean doProcess(TbActorMsg msg) {
+ if (cantFindTenant) {
+ log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg);
+ if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
+ QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
+ queueMsg.getMsg().getCallback().onSuccess();
+ } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) {
+ TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
+ transportMsg.getCallback().onSuccess();
+ }
+ return true;
+ }
+ switch (msg.getMsgType()) {
+ case PARTITION_CHANGE_MSG:
+ PartitionChangeMsg partitionChangeMsg = (PartitionChangeMsg) msg;
+ ServiceType serviceType = partitionChangeMsg.getServiceType();
+ if (ServiceType.TB_RULE_ENGINE.equals(serviceType)) {
+ //To Rule Chain Actors
+ broadcast(msg);
+ } else if (ServiceType.TB_CORE.equals(serviceType)) {
+ List deviceActorIds = ctx.filterChildren(new TbEntityTypeActorIdPredicate(EntityType.DEVICE) {
+ @Override
+ protected boolean testEntityId(EntityId entityId) {
+ return super.testEntityId(entityId) && !isMyPartition(entityId);
+ }
+ });
+ deviceActorIds.forEach(id -> ctx.stop(id));
+ }
+ break;
+ case COMPONENT_LIFE_CYCLE_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case QUEUE_TO_RULE_ENGINE_MSG:
+ onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg);
+ break;
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
+ onToDeviceActorMsg((DeviceAwareMsg) msg, false);
+ break;
+ case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
+ onToDeviceActorMsg((DeviceAwareMsg) msg, true);
+ break;
+ case SESSION_TIMEOUT_MSG:
+ ctx.broadcastToChildrenByType(msg, EntityType.DEVICE);
+ break;
+ case RULE_CHAIN_INPUT_MSG:
+ case RULE_CHAIN_OUTPUT_MSG:
+ case RULE_CHAIN_TO_RULE_CHAIN_MSG:
+ onRuleChainMsg((RuleChainAwareMsg) msg);
+ break;
+ case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
+ case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG:
+ case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG:
+ onToEdgeSessionMsg((EdgeSessionMsg) msg);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isMyPartition(EntityId entityId) {
+ return systemContext.resolve(ServiceType.TB_CORE, tenantId, entityId).isMyPartition();
+ }
+
+ private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) {
+ if (!isRuleEngine) {
+ log.warn("RECEIVED INVALID MESSAGE: {}", msg);
+ return;
+ }
+ TbMsg tbMsg = msg.getMsg();
+ if (getApiUsageState().isReExecEnabled()) {
+ if (tbMsg.getRuleChainId() == null) {
+ if (getRootChainActor() != null) {
+ getRootChainActor().tell(msg);
+ } else {
+ tbMsg.getCallback().onFailure(new RuleEngineException("No Root Rule Chain available!"));
+ log.info("[{}] No Root Chain: {}", tenantId, msg);
+ }
+ } else {
+ try {
+ ctx.tell(new TbEntityActorId(tbMsg.getRuleChainId()), msg);
+ } catch (TbActorNotRegisteredException ex) {
+ log.trace("Received message for non-existing rule chain: [{}]", tbMsg.getRuleChainId());
+ //TODO: 3.1 Log it to dead letters queue;
+ tbMsg.getCallback().onSuccess();
+ }
+ }
+ } else {
+ log.trace("[{}] Ack message because Rule Engine is disabled", tenantId);
+ tbMsg.getCallback().onSuccess();
+ }
+ }
+
+ private void onRuleChainMsg(RuleChainAwareMsg msg) {
+ if (getApiUsageState().isReExecEnabled()) {
+ getOrCreateActor(msg.getRuleChainId()).tell(msg);
+ }
+ }
+
+ private void onToDeviceActorMsg(DeviceAwareMsg msg, boolean priority) {
+ if (!isCore) {
+ log.warn("RECEIVED INVALID MESSAGE: {}", msg);
+ }
+ TbActorRef deviceActor = getOrCreateDeviceActor(msg.getDeviceId());
+ if (priority) {
+ deviceActor.tellWithHighPriority(msg);
+ } else {
+ deviceActor.tell(msg);
+ }
+ }
+
+ private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
+ if (msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) {
+ ApiUsageState old = getApiUsageState();
+ apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenantId));
+ if (old.isReExecEnabled() && !apiUsageState.isReExecEnabled()) {
+ log.info("[{}] Received API state update. Going to DISABLE Rule Engine execution.", tenantId);
+ destroyRuleChains();
+ } else if (!old.isReExecEnabled() && apiUsageState.isReExecEnabled()) {
+ log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId);
+ initRuleChains();
+ }
+ } else if (msg.getEntityId().getEntityType() == EntityType.EDGE) {
+ EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
+ EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
+ if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
+ edgeRpcService.deleteEdge(tenantId, edgeId);
+ } else if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
+ Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
+ edgeRpcService.updateEdge(tenantId, edge);
+ }
+ } else if (isRuleEngine) {
+ TbActorRef target = getEntityActorRef(msg.getEntityId());
+ if (target != null) {
+ if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
+ RuleChain ruleChain = systemContext.getRuleChainService().
+ findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
+ if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
+ visit(ruleChain, target);
+ }
+ }
+ target.tellWithHighPriority(msg);
+ } else {
+ log.debug("[{}] Invalid component lifecycle msg: {}", tenantId, msg);
+ }
+ }
+ }
+
+ private TbActorRef getOrCreateDeviceActor(DeviceId deviceId) {
+ return ctx.getOrCreateChildActor(new TbEntityActorId(deviceId),
+ () -> DefaultActorService.DEVICE_DISPATCHER_NAME,
+ () -> new DeviceActorCreator(systemContext, tenantId, deviceId));
+ }
+
+ private void onToEdgeSessionMsg(EdgeSessionMsg msg) {
+ systemContext.getEdgeRpcService().onToEdgeSessionMsg(tenantId, msg);
+ }
+
+ private ApiUsageState getApiUsageState() {
+ if (apiUsageState == null) {
+ apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenantId));
+ }
+ return apiUsageState;
+ }
+
+ public static class ActorCreator extends ContextBasedCreator {
+
+ private final TenantId tenantId;
+
+ public ActorCreator(ActorSystemContext context, TenantId tenantId) {
+ super(context);
+ this.tenantId = tenantId;
+ }
+
+ @Override
+ public TbActorId createActorId() {
+ return new TbEntityActorId(tenantId);
+ }
+
+ @Override
+ public TbActor createActor() {
+ return new TenantActor(context, tenantId);
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..360d5e54c084745f0d5870544a6aa4123c7a5d55
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
@@ -0,0 +1,302 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
+import org.springframework.security.crypto.keygen.StringKeyGenerator;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
+import org.springframework.security.oauth2.core.oidc.OidcScopes;
+import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
+import org.springframework.security.web.util.UrlUtils;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
+import org.thingsboard.server.common.data.StringUtils;
+import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
+import org.thingsboard.server.dao.oauth2.OAuth2Service;
+import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames;
+import org.thingsboard.server.service.security.model.token.OAuth2AppTokenFactory;
+import org.thingsboard.server.utils.MiscUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+@Service
+@Slf4j
+public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
+ private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
+ private static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/";
+ private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
+ private static final char PATH_DELIMITER = '/';
+
+ private final AntPathRequestMatcher authorizationRequestMatcher = new AntPathRequestMatcher(
+ DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}");
+ private final StringKeyGenerator stateGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder());
+ private final StringKeyGenerator secureKeyGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
+
+ @Autowired
+ private ClientRegistrationRepository clientRegistrationRepository;
+
+ @Autowired
+ private OAuth2Service oAuth2Service;
+
+ @Autowired
+ private OAuth2AppTokenFactory oAuth2AppTokenFactory;
+
+ @Autowired(required = false)
+ private OAuth2Configuration oauth2Configuration;
+
+
+ @Override
+ public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
+ String registrationId = this.resolveRegistrationId(request);
+ String redirectUriAction = getAction(request, "login");
+ String appPackage = getAppPackage(request);
+ String appToken = getAppToken(request);
+ return resolve(request, registrationId, redirectUriAction, appPackage, appToken);
+ }
+
+ @Override
+ public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId) {
+ if (registrationId == null) {
+ return null;
+ }
+ String redirectUriAction = getAction(request, "authorize");
+ String appPackage = getAppPackage(request);
+ String appToken = getAppToken(request);
+ return resolve(request, registrationId, redirectUriAction, appPackage, appToken);
+ }
+
+ private String getAction(HttpServletRequest request, String defaultAction) {
+ String action = request.getParameter("action");
+ if (action == null) {
+ return defaultAction;
+ }
+ return action;
+ }
+
+ private String getAppPackage(HttpServletRequest request) {
+ return request.getParameter("pkg");
+ }
+
+ private String getAppToken(HttpServletRequest request) {
+ return request.getParameter("appToken");
+ }
+
+ @SuppressWarnings("deprecation")
+ private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage, String appToken) {
+ if (registrationId == null) {
+ return null;
+ }
+
+ ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
+ if (clientRegistration == null) {
+ throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
+ }
+
+ Map attributes = new HashMap<>();
+ attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
+ if (!StringUtils.isEmpty(appPackage)) {
+ if (StringUtils.isEmpty(appToken)) {
+ throw new IllegalArgumentException("Invalid application token.");
+ } else {
+ String appSecret = this.oAuth2Service.findAppSecret(UUID.fromString(registrationId), appPackage);
+ if (StringUtils.isEmpty(appSecret)) {
+ throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package.");
+ }
+ String callbackUrlScheme = this.oAuth2AppTokenFactory.validateTokenAndGetCallbackUrlScheme(appPackage, appToken, appSecret);
+ attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme);
+ }
+ }
+
+ OAuth2AuthorizationRequest.Builder builder;
+ if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
+ builder = OAuth2AuthorizationRequest.authorizationCode();
+ Map additionalParameters = new HashMap<>();
+ if (!CollectionUtils.isEmpty(clientRegistration.getScopes()) &&
+ clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
+ // Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
+ // scope
+ // REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
+ addNonceParameters(attributes, additionalParameters);
+ }
+ if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
+ addPkceParameters(attributes, additionalParameters);
+ }
+ builder.additionalParameters(additionalParameters);
+ } else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
+ builder = OAuth2AuthorizationRequest.implicit();
+ } else {
+ throw new IllegalArgumentException("Invalid Authorization Grant Type (" +
+ clientRegistration.getAuthorizationGrantType().getValue() +
+ ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
+ }
+
+ String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);
+
+ return builder
+ .clientId(clientRegistration.getClientId())
+ .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
+ .redirectUri(redirectUriStr)
+ .scopes(clientRegistration.getScopes())
+ .state(this.stateGenerator.generateKey())
+ .attributes(attributes)
+ .build();
+ }
+
+ private String resolveRegistrationId(HttpServletRequest request) {
+ if (this.authorizationRequestMatcher.matches(request)) {
+ return this.authorizationRequestMatcher
+ .matcher(request).getVariables().get(REGISTRATION_ID_URI_VARIABLE_NAME);
+ }
+ return null;
+ }
+
+ /**
+ * Expands the {@link ClientRegistration#getRedirectUriTemplate()} with following provided variables:
+ * - baseUrl (e.g. https://localhost/app)
+ * - baseScheme (e.g. https)
+ * - baseHost (e.g. localhost)
+ * - basePort (e.g. :8080)
+ * - basePath (e.g. /app)
+ * - registrationId (e.g. google)
+ * - action (e.g. login)
+ *
+ * Null variables are provided as empty strings.
+ *
+ * Default redirectUriTemplate is: {@link org.springframework.security.config.oauth2.client}.CommonOAuth2Provider#DEFAULT_REDIRECT_URL
+ *
+ * @return expanded URI
+ */
+ private String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) {
+ Map uriVariables = new HashMap<>();
+ uriVariables.put("registrationId", clientRegistration.getRegistrationId());
+
+ UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
+ .replacePath(request.getContextPath())
+ .replaceQuery(null)
+ .fragment(null)
+ .build();
+ String scheme = uriComponents.getScheme();
+ uriVariables.put("baseScheme", scheme == null ? "" : scheme);
+ String host = uriComponents.getHost();
+ uriVariables.put("baseHost", host == null ? "" : host);
+ // following logic is based on HierarchicalUriComponents#toUriString()
+ int port = uriComponents.getPort();
+ uriVariables.put("basePort", port == -1 ? "" : ":" + port);
+ String path = uriComponents.getPath();
+ if (StringUtils.hasLength(path)) {
+ if (path.charAt(0) != PATH_DELIMITER) {
+ path = PATH_DELIMITER + path;
+ }
+ }
+ uriVariables.put("basePath", path == null ? "" : path);
+ uriVariables.put("baseUrl", uriComponents.toUriString());
+
+ uriVariables.put("action", action == null ? "" : action);
+
+ String redirectUri = getRedirectUri(request);
+ log.trace("Redirect URI - {}.", redirectUri);
+
+ return UriComponentsBuilder.fromUriString(redirectUri)
+ .buildAndExpand(uriVariables)
+ .toUriString();
+ }
+
+ private String getRedirectUri(HttpServletRequest request) {
+ String loginProcessingUri = oauth2Configuration != null ? oauth2Configuration.getLoginProcessingUrl() : DEFAULT_LOGIN_PROCESSING_URI;
+
+ String scheme = MiscUtils.getScheme(request);
+ String domainName = MiscUtils.getDomainName(request);
+ int port = MiscUtils.getPort(request);
+ String baseUrl = scheme + "://" + domainName;
+ if (needsPort(scheme, port)){
+ baseUrl += ":" + port;
+ }
+ return baseUrl + loginProcessingUri;
+ }
+
+ private boolean needsPort(String scheme, int port) {
+ boolean isHttpDefault = "http".equals(scheme.toLowerCase()) && port == 80;
+ boolean isHttpsDefault = "https".equals(scheme.toLowerCase()) && port == 443;
+ return !isHttpDefault && !isHttpsDefault;
+ }
+
+ /**
+ * Creates nonce and its hash for use in OpenID Connect 1.0 Authentication Requests.
+ *
+ * @param attributes where the {@link OidcParameterNames#NONCE} is stored for the authentication request
+ * @param additionalParameters where the {@link OidcParameterNames#NONCE} hash is added for the authentication request
+ *
+ * @since 5.2
+ * @see 3.1.2.1. Authentication Request
+ */
+ private void addNonceParameters(Map attributes, Map additionalParameters) {
+ try {
+ String nonce = this.secureKeyGenerator.generateKey();
+ String nonceHash = createHash(nonce);
+ attributes.put(OidcParameterNames.NONCE, nonce);
+ additionalParameters.put(OidcParameterNames.NONCE, nonceHash);
+ } catch (NoSuchAlgorithmException e) { }
+ }
+
+ /**
+ * Creates and adds additional PKCE parameters for use in the OAuth 2.0 Authorization and Access Token Requests
+ *
+ * @param attributes where {@link PkceParameterNames#CODE_VERIFIER} is stored for the token request
+ * @param additionalParameters where {@link PkceParameterNames#CODE_CHALLENGE} and, usually,
+ * {@link PkceParameterNames#CODE_CHALLENGE_METHOD} are added to be used in the authorization request.
+ *
+ * @since 5.2
+ * @see 1.1. Protocol Flow
+ * @see 4.1. Client Creates a Code Verifier
+ * @see 4.2. Client Creates the Code Challenge
+ */
+ private void addPkceParameters(Map attributes, Map additionalParameters) {
+ String codeVerifier = this.secureKeyGenerator.generateKey();
+ attributes.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);
+ try {
+ String codeChallenge = createHash(codeVerifier);
+ additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeChallenge);
+ additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256");
+ } catch (NoSuchAlgorithmException e) {
+ additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeVerifier);
+ }
+ }
+
+ private static String createHash(String value) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ byte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));
+ return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/MvcCorsProperties.java b/application/src/main/java/org/thingsboard/server/config/MvcCorsProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..2205cdc017d6551e3a4237f7931d737afeb2bcc1
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/MvcCorsProperties.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by yyh on 2017/5/2.
+ * CORS configuration
+ */
+@Configuration
+@ConfigurationProperties(prefix = "spring.mvc.cors")
+public class MvcCorsProperties {
+
+ private Map mappings = new HashMap<>();
+
+ public MvcCorsProperties() {
+ super();
+ }
+
+ public Map getMappings() {
+ return mappings;
+ }
+
+ public void setMappings(Map mappings) {
+ this.mappings = mappings;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..085b05a33be39c593b3ffc0af5d6595c01855226
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.thingsboard.server.common.data.StringUtils;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.tools.TbRateLimits;
+import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
+import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
+import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+@Slf4j
+@Component
+public class RateLimitProcessingFilter extends OncePerRequestFilter {
+
+ @Autowired
+ private ThingsboardErrorResponseHandler errorResponseHandler;
+
+ @Autowired
+ @Lazy
+ private TbTenantProfileCache tenantProfileCache;
+
+ private final ConcurrentMap perTenantLimits = new ConcurrentHashMap<>();
+ private final ConcurrentMap perCustomerLimits = new ConcurrentHashMap<>();
+
+ @Override
+ public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+ SecurityUser user = getCurrentUser();
+ if (user != null && !user.isSystemAdmin()) {
+ var profile = tenantProfileCache.get(user.getTenantId());
+ if (profile == null) {
+ log.debug("[{}] Failed to lookup tenant profile", user.getTenantId());
+ errorResponseHandler.handle(new BadCredentialsException("Failed to lookup tenant profile"), response);
+ return;
+ }
+ var profileConfiguration = profile.getDefaultProfileConfiguration();
+ if (!checkRateLimits(user.getTenantId(), profileConfiguration.getTenantServerRestLimitsConfiguration(), perTenantLimits, response)) {
+ return;
+ }
+ if (user.isCustomerUser()) {
+ if (!checkRateLimits(user.getCustomerId(), profileConfiguration.getCustomerServerRestLimitsConfiguration(), perCustomerLimits, response)) {
+ return;
+ }
+ }
+ }
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ protected boolean shouldNotFilterAsyncDispatch() {
+ return false;
+ }
+
+ @Override
+ protected boolean shouldNotFilterErrorDispatch() {
+ return false;
+ }
+
+ private boolean checkRateLimits(I ownerId, String rateLimitConfig, Map rateLimitsMap, ServletResponse response) {
+ if (StringUtils.isNotEmpty(rateLimitConfig)) {
+ TbRateLimits rateLimits = rateLimitsMap.get(ownerId);
+ if (rateLimits == null || !rateLimits.getConfiguration().equals(rateLimitConfig)) {
+ rateLimits = new TbRateLimits(rateLimitConfig);
+ rateLimitsMap.put(ownerId, rateLimits);
+ }
+
+ if (!rateLimits.tryConsume()) {
+ errorResponseHandler.handle(new TbRateLimitsException(ownerId.getEntityType()), (HttpServletResponse) response);
+ return false;
+ }
+ } else {
+ rateLimitsMap.remove(ownerId);
+ }
+
+ return true;
+ }
+
+ protected SecurityUser getCurrentUser() {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) {
+ return (SecurityUser) authentication.getPrincipal();
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..e23b629fedd126c85afbfcddce59f9707ed9d2ed
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.scheduling.config.ScheduledTaskRegistrar;
+
+@Configuration
+@EnableScheduling
+public class SchedulingConfiguration implements SchedulingConfigurer {
+
+ @Override
+ public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
+ taskRegistrar.setScheduler(taskScheduler());
+ }
+
+ @Bean(destroyMethod="shutdown")
+ public TaskScheduler taskScheduler() {
+ ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler();
+ threadPoolScheduler.setThreadNamePrefix("TB-Scheduling-");
+ threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
+ threadPoolScheduler.setRemoveOnCancelPolicy(true);
+ return threadPoolScheduler;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..c53cf1b504bdfc66b4b172b548681aee6dc3c61f
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
@@ -0,0 +1,397 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.config;
+
+import com.fasterxml.classmate.TypeResolver;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.thingsboard.server.common.data.StringUtils;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.security.Authority;
+import org.thingsboard.server.exception.ThingsboardCredentialsExpiredResponse;
+import org.thingsboard.server.exception.ThingsboardErrorResponse;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.security.auth.rest.LoginRequest;
+import org.thingsboard.server.service.security.auth.rest.LoginResponse;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ExampleBuilder;
+import springfox.documentation.builders.OperationBuilder;
+import springfox.documentation.builders.RepresentationBuilder;
+import springfox.documentation.builders.RequestParameterBuilder;
+import springfox.documentation.builders.ResponseBuilder;
+import springfox.documentation.schema.Example;
+import springfox.documentation.service.ApiDescription;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiListing;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.HttpLoginPasswordScheme;
+import springfox.documentation.service.ParameterType;
+import springfox.documentation.service.Response;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.service.SecurityScheme;
+import springfox.documentation.service.Tag;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.ApiListingBuilderPlugin;
+import springfox.documentation.spi.service.ApiListingScannerPlugin;
+import springfox.documentation.spi.service.contexts.ApiListingContext;
+import springfox.documentation.spi.service.contexts.DocumentationContext;
+import springfox.documentation.spi.service.contexts.OperationContext;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator;
+import springfox.documentation.swagger.common.SwaggerPluginSupport;
+import springfox.documentation.swagger.web.DocExpansion;
+import springfox.documentation.swagger.web.ModelRendering;
+import springfox.documentation.swagger.web.OperationsSorter;
+import springfox.documentation.swagger.web.UiConfiguration;
+import springfox.documentation.swagger.web.UiConfigurationBuilder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.function.Predicate.not;
+import static springfox.documentation.builders.PathSelectors.any;
+import static springfox.documentation.builders.PathSelectors.regex;
+
+@Slf4j
+@Configuration
+@TbCoreComponent
+@Profile("!test")
+public class SwaggerConfiguration {
+
+ @Value("${swagger.api_path_regex}")
+ private String apiPathRegex;
+ @Value("${swagger.security_path_regex}")
+ private String securityPathRegex;
+ @Value("${swagger.non_security_path_regex}")
+ private String nonSecurityPathRegex;
+ @Value("${swagger.title}")
+ private String title;
+ @Value("${swagger.description}")
+ private String description;
+ @Value("${swagger.contact.name}")
+ private String contactName;
+ @Value("${swagger.contact.url}")
+ private String contactUrl;
+ @Value("${swagger.contact.email}")
+ private String contactEmail;
+ @Value("${swagger.license.title}")
+ private String licenseTitle;
+ @Value("${swagger.license.url}")
+ private String licenseUrl;
+ @Value("${swagger.version}")
+ private String version;
+ @Value("${app.version:unknown}")
+ private String appVersion;
+
+ @Bean
+ public Docket thingsboardApi() {
+ TypeResolver typeResolver = new TypeResolver();
+ return new Docket(DocumentationType.OAS_30)
+ .groupName("thingsboard")
+ .apiInfo(apiInfo())
+ .additionalModels(
+ typeResolver.resolve(ThingsboardErrorResponse.class),
+ typeResolver.resolve(ThingsboardCredentialsExpiredResponse.class),
+ typeResolver.resolve(LoginRequest.class),
+ typeResolver.resolve(LoginResponse.class)
+ )
+ .select()
+ .paths(apiPaths())
+ .paths(any())
+ .build()
+ .globalResponses(HttpMethod.GET,
+ defaultErrorResponses(false)
+ )
+ .globalResponses(HttpMethod.POST,
+ defaultErrorResponses(true)
+ )
+ .globalResponses(HttpMethod.DELETE,
+ defaultErrorResponses(false)
+ )
+ .securitySchemes(newArrayList(httpLogin()))
+ .securityContexts(newArrayList(securityContext()))
+ .enableUrlTemplating(true);
+ }
+
+ @Bean
+ @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
+ ApiListingScannerPlugin loginEndpointListingScanner(final CachingOperationNameGenerator operationNames) {
+ return new ApiListingScannerPlugin() {
+ @Override
+ public List apply(DocumentationContext context) {
+ return List.of(loginEndpointApiDescription(operationNames));
+ }
+
+ @Override
+ public boolean supports(DocumentationType delimiter) {
+ return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
+ }
+ };
+ }
+
+ @Bean
+ @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
+ ApiListingBuilderPlugin loginEndpointListingBuilder() {
+ return new ApiListingBuilderPlugin() {
+ @Override
+ public void apply(ApiListingContext apiListingContext) {
+ if (apiListingContext.getResourceGroup().getGroupName().equals("default")) {
+ ApiListing apiListing = apiListingContext.apiListingBuilder().build();
+ if (apiListing.getResourcePath().equals("/api/auth/login")) {
+ apiListingContext.apiListingBuilder().tags(Set.of(new Tag("login-endpoint", "Login Endpoint")));
+ apiListingContext.apiListingBuilder().description("Login Endpoint");
+ }
+ }
+ }
+
+ @Override
+ public boolean supports(DocumentationType delimiter) {
+ return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
+ }
+ };
+ }
+
+ @Bean
+ UiConfiguration uiConfig() {
+ return UiConfigurationBuilder.builder()
+ .deepLinking(true)
+ .displayOperationId(false)
+ .defaultModelsExpandDepth(1)
+ .defaultModelExpandDepth(1)
+ .defaultModelRendering(ModelRendering.EXAMPLE)
+ .displayRequestDuration(false)
+ .docExpansion(DocExpansion.NONE)
+ .filter(false)
+ .maxDisplayedTags(null)
+ .operationsSorter(OperationsSorter.ALPHA)
+ .showExtensions(false)
+ .showCommonExtensions(false)
+ .supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS)
+ .validatorUrl(null)
+ .persistAuthorization(true)
+ .syntaxHighlightActivate(true)
+ .syntaxHighlightTheme("agate")
+ .build();
+ }
+
+ private SecurityScheme httpLogin() {
+ return HttpLoginPasswordScheme
+ .X_AUTHORIZATION_BUILDER
+ .loginEndpoint("/api/auth/login")
+ .name("HTTP login form")
+ .description("Enter Username / Password")
+ .build();
+ }
+
+ private SecurityContext securityContext() {
+ return SecurityContext.builder()
+ .securityReferences(defaultAuth())
+ .operationSelector(securityPathOperationSelector())
+ .build();
+ }
+
+ private Predicate apiPaths() {
+ return regex(apiPathRegex);
+ }
+
+ private Predicate securityPathOperationSelector() {
+ return new SecurityPathOperationSelector(securityPathRegex, nonSecurityPathRegex);
+ }
+
+ List defaultAuth() {
+ AuthorizationScope[] authorizationScopes = new AuthorizationScope[3];
+ authorizationScopes[0] = new AuthorizationScope(Authority.SYS_ADMIN.name(), "System administrator");
+ authorizationScopes[1] = new AuthorizationScope(Authority.TENANT_ADMIN.name(), "Tenant administrator");
+ authorizationScopes[2] = new AuthorizationScope(Authority.CUSTOMER_USER.name(), "Customer");
+ return newArrayList(
+ new SecurityReference("HTTP login form", authorizationScopes));
+ }
+
+ private ApiInfo apiInfo() {
+ String apiVersion = version;
+ if (StringUtils.isEmpty(apiVersion)) {
+ apiVersion = appVersion;
+ }
+ return new ApiInfoBuilder()
+ .title(title)
+ .description(description)
+ .contact(new Contact(contactName, contactUrl, contactEmail))
+ .license(licenseTitle)
+ .licenseUrl(licenseUrl)
+ .version(apiVersion)
+ .build();
+ }
+
+ private ApiDescription loginEndpointApiDescription(final CachingOperationNameGenerator operationNames) {
+ return new ApiDescription(null, "/api/auth/login", "Login method to get user JWT token data", "Login endpoint", Collections.singletonList(
+ new OperationBuilder(operationNames)
+ .summary("Login method to get user JWT token data")
+ .tags(Set.of("login-endpoint"))
+ .authorizations(new ArrayList<>())
+ .position(0)
+ .codegenMethodNameStem("loginPost")
+ .method(HttpMethod.POST)
+ .notes("Login method used to authenticate user and get JWT token data.\n\nValue of the response **token** " +
+ "field can be used as **X-Authorization** header value:\n\n`X-Authorization: Bearer $JWT_TOKEN_VALUE`.")
+ .requestParameters(
+ List.of(
+ new RequestParameterBuilder()
+ .in(ParameterType.BODY)
+ .required(true)
+ .description("Login request")
+ .content(c ->
+ c.requestBody(true)
+ .representation(MediaType.APPLICATION_JSON)
+ .apply(classRepresentation(LoginRequest.class, false))
+ )
+ .build()
+ )
+ )
+ .responses(loginResponses())
+ .build()
+ ), false);
+ }
+
+ private Collection loginResponses() {
+ List responses = new ArrayList<>();
+ responses.add(
+ new ResponseBuilder()
+ .code("200")
+ .description("OK")
+ .representation(MediaType.APPLICATION_JSON)
+ .apply(classRepresentation(LoginResponse.class, true)).
+ build()
+ );
+ responses.addAll(loginErrorResponses());
+ return responses;
+ }
+
+ /** Helper methods **/
+
+ private List defaultErrorResponses(boolean isPost) {
+ return List.of(
+ errorResponse("400", "Bad Request",
+ ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST)),
+ errorResponse("401", "Unauthorized",
+ ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
+ errorResponse("403", "Forbidden",
+ ThingsboardErrorResponse.of("You don't have permission to perform this operation!",
+ ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)),
+ errorResponse("404", "Not Found",
+ ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND)),
+ errorResponse("429", "Too Many Requests",
+ ThingsboardErrorResponse.of("Too many requests for current tenant!",
+ ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS))
+ );
+ }
+
+ private List loginErrorResponses() {
+ return List.of(
+ errorResponse("401", "Unauthorized",
+ List.of(
+ errorExample("bad-credentials", "Bad credentials",
+ ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
+ errorExample("token-expired", "JWT token expired",
+ ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)),
+ errorExample("account-disabled", "Disabled account",
+ ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
+ errorExample("account-locked", "Locked account",
+ ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
+ errorExample("authentication-failed", "General authentication error",
+ ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED))
+ )
+ ),
+ errorResponse("401 ", "Unauthorized (**Expired credentials**)",
+ List.of(
+ errorExample("credentials-expired", "Expired credentials",
+ ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30)))
+ ), ThingsboardCredentialsExpiredResponse.class
+ )
+ );
+ }
+
+ private Response errorResponse(String code, String description, ThingsboardErrorResponse example) {
+ return errorResponse(code, description, List.of(errorExample("error-code-" + code, description, example)));
+ }
+
+ private Response errorResponse(String code, String description, List examples) {
+ return errorResponse(code, description, examples, ThingsboardErrorResponse.class);
+ }
+
+ private Response errorResponse(String code, String description, List examples,
+ Class extends ThingsboardErrorResponse> errorResponseClass) {
+ return new ResponseBuilder()
+ .code(code)
+ .description(description)
+ .examples(examples)
+ .representation(MediaType.APPLICATION_JSON)
+ .apply(classRepresentation(errorResponseClass, true))
+ .build();
+ }
+
+ private Example errorExample(String id, String summary, ThingsboardErrorResponse example) {
+ return new ExampleBuilder()
+ .mediaType(MediaType.APPLICATION_JSON_VALUE)
+ .summary(summary)
+ .id(id)
+ .value(example).build();
+ }
+
+ private Consumer classRepresentation(Class> clazz, boolean isResponse) {
+ return r -> r.model(
+ m ->
+ m.referenceModel(ref ->
+ ref.key(k ->
+ k.qualifiedModelName(q ->
+ q.namespace(clazz.getPackageName())
+ .name(clazz.getSimpleName())).isResponse(isResponse)))
+ );
+ }
+
+ private static class SecurityPathOperationSelector implements Predicate {
+
+ private final Predicate securityPathSelector;
+
+ SecurityPathOperationSelector(String securityPathRegex, String nonSecurityPathRegex) {
+ this.securityPathSelector = regex(securityPathRegex).and(
+ not(
+ regex(nonSecurityPathRegex)
+ ));
+ }
+
+ @Override
+ public boolean test(OperationContext operationContext) {
+ return this.securityPathSelector.test(operationContext.requestMappingPattern());
+ }
+ }
+
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..112fa5c1221de1ce06975a9aeff5d3d91294a275
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.config;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.support.ResourceBundleMessageSource;
+
+@Configuration
+public class ThingsboardMessageConfiguration {
+
+ @Bean
+ @Primary
+ public MessageSource messageSource() {
+ ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
+ messageSource.setBasename("i18n/messages");
+ messageSource.setDefaultEncoding("UTF-8");
+ return messageSource;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6540f007aca9b2a95a8b77fcc8bf64f7f11ad42
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -0,0 +1,252 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
+import org.springframework.security.config.annotation.ObjectPostProcessor;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
+import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider;
+import org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter;
+import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider;
+import org.thingsboard.server.service.security.auth.jwt.RefreshTokenProcessingFilter;
+import org.thingsboard.server.service.security.auth.jwt.SkipPathRequestMatcher;
+import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor;
+import org.thingsboard.server.service.security.auth.oauth2.HttpCookieOAuth2AuthorizationRequestRepository;
+import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider;
+import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter;
+import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled=true)
+@Order(SecurityProperties.BASIC_AUTH_ORDER)
+@TbCoreComponent
+public class ThingsboardSecurityConfiguration {
+
+ public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
+ public static final String JWT_TOKEN_HEADER_PARAM_V2 = "Authorization";
+ public static final String JWT_TOKEN_QUERY_PARAM = "token";
+
+ public static final String WEBJARS_ENTRY_POINT = "/webjars/**";
+ public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**";
+ public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
+ public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
+ public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
+ protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**"};
+ public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
+ public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
+
+ @Autowired private ThingsboardErrorResponseHandler restAccessDeniedHandler;
+
+ @Autowired(required = false)
+ @Qualifier("oauth2AuthenticationSuccessHandler")
+ private AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler;
+
+ @Autowired(required = false)
+ @Qualifier("oauth2AuthenticationFailureHandler")
+ private AuthenticationFailureHandler oauth2AuthenticationFailureHandler;
+
+ @Autowired(required = false)
+ private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
+
+ @Autowired
+ @Qualifier("defaultAuthenticationSuccessHandler")
+ private AuthenticationSuccessHandler successHandler;
+
+ @Autowired
+ @Qualifier("defaultAuthenticationFailureHandler")
+ private AuthenticationFailureHandler failureHandler;
+
+ @Autowired private RestAuthenticationProvider restAuthenticationProvider;
+ @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider;
+ @Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider;
+
+ @Autowired(required = false) OAuth2Configuration oauth2Configuration;
+
+ @Autowired
+ @Qualifier("jwtHeaderTokenExtractor")
+ private TokenExtractor jwtHeaderTokenExtractor;
+
+ @Autowired
+ @Qualifier("jwtQueryTokenExtractor")
+ private TokenExtractor jwtQueryTokenExtractor;
+
+ @Autowired private AuthenticationManager authenticationManager;
+
+ @Autowired private ObjectMapper objectMapper;
+
+ @Autowired private RateLimitProcessingFilter rateLimitProcessingFilter;
+
+ @Bean
+ protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception {
+ RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
+ filter.setAuthenticationManager(this.authenticationManager);
+ return filter;
+ }
+
+ @Bean
+ protected RestPublicLoginProcessingFilter buildRestPublicLoginProcessingFilter() throws Exception {
+ RestPublicLoginProcessingFilter filter = new RestPublicLoginProcessingFilter(PUBLIC_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
+ filter.setAuthenticationManager(this.authenticationManager);
+ return filter;
+ }
+
+ protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
+ List pathsToSkip = new ArrayList<>(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS));
+ pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,
+ PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
+ SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
+ JwtTokenAuthenticationProcessingFilter filter
+ = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher);
+ filter.setAuthenticationManager(this.authenticationManager);
+ return filter;
+ }
+
+ @Bean
+ protected RefreshTokenProcessingFilter buildRefreshTokenProcessingFilter() throws Exception {
+ RefreshTokenProcessingFilter filter = new RefreshTokenProcessingFilter(TOKEN_REFRESH_ENTRY_POINT, successHandler, failureHandler, objectMapper);
+ filter.setAuthenticationManager(this.authenticationManager);
+ return filter;
+ }
+
+ @Bean
+ protected JwtTokenAuthenticationProcessingFilter buildWsJwtTokenAuthenticationProcessingFilter() throws Exception {
+ AntPathRequestMatcher matcher = new AntPathRequestMatcher(WS_TOKEN_BASED_AUTH_ENTRY_POINT);
+ JwtTokenAuthenticationProcessingFilter filter
+ = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtQueryTokenExtractor, matcher);
+ filter.setAuthenticationManager(this.authenticationManager);
+ return filter;
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(ObjectPostProcessor