├── .circleci ├── config.yml └── old.yml ├── .github └── FUNDING.yml ├── .gitignore ├── .prettierrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist.zip ├── dist ├── LICENSE ├── README.md ├── img │ ├── controls.png │ ├── logo.svg │ └── server.png ├── module.js ├── module.js.LICENSE.txt ├── module.js.map ├── partials │ ├── module.html │ ├── options.display.html │ └── options.server.html └── plugin.json ├── grabpl ├── jest.config.js ├── package.json ├── src ├── README.md ├── img │ ├── button.png │ ├── controls.png │ ├── logo.svg │ └── server.png ├── module.test.ts ├── module.ts ├── partials │ ├── module.html │ ├── options.display.html │ └── options.server.html ├── plugin.json └── style.css ├── tsconfig.json ├── yarn.lock └── yarn.lock.bak /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | parameters: 4 | ssh-fingerprint: 5 | type: string 6 | default: ${GITHUB_SSH_FINGERPRINT} 7 | 8 | aliases: 9 | # Workflow filters 10 | - &filter-only-master 11 | branches: 12 | only: master 13 | - &filter-only-release 14 | branches: 15 | only: /^v[1-9]*[0-9]+\.[1-9]*[0-9]+\.x$/ 16 | 17 | workflows: 18 | plugin_workflow: 19 | jobs: 20 | - yarn_install 21 | - build_docs: 22 | requires: 23 | - yarn_install 24 | - build_frontend: 25 | requires: 26 | - yarn_install 27 | - build_backend: 28 | requires: 29 | - yarn_install 30 | - package: 31 | requires: 32 | - build_frontend 33 | - build_backend 34 | - build_docs 35 | - provisioning: 36 | requires: 37 | - build_frontend 38 | - build_backend 39 | - build_docs 40 | - e2e_canary: 41 | requires: 42 | - provisioning 43 | - package 44 | - report: 45 | requires: 46 | - package 47 | - e2e_canary 48 | - approve_release: 49 | type: approval 50 | requires: 51 | - report 52 | filters: *filter-only-release 53 | - publish_github_release: 54 | requires: 55 | - approve_release 56 | filters: *filter-only-release 57 | 58 | executors: 59 | default_exec: # declares a reusable executor 60 | docker: 61 | - image: srclosson/grafana-plugin-ci-alpine:latest 62 | e2e_exec: 63 | docker: 64 | - image: srclosson/grafana-plugin-ci-e2e:latest 65 | 66 | jobs: 67 | yarn_install: 68 | executor: default_exec 69 | steps: 70 | - checkout 71 | - restore_cache: 72 | name: restore node_modules 73 | keys: 74 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 75 | - run: 76 | name: Install dependencies 77 | command: | 78 | mkdir ci 79 | [ -f ~/project/node_modules/.bin/grafana-toolkit ] || yarn install --frozen-lockfile 80 | - save_cache: 81 | name: save node_modules 82 | paths: 83 | - ~/project/node_modules 84 | key: build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 85 | - save_cache: 86 | name: save cypress cache 87 | paths: 88 | - ~/.cache/Cypress 89 | key: cypress-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 90 | 91 | build_docs: 92 | executor: default_exec 93 | steps: 94 | - checkout 95 | - restore_cache: 96 | name: restore node_modules 97 | keys: 98 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 99 | - run: 100 | name: Build docs 101 | command: | 102 | ./node_modules/.bin/grafana-toolkit plugin:ci-docs 103 | [ -d "dist" ] || circleci-agent step halt 104 | - persist_to_workspace: 105 | root: . 106 | paths: 107 | - dist 108 | 109 | build_frontend: 110 | executor: default_exec 111 | steps: 112 | - checkout 113 | - restore_cache: 114 | name: restore node_modules 115 | keys: 116 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 117 | - run: 118 | name: Build and test frontend 119 | command: ./node_modules/.bin/grafana-toolkit plugin:ci-build 120 | - persist_to_workspace: 121 | root: . 122 | paths: 123 | - dist 124 | 125 | build_backend: 126 | executor: default_exec 127 | steps: 128 | - checkout 129 | - restore_cache: 130 | name: restore node_modules 131 | keys: 132 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 133 | - run: 134 | name: Exit if no backend 135 | command: | 136 | [ -f "Magefile.go" ] || circleci-agent step halt 137 | - run: 138 | name: Build backend 139 | command: mage -v buildAll 140 | - run: 141 | name: Test backend 142 | command: | 143 | mage -v lint 144 | mage -v coverage 145 | - persist_to_workspace: 146 | root: . 147 | paths: 148 | - dist 149 | 150 | package: 151 | executor: default_exec 152 | steps: 153 | - checkout 154 | - restore_cache: 155 | name: restore node_modules 156 | keys: 157 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 158 | - attach_workspace: 159 | at: . 160 | - run: 161 | name: Move results to ci folder 162 | command: ./node_modules/.bin/grafana-toolkit plugin:ci-build --finish 163 | - run: 164 | name: Package distribution 165 | command: | 166 | ./node_modules/.bin/grafana-toolkit plugin:ci-package 167 | - persist_to_workspace: 168 | root: . 169 | paths: 170 | - ci/jobs/package 171 | - ci/packages 172 | - ci/dist 173 | - ci/grafana-test-env 174 | - store_artifacts: 175 | path: ci 176 | 177 | provisioning: 178 | executor: default_exec 179 | steps: 180 | - run: 181 | name: Continue if this plugin has a provisioning path 182 | command: | 183 | [ -z "${PROVISIONING_REPO_PATH}" ] && circleci-agent step halt 184 | - checkout 185 | - add_ssh_keys: 186 | fingerprints: 187 | - << pipeline.parameters.ssh-fingerprint >> 188 | - run: 189 | name: Checkout provisioning files 190 | command: git clone --depth 1 git@github.com:grafana/plugin-provisioning.git 191 | - run: 192 | name: Prepare task output dir 193 | command: | 194 | mkdir ci # Avoid error if not exists 195 | mkdir ci/jobs # Avoid error if not exists 196 | mkdir ci/jobs/provisioning 197 | mv plugin-provisioning/${PROVISIONING_REPO_PATH}/* ci/jobs/provisioning 198 | - persist_to_workspace: 199 | root: . 200 | paths: 201 | - ci/jobs/provisioning 202 | 203 | e2e_canary: 204 | executor: e2e_exec 205 | steps: 206 | - run: 207 | name: Continue if this plugin has a provisioning path 208 | command: | 209 | [ -z "${PROVISIONING_REPO_PATH}" ] && circleci-agent step halt 210 | - checkout 211 | - attach_workspace: 212 | at: . 213 | - restore_cache: 214 | name: Restore node_modules 215 | keys: 216 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 217 | - restore_cache: 218 | name: Restore cypress cache 219 | keys: 220 | - cypress-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 221 | - run: 222 | name: Exit if no e2e tests configured 223 | command: | 224 | [ -d cypress ] || circleci-agent step halt 225 | - run: 226 | name: Setup Grafana (local install) 227 | command: | 228 | ginstall canary 229 | /opt/grafana/bin/grafana-server -config ci/grafana-test-env/custom.ini -homepath /opt/grafana & 230 | ## To make sure grafana has started up 231 | while ! $(netstat -tulpn | grep 3000 >/dev/null 2>&1); do sleep 1; done 232 | /opt/grafana/bin/grafana-cli --version 233 | - run: 234 | name: Copy provisioning files 235 | command: cp -r ci/jobs/provisioning provisioning/ 236 | - run: 237 | name: Run e2e tests 238 | command: | 239 | # If the tests fail, but GRAFANACI_STRICT_E2E=0, don't worry about it 240 | ./node_modules/.bin/grafana-e2e run \ 241 | || ( 242 | [ "$GRAFANACI_STRICT_E2E" -eq 0 ] && echo "Bypassing fail. ci-nostrict enabled" 243 | ) 244 | - run: 245 | when: always 246 | name: Prepare task output dir 247 | command: | 248 | # TODO: probably move all of this to `@grafana/toolkit plugin:ci-test` 249 | mkdir ci/jobs/e2e_canary 250 | # only copy if they exist 251 | cp cypress/screenshots/*.* ci/jobs/e2e_canary 2>/dev/null || : 252 | cp cypress/videos/*.* ci/jobs/e2e_canary 2>/dev/null || : 253 | - persist_to_workspace: 254 | root: . 255 | paths: 256 | - ci/jobs/e2e_canary 257 | - store_test_results: 258 | path: ci/jobs/e2e_canary 259 | - store_artifacts: 260 | path: ci/jobs/e2e_canary 261 | 262 | report: 263 | executor: default_exec 264 | steps: 265 | - checkout 266 | - attach_workspace: 267 | at: . 268 | - restore_cache: 269 | name: restore node_modules 270 | keys: 271 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 272 | - run: 273 | name: Toolkit report 274 | command: | 275 | ./node_modules/.bin/grafana-toolkit plugin:ci-report 276 | - store_artifacts: 277 | path: ci 278 | 279 | publish_github_release: 280 | executor: default_exec 281 | steps: 282 | - checkout 283 | - add_ssh_keys: 284 | fingerprints: 285 | - "dc:60:ab:c7:2d:8c:82:50:2a:2a:97:1a:c0:66:83:14" 286 | - attach_workspace: 287 | at: . 288 | - restore_cache: 289 | name: restore node_modules 290 | keys: 291 | - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} 292 | - run: 293 | name: "Publish Release on GitHub" 294 | command: | 295 | ./node_modules/.bin/grafana-toolkit plugin:github-publish 296 | -------------------------------------------------------------------------------- /.circleci/old.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | aliases: 4 | # Workflow filters 5 | - &filter-only-master 6 | branches: 7 | only: master 8 | 9 | defaults: &defaults 10 | docker: 11 | - image: circleci/node:12 12 | 13 | jobs: 14 | build_plugin: 15 | <<: *defaults 16 | steps: 17 | - checkout 18 | - restore_cache: 19 | keys: 20 | - yarn-packages-{{ checksum "yarn.lock" }} 21 | - run: 22 | name: Install dependencies 23 | command: | 24 | yarn install --pure-lockfile 25 | - run: 26 | name: Build and test frontend 27 | command: | 28 | npx grafana-toolkit plugin:ci-build 29 | npx grafana-toolkit plugin:ci-build --finish 30 | - save_cache: 31 | paths: 32 | - node_modules 33 | key: yarn-packages-{{ checksum "yarn.lock" }} 34 | - persist_to_workspace: 35 | root: . 36 | paths: 37 | - ci 38 | build_docs: 39 | <<: *defaults 40 | steps: 41 | - checkout 42 | - restore_cache: 43 | keys: 44 | - yarn-packages-{{ checksum "yarn.lock" }} 45 | - run: 46 | name: Install dependencies 47 | command: | 48 | yarn install --pure-lockfile 49 | - run: 50 | name: Build Docs 51 | command: | 52 | mkdir ci # Avoid error if not exists 53 | ./node_modules/.bin/grafana-toolkit plugin:ci-docs 54 | - save_cache: 55 | paths: 56 | - node_modules 57 | key: yarn-packages-{{ checksum "yarn.lock" }} 58 | - persist_to_workspace: 59 | root: . 60 | paths: 61 | - ci 62 | 63 | package: 64 | <<: *defaults 65 | steps: 66 | - checkout 67 | - attach_workspace: 68 | at: . 69 | - restore_cache: 70 | keys: 71 | - yarn-packages-{{ checksum "yarn.lock" }} 72 | - run: 73 | name: Package Distribution 74 | command: | 75 | yarn install --pure-lockfile 76 | ./node_modules/.bin/grafana-toolkit plugin:ci-package 77 | - persist_to_workspace: 78 | root: . 79 | paths: 80 | - ci/jobs/package 81 | - ci/packages 82 | - ci/dist 83 | - ci/grafana-test-env 84 | 85 | test_integration: 86 | docker: 87 | - image: circleci/node:12-browsers 88 | steps: 89 | - checkout 90 | - attach_workspace: 91 | at: . 92 | - restore_cache: 93 | keys: 94 | - yarn-packages-{{ checksum "yarn.lock" }} 95 | - run: 96 | name: Setup Grafana (local install) 97 | command: | 98 | wget https://dl.grafana.com/oss/release/grafana_6.6.1_amd64.deb 99 | sudo apt-get install -y adduser libfontconfig1 100 | sudo dpkg -i grafana_6.6.1_amd64.deb 101 | sudo apt-get install locate 102 | sudo updatedb 103 | sudo locate grafana 104 | sudo cat /etc/grafana/grafana.ini 105 | sudo echo ------------------------ 106 | sudo cp ci/grafana-test-env/custom.ini /usr/share/grafana/conf/custom.ini 107 | sudo cp ci/grafana-test-env/custom.ini /etc/grafana/grafana.ini 108 | sudo service grafana-server start 109 | sudo grafana-cli --version 110 | - run: 111 | name: Run e2e tests 112 | command: | 113 | yarn install --pure-lockfile 114 | ./node_modules/.bin/grafana-toolkit plugin:test 115 | - persist_to_workspace: 116 | root: . 117 | paths: 118 | - ci/jobs/test_integration 119 | - store_test_results: 120 | path: ci/jobs/test_integration 121 | - store_artifacts: 122 | path: ci/jobs/test_integration 123 | 124 | report: 125 | docker: 126 | - image: circleci/node:12 127 | steps: 128 | - checkout 129 | - attach_workspace: 130 | at: . 131 | - restore_cache: 132 | keys: 133 | - yarn-packages-{{ checksum "yarn.lock" }} 134 | - run: 135 | name: Toolkit report 136 | command: | 137 | yarn install --pure-lockfile 138 | ./node_modules/.bin/grafana-toolkit plugin:ci-report 139 | - store_artifacts: 140 | path: ci 141 | 142 | publish_github_release: 143 | <<: *defaults 144 | steps: 145 | - checkout 146 | - add_ssh_keys: 147 | fingerprints: 148 | - "ff:6a:6d:26:8e:5f:27:1c:4f:34:35:93:2a:cb:11:3c" 149 | - attach_workspace: 150 | at: . 151 | - restore_cache: 152 | keys: 153 | - yarn-packages-{{ checksum "yarn.lock" }} 154 | - run: 155 | name: "Publish Release on GitHub" 156 | command: | 157 | yarn install --pure-lockfile 158 | ./node_modules/.bin/grafana-toolkit plugin:github-publish 159 | 160 | workflows: 161 | version: 2 162 | plugin_workflow: 163 | jobs: 164 | - build_plugin 165 | - build_docs 166 | - package: 167 | requires: 168 | - build_plugin 169 | - build_docs 170 | - test_integration: 171 | requires: 172 | - package 173 | - report: 174 | requires: 175 | - test_integration 176 | - approve_release: 177 | type: approval 178 | requires: 179 | - report 180 | filters: *filter-only-master 181 | - publish_github_release: 182 | requires: 183 | - approve_release 184 | filters: *filter-only-master -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: geeks_r_us 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | dist/ 34 | artifacts/ 35 | work/ 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # next.js build output 63 | .next 64 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require("./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json"), 3 | }; 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version: 1.0.0 2 | * initial working version of the mqtt-panel 3 | 4 | ## Version 1.0.1 5 | * fixed exception on wrong mqtt server url 6 | * panel variables can be used in server config section 7 | * slider debounced to reduce traffic 8 | 9 | ## Version 1.0.2 10 | * added basic auth 11 | * fixed some issues with updating 12 | * some refactoring 13 | 14 | ## Version 1.0.3 15 | * Security fix: bump jquery 3.5.0 16 | * Security fix: bump websocket-extension to 0.1.4 17 | * Security fix: bump angular to 1.8.0 18 | 19 | ## Version 1.0.4 20 | * Security fix: bump elliptic to 6.5.3 21 | * Changes in build process for Grafana 22 | 23 | ## Verion 1.0.5 24 | * Fixed errors in plugin.json 25 | 26 | ## Version 1.0.6 27 | * Securitiy fixes: bumped some critical dependencies 28 | * Fixed compiling issues (#12) 29 | * All option fields can process Grafana variables now 30 | * Added button widget (thx to ManuelBu96) 31 | 32 | ## Version 1.0.7 33 | * Fixed switch issue (#23) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MQTT Panel 2 | 3 | ![controls](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/logo.svg?sanitize=true "controls") 4 | [![CircleCI](https://circleci.com/gh/geeks-r-us/mqtt-panel.svg?style=svg)](https://circleci.com/gh/geeks-r-us/mqtt-panel) 5 | 6 | A panel to enable grafana to fulfil simple control tasks via MQTT. 7 | 8 | Currently it contains 3 types of control: 9 | 10 | ![controls](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/controls.png "controls") 11 | 12 | Each type can connect to a mqtt server and subscribe and publish to a topic. 13 | 14 | ![server](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/server.png "server") 15 | 16 | The panel is able to pick values from an json encoded topic by the use of [JSONata query language](https://docs.jsonata.org/overview.html) 17 | 18 | ### Limitations 19 | 20 | Due to the MQTT client runs in the browser it can only establish mqtt connection over websockets and can only connect 21 | to servers reachable by your browser. 22 | 23 | ### Testing 24 | 25 | If you like to test this plugin the easies way is to load it into a grafana docker container. 26 | 27 | ``` 28 | docker run -d -p 3000:3000 -e "GF_INSTALL_PLUGINS=https://github.com/geeks-r-us/mqtt-panel/releases/download/v1.0.7/geeksrus-mqtt-panel-1.0.7.zip;mqtt-panel" --name=grafana grafana/grafana 29 | ``` 30 | Open http://localhost:3000 in your browser 31 | 32 | This is my first attempt to write a grafana panel so every issue report, comment or PR are welcome. 33 | 34 | ### Support 35 | 36 | If you find this module useful please support me with a cup of [coffee](https://ko-fi.com/geeks_r_us) 37 | -------------------------------------------------------------------------------- /dist.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeks-r-us/mqtt-panel/b78867d659bb216e9ef09755edf83dfe6bebae28/dist.zip -------------------------------------------------------------------------------- /dist/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | ## MQTT Panel 2 | 3 | ![controls](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/logo.svg?sanitize=true "controls") 4 | [![CircleCI](https://circleci.com/gh/geeks-r-us/mqtt-panel.svg?style=svg)](https://circleci.com/gh/geeks-r-us/mqtt-panel) 5 | 6 | A panel to enable grafana to fulfil simple control tasks via MQTT. 7 | 8 | Currently it contains 4 types of control: 9 | 10 | ![controls](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/controls.png "controls") 11 | 12 | Each type can connect to a mqtt server and subscribe and publish to a topic. 13 | 14 | ![server](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/server.png "server") 15 | 16 | The panel is able to pick values from an json encoded topic by the use of [JSONata query language](https://docs.jsonata.org/overview.html) 17 | 18 | ### Limitations 19 | 20 | Due to the MQTT client runs in the browser it can only establish mqtt connection over websockets and can only connect 21 | to servers reachable by your browser. 22 | 23 | ### Testing 24 | 25 | If you like to test this plugin the easies way is to load it into a grafana docker container. 26 | 27 | ``` 28 | docker run -d -p 3000:3000 -e "GF_INSTALL_PLUGINS=https://github.com/geeks-r-us/mqtt-panel/releases/download/v1.0.7/geeksrus-mqtt-panel-1.0.7.zip;mqtt-panel" --name=grafana grafana/grafana 29 | ``` 30 | Open http://localhost:3000 in your browser 31 | 32 | This is my first attempt to write a grafana panel so every issue report, comment or PR are welcome. 33 | 34 | ### Support 35 | 36 | If you find this module useful please support me with a cup of [coffee](https://ko-fi.com/geeks_r_us) 37 | -------------------------------------------------------------------------------- /dist/img/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeks-r-us/mqtt-panel/b78867d659bb216e9ef09755edf83dfe6bebae28/dist/img/controls.png -------------------------------------------------------------------------------- /dist/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 37 | 39 | 42 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | 80 | 84 | 85 | 89 | MQTT 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /dist/img/server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeks-r-us/mqtt-panel/b78867d659bb216e9ef09755edf83dfe6bebae28/dist/img/server.png -------------------------------------------------------------------------------- /dist/module.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * The buffer module from node.js, for the browser. 3 | * 4 | * @author Feross Aboukhadijeh 5 | * @license MIT 6 | */ 7 | 8 | /*! ***************************************************************************** 9 | Copyright (c) Microsoft Corporation. All rights reserved. 10 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 11 | this file except in compliance with the License. You may obtain a copy of the 12 | License at http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 16 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 17 | MERCHANTABLITY OR NON-INFRINGEMENT. 18 | 19 | See the Apache Version 2.0 License for specific language governing permissions 20 | and limitations under the License. 21 | ***************************************************************************** */ 22 | 23 | /*! https://mths.be/punycode v1.4.1 by @mathias */ 24 | -------------------------------------------------------------------------------- /dist/partials/module.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 |
9 |
10 | 13 |
14 |
15 |
16 |
17 | 27 |
28 |
29 | {{ctrl.panel.viewModel.text}} : {{ctrl.panel.value}} 30 |
31 |
32 |
33 | 39 | 40 |
41 |
42 |
43 | 46 |
47 |
-------------------------------------------------------------------------------- /dist/partials/options.display.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Display
4 |
5 | 6 | 13 | 14 |
15 |
16 | 17 | 24 |
25 |
26 | 27 | 34 |
35 |
36 | 37 | 44 |
45 |
46 | 47 | 54 |
55 |
56 |
57 | 58 | 65 |
66 |
67 | 68 | 75 |
76 |
77 | 78 | 85 |
86 |
87 | -------------------------------------------------------------------------------- /dist/partials/options.server.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Connection
4 |
5 | 6 | 14 | 15 |
16 |
17 | 18 | 25 |
26 |
27 | 28 | 35 |
36 |
37 | 38 | 45 |
46 |
47 | 48 | 54 |
55 |
56 | 57 | 64 |
65 |
66 | 67 |
68 |
69 |
70 |
71 |
72 |
73 |
Authentication
74 |
75 | 76 | 83 | 84 |
85 |
86 | 87 | 93 |
94 |
95 | 96 | 102 |
103 |
104 |
105 | 108 |
109 | 110 |
111 | -------------------------------------------------------------------------------- /dist/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "panel", 3 | "name": "MQTT panel", 4 | "id": "geeksrus-mqtt-panel", 5 | "info": { 6 | "description": "A panel to enable grafana to fulfil simple control tasks via MQTT", 7 | "author": { 8 | "name": "geeks-r-us", 9 | "url": "https://github.com/geeks-r-us/mqtt-panel" 10 | }, 11 | "keywords": [ 12 | "MQTT" 13 | ], 14 | "logos": { 15 | "small": "img/logo.svg", 16 | "large": "img/logo.svg" 17 | }, 18 | "links": [], 19 | "screenshots": [ 20 | { 21 | "name": "Controls", 22 | "path": "img/controls.png" 23 | }, 24 | { 25 | "name": "Server settings", 26 | "path": "img/server.png" 27 | } 28 | ], 29 | "version": "1.0.7-1", 30 | "updated": "2021-05-17" 31 | }, 32 | "dependencies": { 33 | "grafanaVersion": "7.0.x", 34 | "grafanaDependency": ">=7.0.0", 35 | "plugins": [] 36 | } 37 | } -------------------------------------------------------------------------------- /grabpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeks-r-us/mqtt-panel/b78867d659bb216e9ef09755edf83dfe6bebae28/grabpl -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // This file is needed because it is used by vscode and other tools that 2 | // call `jest` directly. However, unless you are doing anything special 3 | // do not edit this file 4 | 5 | const standard = require('@grafana/toolkit/src/config/jest.plugin.config'); 6 | 7 | // This process will use the same config that `yarn test` is using 8 | module.exports = standard.jestConfig(); 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geeksrus-mqtt-panel", 3 | "version": "1.0.7-1", 4 | "description": "A panel to enable grafana to fulfil simple control tasks via MQTT", 5 | "scripts": { 6 | "build": "grafana-toolkit plugin:build", 7 | "test": "grafana-toolkit plugin:test", 8 | "dev": "grafana-toolkit plugin:dev", 9 | "watch": "grafana-toolkit plugin:dev --watch" 10 | }, 11 | "author": "geeks-r-us", 12 | "license": "Apache-2.0", 13 | "devDependencies": { 14 | "@grafana/data": "7.2.0", 15 | "@grafana/runtime": "7.2.0", 16 | "@grafana/toolkit": "^7.5.6", 17 | "@grafana/ui": "7.2.0", 18 | "@types/grafana": "github:CorpGlory/types-grafana.git", 19 | "@types/jquery": "^3.3.34", 20 | "@types/paho-mqtt": "^1.0.4" 21 | }, 22 | "dependencies": { 23 | "angular": "^1.8.0", 24 | "jquery": "^3.5.1", 25 | "jsonata": "^1.8.4", 26 | "mqtt": "^4.2.1" 27 | }, 28 | "resolutions": { 29 | "@babel/preset-env": "7.9.0" 30 | }, 31 | "engines": { 32 | "node": ">=14" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | ## MQTT Panel 2 | 3 | ![controls](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/logo.svg?sanitize=true "controls") 4 | [![CircleCI](https://circleci.com/gh/geeks-r-us/mqtt-panel.svg?style=svg)](https://circleci.com/gh/geeks-r-us/mqtt-panel) 5 | 6 | A panel to enable grafana to fulfil simple control tasks via MQTT. 7 | 8 | Currently it contains 4 types of control: 9 | 10 | ![controls](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/controls.png "controls") 11 | 12 | Each type can connect to a mqtt server and subscribe and publish to a topic. 13 | 14 | ![server](https://github.com/geeks-r-us/mqtt-panel/raw/master/src/img/server.png "server") 15 | 16 | The panel is able to pick values from an json encoded topic by the use of [JSONata query language](https://docs.jsonata.org/overview.html) 17 | 18 | ### Limitations 19 | 20 | Due to the MQTT client runs in the browser it can only establish mqtt connection over websockets and can only connect 21 | to servers reachable by your browser. 22 | 23 | ### Testing 24 | 25 | If you like to test this plugin the easies way is to load it into a grafana docker container. 26 | 27 | ``` 28 | docker run -d -p 3000:3000 -e "GF_INSTALL_PLUGINS=https://github.com/geeks-r-us/mqtt-panel/releases/download/v1.0.7/geeksrus-mqtt-panel-1.0.7.zip;mqtt-panel" --name=grafana grafana/grafana 29 | ``` 30 | Open http://localhost:3000 in your browser 31 | 32 | This is my first attempt to write a grafana panel so every issue report, comment or PR are welcome. 33 | 34 | ### Support 35 | 36 | If you find this module useful please support me with a cup of [coffee](https://ko-fi.com/geeks_r_us) 37 | -------------------------------------------------------------------------------- /src/img/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeks-r-us/mqtt-panel/b78867d659bb216e9ef09755edf83dfe6bebae28/src/img/button.png -------------------------------------------------------------------------------- /src/img/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeks-r-us/mqtt-panel/b78867d659bb216e9ef09755edf83dfe6bebae28/src/img/controls.png -------------------------------------------------------------------------------- /src/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 37 | 39 | 42 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | 80 | 84 | 85 | 89 | MQTT 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/img/server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeks-r-us/mqtt-panel/b78867d659bb216e9ef09755edf83dfe6bebae28/src/img/server.png -------------------------------------------------------------------------------- /src/module.test.ts: -------------------------------------------------------------------------------- 1 | describe('placeholder test', () => { 2 | it('should return true', () => { 3 | expect(true).toBeTruthy(); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | import { MetricsPanelCtrl } from 'grafana/app/plugins/sdk'; 2 | import defaultsDeep from 'lodash/defaultsDeep'; 3 | import { DataFrame } from '@grafana/data'; 4 | import $ from 'jquery'; 5 | import mqtt, { MqttClient, IClientOptions } from 'mqtt'; 6 | import jsonata from 'jsonata'; 7 | import './style.css'; 8 | import angluar from 'angular'; 9 | 10 | interface KeyValue { 11 | key: string; 12 | value: any; 13 | } 14 | 15 | export default class MqttCtrl extends MetricsPanelCtrl { 16 | static templateUrl = 'partials/module.html'; 17 | 18 | panelDefaults = { 19 | // Value 20 | value: 10, 21 | //Server 22 | mqttProtocol: (window.location.protocol === 'https:') ? 'wss' : 'ws', 23 | mqttServer: window.location.hostname, 24 | mqttServerPort: 9001, 25 | mqttTopicSubscribe: 'grafana/mqtt-panel', 26 | mqttTopicPublish: 'grafana/mqtt-panel/set', 27 | mqttAuth: 'None', 28 | mqttUser: '', 29 | mqttPassword: '', 30 | mqttTopicQuery: '', 31 | // GUI 32 | mode: 'Text', 33 | receiveOnly: false, 34 | 35 | model: { 36 | // Text 37 | text: 'Send', 38 | // Slider 39 | minValue: 0, 40 | maxValue: 100, 41 | step: 1, 42 | // Switch 43 | offValue: 'false', 44 | onValue: 'true', 45 | }, 46 | viewModel: { 47 | // Text 48 | text: 'Send', 49 | // Slider 50 | minValue: 0, 51 | maxValue: 100, 52 | step: 1, 53 | }, 54 | }; 55 | 56 | firstReceived: boolean = false; 57 | client: mqtt.MqttClient; 58 | input: any = null; 59 | value: any = null; 60 | 61 | // Simple example showing the last value of all data 62 | firstValues: KeyValue[] = []; 63 | 64 | /** @ngInject */ 65 | constructor($scope, $injector, public templateSrv) { 66 | super($scope, $injector); 67 | defaultsDeep(this.panel, this.panelDefaults); 68 | 69 | // Get results directly as DataFrames 70 | (this as any).dataFormat = 'series'; 71 | 72 | this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); 73 | this.events.on('render', this.onRender.bind(this)); 74 | this.events.on('data-error', this.onDataError.bind(this)); 75 | this.events.on('refresh', this.onRefresh.bind(this)); 76 | 77 | // Create a client instance: Broker, Port, Websocket Path, Client ID 78 | this.client = this.mqttConnect(); 79 | 80 | angluar.module('grafana.directives').directive('stringToNumber', this.stringToNumber); 81 | } 82 | 83 | onInitEditMode() { 84 | this.addEditorTab('Display', `public/plugins/${this.pluginId}/partials/options.display.html`, 2); 85 | this.addEditorTab('Server', `public/plugins/${this.pluginId}/partials/options.server.html`, 3); 86 | } 87 | 88 | onRefresh() { 89 | this.panel.viewModel.text = this.templateSrv.replace(String(this.panel.model.text)); 90 | this.panel.viewModel.minValue = this.templateSrv.replace(String(this.panel.model.minValue)); 91 | this.panel.viewModel.maxValue = this.templateSrv.replace(String(this.panel.model.maxValue)); 92 | this.panel.viewModel.step = this.templateSrv.replace(String(this.panel.model.step)); 93 | } 94 | 95 | onRender() { 96 | if (!this.firstValues || !this.firstValues.length) { 97 | return; 98 | } 99 | 100 | // Tells the screen capture system that you finished 101 | this.renderingCompleted(); 102 | } 103 | 104 | onDataError(err: any) { 105 | console.log('onDataError', err); 106 | } 107 | 108 | // 6.3+ get typed DataFrame directly 109 | handleDataFrame(data: DataFrame[]) { 110 | const values: KeyValue[] = []; 111 | 112 | for (const frame of data) { 113 | for (let i = 0; i < frame.fields.length; i++) { 114 | values.push({ 115 | key: frame.fields[i].name, 116 | value: frame.fields[i].values, 117 | }); 118 | } 119 | } 120 | 121 | this.firstValues = values; 122 | } 123 | 124 | mqttConnect(): MqttClient { 125 | var url = 126 | this.panel.mqttProtocol + 127 | '://' + 128 | this.templateSrv.replace(String(this.panel.mqttServer)) + 129 | ':' + 130 | this.templateSrv.replace(String(this.panel.mqttServerPort)); 131 | console.log(url); 132 | 133 | var options: IClientOptions = {}; 134 | if (this.panel.mqttAuth === 'BasicAuth') { 135 | options.username = this.templateSrv.replace(String(this.panel.mqttUser)); 136 | options.password = this.templateSrv.replace(String(this.panel.mqttPassword)); 137 | console.log('connection with: ' + options.username + ' : ' + options.password); 138 | } 139 | 140 | let client = mqtt.connect(url, options); 141 | client.on('connectionLost', this.onConnectionLost.bind(this)); 142 | client.on('connect', this.onConnect.bind(this)); 143 | client.on('message', this.onMessage.bind(this)); 144 | client.subscribe(this.templateSrv.replace(String(this.panel.mqttTopicSubscribe))); 145 | console.log('subscribing: ' + this.templateSrv.replace(String(this.panel.mqttTopicSubscribe))); 146 | 147 | return client; 148 | } 149 | 150 | getProtocols() { 151 | return [ 152 | { text: 'ws', value: 'ws' }, 153 | { text: 'wss', value: 'wss' }, 154 | ]; 155 | } 156 | 157 | getModes() { 158 | return [ 159 | { text: 'Text', value: 'Text' }, 160 | { text: 'Slider', value: 'Slider' }, 161 | { text: 'Switch', value: 'Switch' }, 162 | { text: 'Button', value: 'Button' }, 163 | ]; 164 | } 165 | 166 | getMqttAuths() { 167 | return [ 168 | { text: 'None', value: 'None' }, 169 | { text: 'Basic Auth', value: 'BasicAuth' }, 170 | ]; 171 | } 172 | 173 | link(scope: any, elem: any, attrs: any, ctrl: any) { 174 | this.input = $(elem.find('#value')[0]); 175 | console.log('link'); 176 | } 177 | 178 | stringToNumber() { 179 | return { 180 | require: 'ngModel', 181 | link: function(scope, element, attrs, ngModel) { 182 | ngModel.$parsers.push(function(value) { 183 | return '' + value; 184 | }); 185 | ngModel.$formatters.push(function(value) { 186 | return parseFloat(value); 187 | }); 188 | }, 189 | }; 190 | } 191 | 192 | // MQTT 193 | onConnect() { 194 | console.log('connected'); 195 | this.panel.refresh(); 196 | } 197 | 198 | onConnectionLost() { 199 | console.log('onConnectionLost'); 200 | this.panel.refresh(); 201 | } 202 | 203 | onMessage(topic, message) { 204 | this.firstReceived = true; 205 | let value; 206 | 207 | // execute query if set 208 | if (this.panel.mqttTopicQuery.length > 0) { 209 | try { 210 | let jsonmesssage = JSON.parse(message); 211 | let expression = jsonata(this.panel.mqttTopicQuery); 212 | let result = expression.evaluate(jsonmesssage); 213 | 214 | console.log( 215 | 'JSONata query: ' + this.panel.mqttTopicQuery + ' on message: ' + message + ' results in: ' + result 216 | ); 217 | if (result) { 218 | message = result; 219 | } else { 220 | message = ''; 221 | } 222 | } catch (e) { 223 | console.log(e.message); 224 | } 225 | } 226 | 227 | switch (this.panel.mode) { 228 | case 'Switch': 229 | value = this.templateSrv.replace(String(this.panel.model.onValue)) === message.toString(); 230 | break; 231 | case 'Text': 232 | case 'Slider': 233 | value = message; 234 | break; 235 | case 'Button': 236 | //value = message; 237 | break; 238 | } 239 | 240 | console.log('Received : ' + topic + ' : ' + value.toString()); 241 | this.panel.value = value; 242 | this.panel.refresh(); 243 | } 244 | 245 | connect() { 246 | this.client.end(true); 247 | this.client = this.mqttConnect(); 248 | this.firstReceived = false; 249 | } 250 | 251 | publish() { 252 | let value; 253 | switch (this.panel.mode) { 254 | case 'Switch': 255 | value = this.panel.value 256 | ? this.templateSrv.replace(String(this.panel.model.onValue)) 257 | : this.templateSrv.replace(String(this.panel.model.offValue)); 258 | break; 259 | case 'Button': 260 | case 'Text': 261 | case 'Slider': 262 | value = this.templateSrv.replace(this.panel.value.toString(), this.templateSrv.getVariables()); 263 | break; 264 | } 265 | if(this.firstReceived) 266 | { 267 | console.log('Published : ' + this.templateSrv.replace(String(this.panel.mqttTopicPublish)) + ' : ' + value); 268 | this.client.publish(this.templateSrv.replace(String(this.panel.mqttTopicPublish)), value); 269 | } else { 270 | console.log('NOT Published : ' + this.templateSrv.replace(String(this.panel.mqttTopicPublish)) + ' : ' + value); 271 | console.log("CAUSE: Not received a message first."); 272 | this.firstReceived = true; 273 | } 274 | } 275 | 276 | connected() { 277 | return this.client != null && this.client.connected ? 'successful' : 'failed'; 278 | } 279 | } 280 | 281 | export { MqttCtrl as PanelCtrl }; 282 | -------------------------------------------------------------------------------- /src/partials/module.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 |
9 |
10 | 13 |
14 |
15 |
16 |
17 | 27 |
28 |
29 | {{ctrl.panel.viewModel.text}} : {{ctrl.panel.value}} 30 |
31 |
32 |
33 | 39 | 40 |
41 |
42 |
43 | 46 |
47 |
-------------------------------------------------------------------------------- /src/partials/options.display.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Display
4 |
5 | 6 | 13 | 14 |
15 |
16 | 17 | 24 |
25 |
26 | 27 | 34 |
35 |
36 | 37 | 44 |
45 |
46 | 47 | 54 |
55 |
56 |
57 | 58 | 65 |
66 |
67 | 68 | 75 |
76 |
77 | 78 | 85 |
86 |
87 | -------------------------------------------------------------------------------- /src/partials/options.server.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Connection
4 |
5 | 6 | 14 | 15 |
16 |
17 | 18 | 25 |
26 |
27 | 28 | 35 |
36 |
37 | 38 | 45 |
46 |
47 | 48 | 54 |
55 |
56 | 57 | 64 |
65 |
66 | 67 |
68 |
69 |
70 |
71 |
72 |
73 |
Authentication
74 |
75 | 76 | 83 | 84 |
85 |
86 | 87 | 93 |
94 |
95 | 96 | 102 |
103 |
104 |
105 | 108 |
109 | 110 |
111 | -------------------------------------------------------------------------------- /src/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "panel", 3 | "name": "MQTT panel", 4 | "id": "geeksrus-mqtt-panel", 5 | "info": { 6 | "description": "A panel to enable grafana to fulfil simple control tasks via MQTT", 7 | "author": { 8 | "name": "geeks-r-us", 9 | "url": "https://github.com/geeks-r-us/mqtt-panel" 10 | }, 11 | "keywords": [ 12 | "MQTT" 13 | ], 14 | "logos": { 15 | "small": "img/logo.svg", 16 | "large": "img/logo.svg" 17 | }, 18 | "links": [], 19 | "screenshots": [ 20 | { 21 | "name": "Controls", 22 | "path": "img/controls.png" 23 | }, 24 | { 25 | "name": "Server settings", 26 | "path": "img/server.png" 27 | } 28 | ], 29 | "version": "%VERSION%", 30 | "updated": "%TODAY%" 31 | }, 32 | "dependencies": { 33 | "grafanaVersion": "7.0.x", 34 | "grafanaDependency": ">=7.0.0", 35 | "plugins": [] 36 | } 37 | } -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | .container-outer { 2 | display: flex; 3 | flex-direction: row; 4 | } 5 | 6 | .container-column { 7 | flex: 1 1 0px; 8 | } 9 | 10 | .connection-state { 11 | width: 10px; 12 | height: 10px; 13 | border-radius: 50%; 14 | margin: 0 0 0 10px; 15 | position: absolute; 16 | top: 50%; 17 | -ms-transform: translateY(-50%); 18 | transform: translateY(-50%); 19 | } 20 | 21 | .connection-state-outer { 22 | position: static; 23 | } 24 | 25 | .connection-state.successful { 26 | background: #0f0; 27 | } 28 | 29 | .connection-state.failed { 30 | background: #f00; 31 | } 32 | 33 | .slidecontainer { 34 | width: 100%; /* Width of the outside container */ 35 | } 36 | 37 | .slider { 38 | -webkit-appearance: none; 39 | width: 100%; 40 | height: 15px; 41 | border-radius: 5px; 42 | background: #d3d3d3; 43 | outline: none; 44 | opacity: 0.7; 45 | -webkit-transition: .2s; 46 | transition: opacity .2s; 47 | } 48 | 49 | .slider::-webkit-slider-thumb { 50 | -webkit-appearance: none; 51 | appearance: none; 52 | width: 25px; 53 | height: 25px; 54 | border-radius: 50%; 55 | background: #EB7B18; 56 | cursor: pointer; 57 | } 58 | 59 | .slider::-moz-range-thumb { 60 | width: 25px; 61 | height: 25px; 62 | border-radius: 50%; 63 | background: #EB7B18 64 | ; 65 | cursor: pointer; 66 | } 67 | 68 | .slider-text { 69 | text-align: center; 70 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@grafana/toolkit/src/config/tsconfig.plugin.json", 3 | "include": ["src", "types"], 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "baseUrl": "./src", 7 | "typeRoots": ["./node_modules/@types"], 8 | "noImplicitAny": false, 9 | "noImplicitThis": false 10 | } 11 | } 12 | --------------------------------------------------------------------------------