├── .codeclimate.yml ├── .eslintignore ├── .eslintrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── auto-merge.yml ├── dependabot.yml ├── stale.yml └── workflows │ ├── dependabot-automerge.yml │ └── test-and-release.yml ├── .gitignore ├── .idea ├── encodings.xml ├── ioBroker.smartmeter.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── .mocharc.json ├── .npmignore ├── .releaseconfig.json ├── LICENSE ├── README.md ├── admin ├── i18n │ ├── de │ │ └── translations.json │ ├── en │ │ └── translations.json │ ├── es │ │ └── translations.json │ ├── fr │ │ └── translations.json │ ├── it │ │ └── translations.json │ ├── nl │ │ └── translations.json │ ├── pl │ │ └── translations.json │ ├── pt │ │ └── translations.json │ ├── ru │ │ └── translations.json │ └── zh-cn │ │ └── translations.json ├── index.html ├── questionmark.png ├── smartmeter.jpg ├── smartmeter.png ├── tooltip.css └── words.js ├── io-package.json ├── lib └── tools.js ├── package-lock.json ├── package.json ├── smartmeter.js └── test ├── lib └── setup.js ├── mocha.setup.js ├── testAdapter.js └── testPackageFiles.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | checks: 6 | Similar code: 7 | enabled: false 8 | Identical code: 9 | enabled: false 10 | config: 11 | languages: 12 | - javascript 13 | eslint: 14 | enabled: true 15 | checks: 16 | complexity: 17 | enabled: false 18 | exclude_fingerprints: 19 | - b48b8d51245ef95a52fc9bfe0132301d 20 | fixme: 21 | enabled: true 22 | ratings: 23 | paths: 24 | - "**.js" 25 | exclude_paths: 26 | - test/ 27 | - lib/utils.js 28 | - Gruntfile.js 29 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | ecmaFeatures: 2 | modules: true 3 | jsx: true 4 | 5 | env: 6 | amd: true 7 | browser: true 8 | es6: true 9 | jquery: true 10 | node: true 11 | 12 | # http://eslint.org/docs/rules/ 13 | rules: 14 | # Possible Errors 15 | comma-dangle: [2, never] 16 | no-cond-assign: 2 17 | no-console: 0 18 | no-constant-condition: 2 19 | no-control-regex: 2 20 | no-debugger: 2 21 | no-dupe-args: 2 22 | no-dupe-keys: 2 23 | no-duplicate-case: 2 24 | no-empty: 2 25 | no-empty-character-class: 2 26 | no-ex-assign: 2 27 | no-extra-boolean-cast: 2 28 | no-extra-parens: 0 29 | no-extra-semi: 2 30 | no-func-assign: 2 31 | no-inner-declarations: [2, functions] 32 | no-invalid-regexp: 2 33 | no-irregular-whitespace: 2 34 | no-negated-in-lhs: 2 35 | no-obj-calls: 2 36 | no-regex-spaces: 2 37 | no-sparse-arrays: 2 38 | no-unexpected-multiline: 2 39 | no-unreachable: 2 40 | use-isnan: 2 41 | valid-jsdoc: 0 42 | valid-typeof: 2 43 | 44 | # Best Practices 45 | accessor-pairs: 2 46 | block-scoped-var: 0 47 | complexity: [2, 6] 48 | consistent-return: 0 49 | curly: 0 50 | default-case: 0 51 | dot-location: 0 52 | dot-notation: 0 53 | eqeqeq: 2 54 | guard-for-in: 2 55 | no-alert: 2 56 | no-caller: 2 57 | no-case-declarations: 2 58 | no-div-regex: 2 59 | no-else-return: 0 60 | no-empty-label: 2 61 | no-empty-pattern: 2 62 | no-eq-null: 2 63 | no-eval: 2 64 | no-extend-native: 2 65 | no-extra-bind: 2 66 | no-fallthrough: 2 67 | no-floating-decimal: 0 68 | no-implicit-coercion: 0 69 | no-implied-eval: 2 70 | no-invalid-this: 0 71 | no-iterator: 2 72 | no-labels: 0 73 | no-lone-blocks: 2 74 | no-loop-func: 2 75 | no-magic-number: 0 76 | no-multi-spaces: 0 77 | no-multi-str: 0 78 | no-native-reassign: 2 79 | no-new-func: 2 80 | no-new-wrappers: 2 81 | no-new: 2 82 | no-octal-escape: 2 83 | no-octal: 2 84 | no-proto: 2 85 | no-redeclare: 2 86 | no-return-assign: 2 87 | no-script-url: 2 88 | no-self-compare: 2 89 | no-sequences: 0 90 | no-throw-literal: 0 91 | no-unused-expressions: 2 92 | no-useless-call: 2 93 | no-useless-concat: 2 94 | no-void: 2 95 | no-warning-comments: 0 96 | no-with: 2 97 | radix: 2 98 | vars-on-top: 0 99 | wrap-iife: 2 100 | yoda: 0 101 | 102 | # Strict 103 | strict: 0 104 | 105 | # Variables 106 | init-declarations: 0 107 | no-catch-shadow: 2 108 | no-delete-var: 2 109 | no-label-var: 2 110 | no-shadow-restricted-names: 2 111 | no-shadow: 0 112 | no-undef-init: 2 113 | no-undef: 0 114 | no-undefined: 0 115 | no-unused-vars: 0 116 | no-use-before-define: 0 117 | 118 | # Node.js and CommonJS 119 | callback-return: 2 120 | global-require: 2 121 | handle-callback-err: 2 122 | no-mixed-requires: 0 123 | no-new-require: 0 124 | no-path-concat: 2 125 | no-process-exit: 2 126 | no-restricted-modules: 0 127 | no-sync: 0 128 | 129 | # Stylistic Issues 130 | array-bracket-spacing: 0 131 | block-spacing: 0 132 | brace-style: 0 133 | camelcase: 0 134 | comma-spacing: 0 135 | comma-style: 0 136 | computed-property-spacing: 0 137 | consistent-this: 0 138 | eol-last: 0 139 | func-names: 0 140 | func-style: 0 141 | id-length: 0 142 | id-match: 0 143 | indent: 0 144 | jsx-quotes: 0 145 | key-spacing: 0 146 | linebreak-style: 0 147 | lines-around-comment: 0 148 | max-depth: 0 149 | max-len: 0 150 | max-nested-callbacks: 0 151 | max-params: 0 152 | max-statements: [2, 30] 153 | new-cap: 0 154 | new-parens: 0 155 | newline-after-var: 0 156 | no-array-constructor: 0 157 | no-bitwise: 0 158 | no-continue: 0 159 | no-inline-comments: 0 160 | no-lonely-if: 0 161 | no-mixed-spaces-and-tabs: 0 162 | no-multiple-empty-lines: 0 163 | no-negated-condition: 0 164 | no-nested-ternary: 0 165 | no-new-object: 0 166 | no-plusplus: 0 167 | no-restricted-syntax: 0 168 | no-spaced-func: 0 169 | no-ternary: 0 170 | no-trailing-spaces: 0 171 | no-underscore-dangle: 0 172 | no-unneeded-ternary: 0 173 | object-curly-spacing: 0 174 | one-var: 0 175 | operator-assignment: 0 176 | operator-linebreak: 0 177 | padded-blocks: 0 178 | quote-props: 0 179 | quotes: 0 180 | require-jsdoc: 0 181 | semi-spacing: 0 182 | semi: 0 183 | sort-vars: 0 184 | space-after-keywords: 0 185 | space-before-blocks: 0 186 | space-before-function-paren: 0 187 | space-before-keywords: 0 188 | space-in-parens: 0 189 | space-infix-ops: 0 190 | space-return-throw-case: 0 191 | space-unary-ops: 0 192 | spaced-comment: 0 193 | wrap-regex: 0 194 | 195 | # ECMAScript 6 196 | arrow-body-style: 0 197 | arrow-parens: 0 198 | arrow-spacing: 0 199 | constructor-super: 0 200 | generator-star-spacing: 0 201 | no-arrow-condition: 0 202 | no-class-assign: 0 203 | no-const-assign: 0 204 | no-dupe-class-members: 0 205 | no-this-before-super: 0 206 | no-var: 0 207 | object-shorthand: 0 208 | prefer-arrow-callback: 0 209 | prefer-const: 0 210 | prefer-reflect: 0 211 | prefer-spread: 0 212 | prefer-template: 0 213 | require-yield: 0 214 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: Apollon77 4 | patreon: Apollon77 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Something is not working as it should 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '...' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots & Logfiles** 23 | If applicable, add screenshots and logfiles to help explain your problem. 24 | 25 | **Versions:** 26 | - Adapter version: 27 | - JS-Controller version: 28 | - Node version: 29 | - Operating system: 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/auto-merge.yml: -------------------------------------------------------------------------------- 1 | # Configure here which dependency updates should be merged automatically. 2 | # The recommended configuration is the following: 3 | - match: 4 | # Only merge patches for production dependencies 5 | dependency_type: production 6 | update_type: "semver:patch" 7 | - match: 8 | # Except for security fixes, here we allow minor patches 9 | dependency_type: production 10 | update_type: "security:minor" 11 | - match: 12 | # and development dependencies can have a minor update, too 13 | dependency_type: development 14 | update_type: "semver:minor" 15 | 16 | # The syntax is based on the legacy dependabot v1 automerged_updates syntax, see: 17 | # https://dependabot.com/docs/config-file/#automerged_updates -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | time: "04:00" 8 | timezone: Europe/Berlin 9 | - package-ecosystem: npm 10 | directory: "/" 11 | schedule: 12 | interval: monthly 13 | time: "04:00" 14 | timezone: Europe/Berlin 15 | open-pull-requests-limit: 20 16 | versioning-strategy: increase -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 90 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 7 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - enhancement 16 | - security 17 | 18 | # Set to true to ignore issues in a project (defaults to false) 19 | exemptProjects: true 20 | 21 | # Set to true to ignore issues in a milestone (defaults to false) 22 | exemptMilestones: true 23 | 24 | # Set to true to ignore issues with an assignee (defaults to false) 25 | exemptAssignees: false 26 | 27 | # Label to use when marking as stale 28 | staleLabel: wontfix 29 | 30 | # Comment to post when marking as stale. Set to `false` to disable 31 | markComment: > 32 | This issue has been automatically marked as stale because it has not had 33 | recent activity. It will be closed if no further activity occurs within the next 7 days. 34 | Please check if the issue is still relevant in the most current version of the adapter 35 | and tell us. Also check that all relevant details, logs and reproduction steps 36 | are included and update them if needed. 37 | Thank you for your contributions. 38 | 39 | Dieses Problem wurde automatisch als veraltet markiert, da es in letzter Zeit keine Aktivitäten gab. 40 | Es wird geschlossen, wenn nicht innerhalb der nächsten 7 Tage weitere Aktivitäten stattfinden. 41 | Bitte überprüft, ob das Problem auch in der aktuellsten Version des Adapters noch relevant ist, 42 | und teilt uns dies mit. Überprüft auch, ob alle relevanten Details, Logs und Reproduktionsschritte 43 | enthalten sind bzw. aktualisiert diese. 44 | Vielen Dank für Eure Unterstützung. 45 | 46 | # Comment to post when removing the stale label. 47 | # unmarkComment: > 48 | # Your comment here. 49 | 50 | # Comment to post when closing a stale Issue or Pull Request. 51 | closeComment: > 52 | This issue has been automatically closed because of inactivity. Please open a new 53 | issue if still relevant and make sure to include all relevant details, logs and 54 | reproduction steps. 55 | Thank you for your contributions. 56 | 57 | Dieses Problem wurde aufgrund von Inaktivität automatisch geschlossen. Bitte öffnet ein 58 | neues Issue, falls dies noch relevant ist und stellt sicher das alle relevanten Details, 59 | Logs und Reproduktionsschritte enthalten sind. 60 | Vielen Dank für Eure Unterstützung. 61 | 62 | # Limit the number of actions per hour, from 1-30. Default is 30 63 | limitPerRun: 30 64 | 65 | # Limit to only `issues` or `pulls` 66 | only: issues 67 | 68 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 69 | # pulls: 70 | # daysUntilStale: 30 71 | # markComment: > 72 | # This pull request has been automatically marked as stale because it has not had 73 | # recent activity. It will be closed if no further activity occurs. Thank you 74 | # for your contributions. 75 | 76 | # issues: 77 | # exemptLabels: 78 | # - confirmed -------------------------------------------------------------------------------- /.github/workflows/dependabot-automerge.yml: -------------------------------------------------------------------------------- 1 | # Automatically merge Dependabot PRs when version comparison is within the range 2 | # that is configured in .github/auto-merge.yml 3 | 4 | name: Auto-Merge Dependabot PRs 5 | 6 | on: 7 | pull_request_target: 8 | 9 | jobs: 10 | auto-merge: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v3 15 | 16 | - name: Check if PR should be auto-merged 17 | uses: ahmadnassri/action-dependabot-auto-merge@v2 18 | with: 19 | # This must be a personal access token with push access 20 | github-token: ${{ secrets.AUTO_MERGE_TOKEN }} 21 | # By default, squash and merge, so Github chooses nice commit messages 22 | command: squash and merge -------------------------------------------------------------------------------- /.github/workflows/test-and-release.yml: -------------------------------------------------------------------------------- 1 | # This is a composition of lint and test scripts 2 | # Make sure to update this file along with the others 3 | 4 | name: Test and Release 5 | 6 | # Run this job on all pushes and pull requests 7 | # as well as tags with a semantic version 8 | on: 9 | push: 10 | branches: 11 | - '*' 12 | tags: 13 | # normal versions 14 | - "v?[0-9]+.[0-9]+.[0-9]+" 15 | # pre-releases 16 | - "v?[0-9]+.[0-9]+.[0-9]+-**" 17 | pull_request: {} 18 | 19 | jobs: 20 | # Performs quick checks before the expensive test runs 21 | check-and-lint: 22 | if: contains(github.event.head_commit.message, '[skip ci]') == false 23 | 24 | runs-on: ubuntu-latest 25 | 26 | strategy: 27 | matrix: 28 | node-version: [18.x] 29 | 30 | steps: 31 | - uses: actions/checkout@v3 32 | - name: Use Node.js ${{ matrix.node-version }} 33 | uses: actions/setup-node@v3 34 | with: 35 | node-version: ${{ matrix.node-version }} 36 | 37 | 38 | - name: Install Dependencies 39 | run: npm ci 40 | 41 | # - name: Perform a type check 42 | # run: npm run check:ts 43 | # env: 44 | # CI: true 45 | # - name: Lint TypeScript code 46 | # run: npm run lint 47 | # - name: Test package files 48 | # run: npm run test:package 49 | 50 | # Runs adapter tests on all supported node versions and OSes 51 | adapter-tests: 52 | if: contains(github.event.head_commit.message, '[skip ci]') == false 53 | 54 | needs: [check-and-lint] 55 | 56 | runs-on: ${{ matrix.os }} 57 | strategy: 58 | matrix: 59 | node-version: [16.x, 18.x, 20.x] 60 | os: [ubuntu-latest, windows-latest, macos-latest] 61 | 62 | steps: 63 | - uses: actions/checkout@v3 64 | - name: Use Node.js ${{ matrix.node-version }} 65 | uses: actions/setup-node@v3 66 | with: 67 | node-version: ${{ matrix.node-version }} 68 | 69 | - name: Install Dependencies 70 | run: npm ci 71 | 72 | - name: Run local tests 73 | run: npm test 74 | # - name: Run unit tests 75 | # run: npm run test:unit 76 | # - name: Run integration tests # (linux/osx) 77 | # if: startsWith(runner.OS, 'windows') == false 78 | # run: DEBUG=testing:* npm run test:integration 79 | # - name: Run integration tests # (windows) 80 | # if: startsWith(runner.OS, 'windows') 81 | # run: set DEBUG=testing:* & npm run test:integration 82 | 83 | # Deploys the final package to NPM 84 | deploy: 85 | needs: [adapter-tests] 86 | 87 | # Trigger this step only when a commit on master is tagged with a version number 88 | if: | 89 | contains(github.event.head_commit.message, '[skip ci]') == false && 90 | github.event_name == 'push' && 91 | startsWith(github.ref, 'refs/tags/') 92 | runs-on: ubuntu-latest 93 | strategy: 94 | matrix: 95 | node-version: [18.x] 96 | 97 | steps: 98 | - name: Checkout code 99 | uses: actions/checkout@v3 100 | 101 | - name: Use Node.js ${{ matrix.node-version }} 102 | uses: actions/setup-node@v3 103 | with: 104 | node-version: ${{ matrix.node-version }} 105 | 106 | - name: Extract the version and commit body from the tag 107 | id: extract_release 108 | # The body may be multiline, therefore we need to escape some characters 109 | run: | 110 | VERSION="${{ github.ref }}" 111 | VERSION=${VERSION##*/} 112 | VERSION=${VERSION##*v} 113 | echo "::set-output name=VERSION::$VERSION" 114 | BODY=$(git show -s --format=%b) 115 | BODY="${BODY//'%'/'%25'}" 116 | BODY="${BODY//$'\n'/'%0A'}" 117 | BODY="${BODY//$'\r'/'%0D'}" 118 | echo "::set-output name=BODY::$BODY" 119 | 120 | - name: Install Dependencies 121 | run: npm ci 122 | 123 | # - name: Create a clean build 124 | # run: npm run build 125 | - name: Publish package to npm 126 | run: | 127 | npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }} 128 | npm whoami 129 | npm publish 130 | 131 | - name: Create Github Release 132 | uses: actions/create-release@v1 133 | env: 134 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 135 | with: 136 | tag_name: ${{ github.ref }} 137 | release_name: Release v${{ steps.extract_release.outputs.VERSION }} 138 | draft: false 139 | # Prerelease versions create prereleases on Github 140 | prerelease: ${{ contains(steps.extract_release.outputs.VERSION, '-') }} 141 | body: ${{ steps.extract_release.outputs.BODY }} 142 | 143 | - name: Notify Sentry.io about the release 144 | run: | 145 | npm i -g @sentry/cli 146 | export SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} 147 | export SENTRY_URL=https://sentry.iobroker.net 148 | export SENTRY_ORG=iobroker 149 | export SENTRY_PROJECT=iobroker-smartmeter 150 | export SENTRY_VERSION=iobroker.smartmeter@${{ steps.extract_release.outputs.VERSION }} 151 | sentry-cli releases new $SENTRY_VERSION 152 | sentry-cli releases set-commits $SENTRY_VERSION --auto 153 | sentry-cli releases finalize $SENTRY_VERSION 154 | 155 | # Add the following line BEFORE finalize if sourcemap uploads are needed 156 | # sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/ 157 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .vscode/ftp-sync.json 3 | .vscode/launch.json 4 | .remote-sync.json 5 | node_modules 6 | .idea -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/ioBroker.smartmeter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": [ 3 | "./test/mocha.setup.js" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | Gruntfile.js 2 | tasks 3 | node_modules 4 | .idea 5 | .gitignore 6 | .git 7 | .DS_Store 8 | test 9 | .travis.yml 10 | appveyor.yml 11 | .codeclimate.yml 12 | .eslinkignore 13 | .eslintrc 14 | .vscode 15 | package-lock.json 16 | .github 17 | .gitignore 18 | .vscode/ftp-sync.json 19 | .vscode/launch.json 20 | .remote-sync.json 21 | node_modules 22 | admin/i18n -------------------------------------------------------------------------------- /.releaseconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["iobroker", "license"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2023 Ingo Fischer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](admin/smartmeter.png) 2 | # ioBroker.smartmeter 3 | 4 | ![Number of Installations](http://iobroker.live/badges/smartmeter-installed.svg) 5 | ![Number of Installations](http://iobroker.live/badges/smartmeter-stable.svg) 6 | [![NPM version](http://img.shields.io/npm/v/iobroker.smartmeter.svg)](https://www.npmjs.com/package/iobroker.smartmeter) 7 | 8 | ![Test and Release](https://github.com/Apollon77/iobroker.smartmeter/workflows/Test%20and%20Release/badge.svg) 9 | [![Translation status](https://weblate.iobroker.net/widgets/adapters/-/smartmeter/svg-badge.svg)](https://weblate.iobroker.net/engage/adapters/?utm_source=widget) 10 | [![Downloads](https://img.shields.io/npm/dm/iobroker.smartmeter.svg)](https://www.npmjs.com/package/iobroker.smartmeter) 11 | 12 | **This adapter uses Sentry libraries to automatically report exceptions and code errors to me as the developer.** More details see below! 13 | 14 | This adapter for ioBroker allows the reading and parsing of smartmeter protocols that follow the OBIS number logic to make their data available. 15 | 16 | ***The Adapter needs nodejs 16.x+ to work!*** 17 | 18 | ***This Adapter needs to have git installed currently for installing!*** 19 | 20 | ## Description of parameters 21 | 22 | ioBroker-Forum-Thread: http://forum.iobroker.net/viewtopic.php?f=23&t=5047&p=54973 23 | 24 | ### Data Protocol 25 | Supported Protocols: 26 | * **Sml**: SML (Smart Message Language) as binary format 27 | * **D0**: D0 (based on IEC 62056-21:2002/IEC 61107/EN 61107) as ASCII format (binary protocol mode E not supported currently) 28 | * **Json-Efr**: OBIS data from EFR Smart Grid Hub (JSON format) 29 | 30 | ### Data Transfer 31 | * **Serial Receiving**: receive through serial push data (smartmeter send data without any request on regular intervals). Mostly used for SML 32 | * **Serial Bi-Directional Communication**: D0 protocol in modes A, B, C and D (mode E curently NOT supported!) with Wakeup-, Signon-, pot. ACK- and Data-messages to read out data (programing/write mode not implemented so far) 33 | * **Http-Requests**: Read data via HTTP by requesting an defined URL 34 | * **Local Files**: Read data from a local file 35 | 36 | ### Data request interval 37 | Number of seconds to wait for next request or pause serial receiving, value 0 possible to restart directly after finishing one message, 38 | 39 | Default: is 300 (=5 Minutes) 40 | 41 | ### Serial Device Baudrate 42 | baudrate for initial serial connection, if not defined default values per Transport type are used (9600 for SerialResponseTransprt and 300 for SerialRequestResponseTransport) 43 | 44 | ### D0: SignOn-Message Command 45 | Command for SignIn-Message, default "?" to query mandatory fields, other values depending on device. 46 | Example: The 2WR5 Heatmeter uses "#" to query a lot more data (optional fields together with all mandatory) 47 | 48 | ### D0: Mode-Overwrite 49 | The Adapter tries to determine the D0 Protocol mode as defined in the specifications. There are some devices that do not comply to the specifications and so bring problems. Using this option you can overwrite the determined protocol mode. 50 | * Mode A: no baudrate changeover, no Ack-Message 51 | * Mode B: baudrate changeover, no Ack-Message 52 | * Mode C: baudrate changeover and Ack-Message needed 53 | * Mode D: No baudrate changeover, baudrate always 2400 54 | * Mode E: baudrate changeover and Ack-Message needed, Custom protocols, not Supported currectly!! Contact me if you have such an smartmeter 55 | 56 | ### D0: Baudrate-Changeover-Overwrite 57 | The adapter tries to determine the Baudrate for the data messages as defined in the protocol specifications. But as with the Mode some smartmeter provide wrong data here. SO you can use this to overwrite the baudrate for the data message as needed. Leave empty to use the baudrate changeover as defined by the smart meter. 58 | 59 | ## Adapter is tested with ... 60 | ... at least: 61 | * Hager eHz Energy Meter (multiple, e.g. eHZ-IW8E2A5L0EK2P, EHZ363W5, ) 62 | * EMH Energy Meter 63 | * EFR SmartGridHub 64 | * Siemens 2WR5 reader from an heat station 65 | * Elster AS1440 66 | * Iskraemeco MT174 67 | * Iskraemeco MT175 68 | * Itron EM214 Typ 720 69 | * Landis & Gyr E220 70 | * Dutch smart meter using DSRM protocol (use "Serial Device reading data only" and "D0" as protocol) 71 | * DZG DWS7412.1T 72 | * *IMPORTANT*: There seems to be a Firmware bug and sometimes the current energy consumptions becomes negative! Manual recalculation possible using formular from https://github.com/Apollon77/smartmeter-obis/issues/75#issuecomment-581650736* ... and many many more 73 | 74 | Please send me an info on devices where you have used the library successfully and I will add it here. 75 | 76 | ## Special Smartmeters and problems 77 | 78 | ### DZG DVS74 79 | It seems to be an error in the SML firmware sometimes and values are wrongly encoded in the SML message, but the message itself is valid. Solution is to post process the value using a Javascript. See https://github.com/Apollon77/smartmeter-obis/issues/75#issuecomment-581650736 80 | 81 | ## How to report issues and feature requests 82 | 83 | Please use GitHub issues for this. 84 | 85 | Best is to set the adapter to Debug log mode (Instances -> Expert mode -> Column Log level). Then please get the logfile from disk (subdirectory "log" in ioBroker installation directory and not from Admin because Admin cuts the lines). If you do not like providing it in GitHub issue you can also send it to me via email (iobroker@fischer-ka.de). Please add a reference to the relevant GitHub issue AND also describe what I see in the log at which time. 86 | 87 | ## What is Sentry and what is reported to the servers? 88 | Sentry.io is a way for developers to get an overview about errors from their applications. And exactly this is implemented in this adapter. 89 | 90 | When the adapter crashes or an other Code error happens, this error message that also appears in the ioBroker log is submitted to our own Sentry server hosted in germany. When you allowed iobroker GmbH to collect diagnostic data then also your installation ID (this is just a unique ID **without** any additional infos about you, email, name or such) is included. This allows Sentry to group errors and show how many unique users are affected by such an error. All of this helps me to provide error free adapters that basically never crashs. 91 | 92 | ## Changelog 93 | ### 3.4.0 (2023-11-25) 94 | * IMPORTANT: This version requires at least Node.js 16+ 95 | * (Apollon77) Prevent some crash cases 96 | 97 | ### 3.3.4 (2022-03-11) 98 | * (Apollon77) Restore functionality of Port list in Admin 99 | 100 | ### 3.3.3 (2022-02-21) 101 | * IMPORTANT: This version requires at least Node.js 12.17+ (excluding non LTS like 13.x) 102 | * (Apollon77) Prevent some crash cases 103 | 104 | ### 3.2.1 (2021-05-09) 105 | * (Apollon77) Optimize for js-controller 3.3 106 | 107 | ### 3.2.0 (2021-01-24) 108 | * (Apollon77) Add new protocolSmlInputEncoding option for SML protocol. With this also ascii or base64 based encodings (e.g. with TCP transports) are possible. 109 | 110 | ### 3.1.9 (2021-01-22) 111 | * (Apollon77) optimize stop handling (Sentry IOBROKER-SMARTMETER-10) 112 | 113 | ### 3.1.8 (2021-01-14) 114 | * (Apollon77) prevent last warnings with js-controller 3.2 115 | 116 | ### 3.1.7 (2021-01-13) 117 | * (Apollon77) prevent warnings with js-controller 3.2 118 | * (Apollon77) update js-controller dependency to at least require js-controller 2.0.0 119 | 120 | ### 3.1.6 (2020-11-15) 121 | * (Apollon77) update OpenSML lib to support Holley DTZ541 wrongly implemented CRC Algorithm 122 | 123 | ### 3.1.5 (2020-09-21) 124 | * (Apollon77) update dependencies to prevent some crash cases and optimize tcp mode 125 | 126 | ### 3.1.3 (2020-07-20) 127 | * (Apollon77) update dependencies to prevent some crash cases 128 | 129 | ### 3.1.2 (2020-04-12) 130 | * (Apollon77) catch errors when no memory is available anymore and stop processing 131 | 132 | ### 3.1.1 (2020-03-11) 133 | * (Apollon77) fix admin when switching to TCPTransport 134 | * (Apollon77) bugfixes and optimizations 135 | 136 | ### 3.1.0 (2020-03-08) 137 | * (Apollon77) bugfixes and optimizations 138 | * (Apollon77) experimental TCP support, please give feedback 139 | 140 | ### 3.0.10 (2020-02-05) 141 | * (Apollon77) make sure HTTP based smartmeters are also polled frequently when responses are invalid 142 | * (Apollon77) other optimizations 143 | * (Apollon77) Switch Sentry to iobroker own instance hosted in germany 144 | 145 | ### 3.0.8 (2019-12-20) 146 | * (Apollon77) errors prevented when stopping to process data 147 | 148 | ### 3.0.7 (2019-12-18) 149 | * (Apollon77) errors prevented when stopping to process data 150 | 151 | ### 3.0.6 (2019-12-07) 152 | * (Apollon77) serial port configuration further optimized 153 | * (Apollon77) update smartmeter-obis lib to fix some edge case errors and serial close handling 154 | 155 | ### 3.0.3 (2019-11-30) 156 | * (Apollon77) serial port configuration further optimized 157 | 158 | ### 3.0.2 (2019-11-29) 159 | * (Apollon77) Fix use of "/dev/serial/by-id" paths on linux if available 160 | 161 | ### 3.0.1 (2019-11-27) 162 | * (Apollon77) BREAKING CHANGE: Supports nodejs 8.x+ only, up to 12.x 163 | * (Apollon77) support compact mode 164 | * (Apollon77) update to latest library versions to fix problems and add special handling for some smart meters with broken firmware 165 | * (Apollon77) Use "/dev/serial/by-id" paths on linux if available; add port selection to Admin 166 | * (Apollon77) Add Sentry for error reporting 167 | 168 | ### 2.0.0 (2019-03-22) 169 | * (Apollon77) BREAKING CHANGE: State names changed because * no longer supported. Is replaced by __ now because of possible collisions in state names with only one _ 170 | 171 | ### 1.2.2 (2018-11-11) 172 | * Update smartmeter library, fix HTTP-JSON-Transport 173 | 174 | ### 1.2.1 (2018-06-23) 175 | * BREAKING CHANGE: State names changed because * no longer supported. Is replaced by _ 176 | 177 | ### 1.1.3 (2018-04-13) 178 | * Fix Admin 179 | 180 | ### 1.1.2 (26.03.2018) 181 | * Add better support for devices with more then 16 values (OpenSML Library upgrade) 182 | 183 | ### 1.1.0 (31.01.2018) 184 | * Allow multiple queries for D0 and Serial-Bidirectional communication 185 | * a lot of bugfixing and Optimizations 186 | * Switch to SerialPort 6.0.4 to hopefully get more stable (less/no SIGSEGV/SIGABRT ...) 187 | 188 | ### 1.0.0 (25.08.2017) 189 | * Update smartmeter library and fix some timing issues 190 | 191 | ### 0.5.12 (23.07.2017) 192 | * update SML library 193 | 194 | ### 0.5.11 (21.06.2017) 195 | * optimize D0 handling and add support for Dutch smartmeter using DSRM protocol. 196 | 197 | ### 0.5.8 (06.04.2017) 198 | * optimize Serial handling on Windows (because pause and resume are not supported there) 199 | 200 | ### 0.5.6 (02.04.2017) 201 | * update library 202 | 203 | ### 0.5.5 (19.03.2017) 204 | * improved baudrate-changeover logic for D0 protocol (now hopefully finally) 205 | * enhanced D0 protocol support for multiple values 206 | 207 | ### 0.5.0 (26.02.2017) 208 | * maintenance update 209 | 210 | ### 0.4.2 (27.02.2017) 211 | * one last try to fix the crashes SIGABRT/SIGSEGV 212 | 213 | ### 0.4.1 (24.02.2017) 214 | * Fix potential hanging communication with D0 Serial 215 | 216 | ### 0.4.0 (23.02.2017) 217 | * Optimize for D0 Message handling and baudrate changeovers 218 | 219 | ### 0.3.2 (22.02.2017) 220 | * Optimize D0 protocol handling for mode E 221 | 222 | ### 0.3.1 (12.02.2017) 223 | * Finalize Adapter config and added some informations 224 | 225 | ### 0.3.0 (11.02.2017) 226 | * We now should be quiet stable 227 | 228 | ### 0.1.1 229 | * Update smartmeter-obis library to 0.2.5 to add Serial Timeout for Request/Response protocol 230 | 231 | ### 0.1.0 232 | * Initial version for public testing 233 | 234 | ### 0.0.1 235 | * Initial version for internal testing 236 | 237 | ## License 238 | 239 | The MIT License (MIT) 240 | 241 | Copyright (c) 2017-2023 Apollon77 242 | 243 | Permission is hereby granted, free of charge, to any person obtaining a copy 244 | of this software and associated documentation files (the "Software"), to deal 245 | in the Software without restriction, including without limitation the rights 246 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 247 | copies of the Software, and to permit persons to whom the Software is 248 | furnished to do so, subject to the following conditions: 249 | 250 | The above copyright notice and this permission notice shall be included in all 251 | copies or substantial portions of the Software. 252 | 253 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 254 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 255 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 256 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 257 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 258 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 259 | SOFTWARE. 260 | -------------------------------------------------------------------------------- /admin/i18n/de/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Liste der seriellen Schnittstellen ist nicht verfügbar", 3 | "Please wait till port list is loaded ...": "Bitte warten bis die Liste der seriellen Schnittstellen geladen wurde …", 4 | "Use custom Serial port path": "Benutzerdefinierten Namen für die serielle Schnittstelle angeben", 5 | "field_anotherQueryDelay": "Verzögerung zwischen zwei SignOn-Nachrichten", 6 | "field_customSerialPort": "Benutzerdefinierter Pfad der seriellen Schnittstelle", 7 | "field_obisFallbackMedium": "D0: Ersatz OBIS-Medium", 8 | "field_obisNameLanguage": "Sprache der Datenpunktnamen", 9 | "field_protocol": "Daten-Protokoll", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: Überschreiben der Baudrate-Änderung", 11 | "field_protocolD0DeviceAddress": "D0: Geräteadresse", 12 | "field_protocolD0ModeOverwrite": "D0: Überschreiben des Modus", 13 | "field_protocolD0SignOnMessage": "D0: Kommando SignOn-Nachricht", 14 | "field_protocolD0WakeupCharacters": "D0: Anzahl WakeUp-Zeichen", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: CRC-Prüfsummenfehler ignorieren", 16 | "field_protocolSmlInputEncoding": "Kodierung der SML Daten", 17 | "field_requestInterval": "Datenabfrageintervall", 18 | "field_transport": "Datenübertragung", 19 | "field_transportHttpRequestTimeout": "Wartezeit auf HTTP-Antwort", 20 | "field_transportHttpRequestUrl": "HTTP-URL", 21 | "field_transportLocalFilePath": "Absoluter Pfad zur Datei", 22 | "field_transportSerialBaudrate": "Serielles Gerät: Baudrate", 23 | "field_transportSerialDataBits": "Serielles Gerät: Data-Bits", 24 | "field_transportSerialMessageTimeout": "Wartezeit auf serielle Antwort", 25 | "field_transportSerialParity": "Serielles Gerät: Parität", 26 | "field_transportSerialPort": "Serielles Gerät: Name", 27 | "field_transportSerialStopBits": "Serielles Gerät: Stop-Bits", 28 | "field_transportTcpHost": "TCP Server", 29 | "field_transportTcpPort": "TCP Port", 30 | "heading_GeneralSettings": "Allgemeine Einstellungen", 31 | "heading_ProtocolSpecificSettings": "Einstellungen Datenprotokoll", 32 | "heading_TransportSpecificSettings": "Einstellungen Datenübertragung", 33 | "info_anotherQueryDelay": "Eine Verzögerung kann nötig sein, um dem Gerät genügend Zeit für einen Reset oder zur Initialisierung zu geben.
Standardwert wenn leer: 1000", 34 | "info_obisFallbackMedium": "(wird zur Namensauflösung benutzt falls kein OBIS-Medium in den Daten existiert)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Leer, wenn die Baudrate vom Gerät verwendet werden soll", 38 | "info_protocolD0DeviceAddress": "(standardmäßig leer)", 39 | "info_protocolD0ModeOverwrite": "Leer wenn der zurückgegebene Modus vom Gerät genutzt werden soll, ansonsten Überschreiben: A, B, C, D, E", 40 | "info_protocolD0SignOnMessage": "(Standard wenn leer: ?
um die Standard-/Pflichtwerte zu lesen)", 41 | "info_protocolD0WakeupCharacters": "(NULL Zeichen die gesendet werden um das Gerät aufzuwecken und Zeit zur Initialisierung zu geben)
Standard wenn leer: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Definition einer alternativen Eingabedaten-Kodierung der eingehenden SML-Daten. Am ehesten relevant für den TCP-Transport.", 44 | "info_requestInterval": "0 benutzen um ohne Unterbrechung zu lesen/anzufordern,
Standard wenn leer: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Standard wenn leer: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Wenn leer werden Protokoll-Standards verwendet
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Wenn leer werden Protokoll-Standards verwendet
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Wie lange soll auf eine korrekte Nachricht gewartet werden? Die Baudrate muss berücksichtigt werden!
Standard wenn leer: 120s", 52 | "info_transportSerialParity": "Wenn leer werden Protokoll-Standards verwendet
(D0: even, SML: none)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Wenn leer werden Protokoll-Standards verwendet
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Smartmeter Adapter Einstellungen", 58 | "value_no_overwrite": "Modus des Devices nutzen", 59 | "value_obisFallbackMedium_0": "0: Abstrakt", 60 | "value_obisFallbackMedium_1": "1: Strom", 61 | "value_obisFallbackMedium_16": "16: Öl", 62 | "value_obisFallbackMedium_17": "17: Druckluft", 63 | "value_obisFallbackMedium_18": "18: Stickstoff", 64 | "value_obisFallbackMedium_4": "4: Heizkostenverteiler", 65 | "value_obisFallbackMedium_5": "5: Kälte", 66 | "value_obisFallbackMedium_6": "6: Wärme", 67 | "value_obisFallbackMedium_7": "7: Gas", 68 | "value_obisFallbackMedium_8": "8: Kaltwasser", 69 | "value_obisFallbackMedium_9": "9: Warmwasser", 70 | "value_obisNameLanguage_de": "Deutsch", 71 | "value_obisNameLanguage_en": "Englisch", 72 | "value_protocolSmlInputEncoding_binary": "Binärdaten (Standard)", 73 | "value_protocol_D0Protocol": "D0 (WakeUp, SignOn, Data)", 74 | "value_protocol_JsonEfrProtocol": "JSON-Format für EFR SmartGridHub", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Standard Protokoll/Übertragung", 77 | "value_transport_HttpRequestTransport": "Daten von einer HTTP-URL auslesen", 78 | "value_transport_LocalFileTransport": "Daten aus einer lokalen Datei lesen", 79 | "value_transport_SerialRequestResponseTransport": "Serielles Gerät mit bidir. Komm.", 80 | "value_transport_SerialResponseTransport": "Serielle Daten werden nur gelesen", 81 | "value_transport_TCPTransport": "Netzwerk/TCP-Daten werden nur gelesen" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/en/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Serial port list not available", 3 | "Please wait till port list is loaded ...": "Please wait till the serial port list is loaded …", 4 | "Use custom Serial port path": "Set a custom serial port name", 5 | "field_anotherQueryDelay": "Delay between multiple SignOn-Messages", 6 | "field_customSerialPort": "Custom serial port path", 7 | "field_obisFallbackMedium": "D0: Fallback OBIS-Medium", 8 | "field_obisNameLanguage": "Language for state names", 9 | "field_protocol": "Data protocol", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: Baudrate changeover overwrite", 11 | "field_protocolD0DeviceAddress": "D0: Device address", 12 | "field_protocolD0ModeOverwrite": "D0: Mode overwrite", 13 | "field_protocolD0SignOnMessage": "D0: SignOn-Message command", 14 | "field_protocolD0WakeupCharacters": "D0: Number of WakeUp-Characters", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: Ignore CRC checksum error", 16 | "field_protocolSmlInputEncoding": "SML Input data encoding", 17 | "field_requestInterval": "Data request interval", 18 | "field_transport": "Data transfer", 19 | "field_transportHttpRequestTimeout": "HTTP-Request timeout", 20 | "field_transportHttpRequestUrl": "HTTP-URL", 21 | "field_transportLocalFilePath": "Absolute path to local datafile", 22 | "field_transportSerialBaudrate": "Serial device baudrate", 23 | "field_transportSerialDataBits": "Serial device DataBits", 24 | "field_transportSerialMessageTimeout": "Serial-Response timeout", 25 | "field_transportSerialParity": "Serial device parity", 26 | "field_transportSerialPort": "Serial device name", 27 | "field_transportSerialStopBits": "Serial device StopBits", 28 | "field_transportTcpHost": "TCP Host", 29 | "field_transportTcpPort": "TCP Port", 30 | "heading_GeneralSettings": "General settings", 31 | "heading_ProtocolSpecificSettings": "Data protocol settings", 32 | "heading_TransportSpecificSettings": "Data transfer settings", 33 | "info_anotherQueryDelay": "A delay is relevant to give the device some time to reset or initialize before the adapter sends out a new message.
Default if empty: 1000", 34 | "info_obisFallbackMedium": "(used for name resolving when no OBIS medium is defined in the incoming data)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Empty if baudrate from the device is used", 38 | "info_protocolD0DeviceAddress": "(Empty by default)", 39 | "info_protocolD0ModeOverwrite": "(Empty if the mode should be used which is provided by the device, else A, B, C, D or E is allowed to overwrite", 40 | "info_protocolD0SignOnMessage": "(Default if empty: ?
to read only default/mandatory values)", 41 | "info_protocolD0WakeupCharacters": "(NULL characters to sent to wake up the device and give him time to initialize)
Default if empty: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Definition of an alternative input data encoding of the incoming SML data. Most likely relevant for TCP transport.", 44 | "info_requestInterval": "Use 0 for continuos reading/requesting,
Default if empty: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Default if empty: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "If empty, protocol defaults are used
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "If empty, protocol defaults are used
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Defines how long to wait for a valid message before restarting. Consider the baudrate!
Default if empty: 120s", 52 | "info_transportSerialParity": "If empty, protocol defaults are used
(D0: even, SML: none)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "If empty, protocol defaults are used
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Smartmeter Adapter Settings", 58 | "value_no_overwrite": "Use mode defined by the device", 59 | "value_obisFallbackMedium_0": "0: Abstract", 60 | "value_obisFallbackMedium_1": "1: Electricity", 61 | "value_obisFallbackMedium_16": "16: Oil", 62 | "value_obisFallbackMedium_17": "17: Compressed air", 63 | "value_obisFallbackMedium_18": "18: Nitrogen", 64 | "value_obisFallbackMedium_4": "4: Heat cost Allocator", 65 | "value_obisFallbackMedium_5": "5: Cooling", 66 | "value_obisFallbackMedium_6": "6: Heat", 67 | "value_obisFallbackMedium_7": "7: Gas", 68 | "value_obisFallbackMedium_8": "8: Cold water", 69 | "value_obisFallbackMedium_9": "9: Hot water", 70 | "value_obisNameLanguage_de": "German", 71 | "value_obisNameLanguage_en": "English", 72 | "value_protocolSmlInputEncoding_binary": "Binary data (Default)", 73 | "value_protocol_D0Protocol": "D0 (WakeUp, SignOn, Data)", 74 | "value_protocol_JsonEfrProtocol": "JSON-Format for EFR SmartGridHub", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Protocol-/Transport-Default", 77 | "value_transport_HttpRequestTransport": "Read data from an HTTP URL", 78 | "value_transport_LocalFileTransport": "Read data from a local file", 79 | "value_transport_SerialRequestResponseTransport": "Serial device with Bi-dir. comm.", 80 | "value_transport_SerialResponseTransport": "Serial device reading data only", 81 | "value_transport_TCPTransport": "TCP device reading data only" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/es/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Lista de puertos no disponible", 3 | "Please wait till port list is loaded ...": "Espere hasta que se cargue la lista de puertos ...", 4 | "Use custom Serial port path": "Establecer un nombre de puerto serie personalizado", 5 | "field_anotherQueryDelay": "Retraso entre varios mensajes de inicio de sesión", 6 | "field_customSerialPort": "Ruta de puerto serie personalizada", 7 | "field_obisFallbackMedium": "D0: OBIS de respaldo medio", 8 | "field_obisNameLanguage": "Idioma para nombres de puntos de datos", 9 | "field_protocol": "Protocolo de datos", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: Sobreescritura de cambio de velocidad en baudios", 11 | "field_protocolD0DeviceAddress": "D0: dirección del dispositivo", 12 | "field_protocolD0ModeOverwrite": "D0: Modo de sobrescritura", 13 | "field_protocolD0SignOnMessage": "D0: Comando SignOn-Message", 14 | "field_protocolD0WakeupCharacters": "D0: Número de personajes de activación", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: ignorar el error de suma de comprobación CRC", 16 | "field_protocolSmlInputEncoding": "Codificación de datos de entrada SML", 17 | "field_requestInterval": "Intervalo de solicitud de datos", 18 | "field_transport": "Transferencia de datos", 19 | "field_transportHttpRequestTimeout": "Tiempo de espera de solicitud HTTP", 20 | "field_transportHttpRequestUrl": "URL HTTP", 21 | "field_transportLocalFilePath": "Ruta absoluta al archivo de datos local", 22 | "field_transportSerialBaudrate": "Velocidad en baudios del dispositivo en serie", 23 | "field_transportSerialDataBits": "Bits de datos del dispositivo en serie", 24 | "field_transportSerialMessageTimeout": "Tiempo de espera de respuesta en serie", 25 | "field_transportSerialParity": "Paridad de dispositivos en serie", 26 | "field_transportSerialPort": "Nombre del dispositivo en serie", 27 | "field_transportSerialStopBits": "StopBits de dispositivo serie", 28 | "field_transportTcpHost": "Host TCP", 29 | "field_transportTcpPort": "Puerto TCP", 30 | "heading_GeneralSettings": "Configuración general", 31 | "heading_ProtocolSpecificSettings": "Configuración del protocolo de datos", 32 | "heading_TransportSpecificSettings": "Configuración de transferencia de datos", 33 | "info_anotherQueryDelay": "Se necesita un retraso porque el adaptador enviará un nuevo mensaje y el dispositivo debe reiniciarse primero,
Predeterminado si está vacío: 1000", 34 | "info_obisFallbackMedium": "(utilizado para la resolución de nombres cuando no se define ningún medio OBIS en el mensaje de datos)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Vacío si se usa la velocidad en baudios del dispositivo", 38 | "info_protocolD0DeviceAddress": "(Vacío por defecto)", 39 | "info_protocolD0ModeOverwrite": "(Vacío si se debe usar el Modo de dispositivo; de lo contrario, A, B, C, D o E pueden sobrescribir", 40 | "info_protocolD0SignOnMessage": "(Por defecto si está vacío: ?
para leer los valores obligatorios)", 41 | "info_protocolD0WakeupCharacters": "(Caracteres NULL)
Predeterminado si está vacío: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Definición de una codificación de datos de entrada alternativa de los datos SML entrantes. Probablemente relevante para el transporte TCP.", 44 | "info_requestInterval": "use 0 para lectura / solicitud continua,
Predeterminado si está vacío: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Predeterminado si está vacío: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Si está vacío, se utilizan los valores predeterminados del protocolo
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Si está vacío, se utilizan los valores predeterminados del protocolo
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Define cuánto tiempo esperar por un mensaje válido antes de reiniciar. ", 52 | "info_transportSerialParity": "Si está vacío, se utilizan los valores predeterminados del protocolo
(D0: par, SML: ninguno)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Si está vacío, se utilizan los valores predeterminados del protocolo
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Configuración del adaptador de Smartmeter", 58 | "value_no_overwrite": "Modo de uso definido por dispositivo", 59 | "value_obisFallbackMedium_0": "0: Resumen", 60 | "value_obisFallbackMedium_1": "1: electricidad", 61 | "value_obisFallbackMedium_16": "16: aceite", 62 | "value_obisFallbackMedium_17": "17: aire comprimido", 63 | "value_obisFallbackMedium_18": "18: nitrógeno", 64 | "value_obisFallbackMedium_4": "4: Asignador de costes de calefacción", 65 | "value_obisFallbackMedium_5": "5: enfriamiento", 66 | "value_obisFallbackMedium_6": "6: Calor", 67 | "value_obisFallbackMedium_7": "7: gas", 68 | "value_obisFallbackMedium_8": "8: agua fría", 69 | "value_obisFallbackMedium_9": "9: agua caliente", 70 | "value_obisNameLanguage_de": "alemán", 71 | "value_obisNameLanguage_en": "inglés", 72 | "value_protocolSmlInputEncoding_binary": "Datos binarios (predeterminado)", 73 | "value_protocol_D0Protocol": "D0 (despertar, inicio de sesión, datos)", 74 | "value_protocol_JsonEfrProtocol": "EFR SmartGridHub en formato JSON", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Protocolo / transporte predeterminado", 77 | "value_transport_HttpRequestTransport": "Leer datos de una URL HTTP", 78 | "value_transport_LocalFileTransport": "Leer datos de un archivo local", 79 | "value_transport_SerialRequestResponseTransport": "Dispositivo serie con Bi-dir. ", 80 | "value_transport_SerialResponseTransport": "Solo datos de lectura del dispositivo serie", 81 | "value_transport_TCPTransport": "Solo datos de lectura del dispositivo TCP" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/fr/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Liste de ports non disponible", 3 | "Please wait till port list is loaded ...": "Veuillez patienter jusqu'à ce que la liste de ports soit chargée ...", 4 | "Use custom Serial port path": "Définir un nom de port série personnalisé", 5 | "field_anotherQueryDelay": "Délai entre plusieurs messages SignOn", 6 | "field_customSerialPort": "Chemin du port série personnalisé", 7 | "field_obisFallbackMedium": "D0: OBIS-Medium de secours", 8 | "field_obisNameLanguage": "Langue des noms de points de données", 9 | "field_protocol": "Protocole de données", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: Écrasement du changement de vitesse en bauds", 11 | "field_protocolD0DeviceAddress": "D0: Adresse de l'appareil", 12 | "field_protocolD0ModeOverwrite": "D0: Écrasement de mode", 13 | "field_protocolD0SignOnMessage": "D0: Commande SignOn-Message", 14 | "field_protocolD0WakeupCharacters": "D0: Nombre de caractères WakeUp", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: Ignorer l'erreur de somme de contrôle CRC", 16 | "field_protocolSmlInputEncoding": "Encodage des données d'entrée SML", 17 | "field_requestInterval": "Intervalle de demande de données", 18 | "field_transport": "Transfert de données", 19 | "field_transportHttpRequestTimeout": "Délai d'expiration de la requête HTTP", 20 | "field_transportHttpRequestUrl": "URL HTTP", 21 | "field_transportLocalFilePath": "Chemin absolu vers le fichier de données local", 22 | "field_transportSerialBaudrate": "Vitesse de transmission du périphérique série", 23 | "field_transportSerialDataBits": "Bits de données de périphérique série", 24 | "field_transportSerialMessageTimeout": "Délai de réponse série", 25 | "field_transportSerialParity": "Parité de périphérique série", 26 | "field_transportSerialPort": "Nom du périphérique série", 27 | "field_transportSerialStopBits": "Bits d'arrêt de périphérique série", 28 | "field_transportTcpHost": "Hôte TCP", 29 | "field_transportTcpPort": "Port TCP", 30 | "heading_GeneralSettings": "réglages généraux", 31 | "heading_ProtocolSpecificSettings": "Paramètres du protocole de données", 32 | "heading_TransportSpecificSettings": "Paramètres de transfert de données", 33 | "info_anotherQueryDelay": "Un délai est nécessaire car l'adaptateur enverra un nouveau message et l'appareil doit d'abord être réinitialisé,
Valeur par défaut si vide: 1000", 34 | "info_obisFallbackMedium": "(utilisé pour la résolution de noms lorsqu'aucun support OBIS n'est défini dans le message de données)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Vide si le débit en bauds de l'appareil est utilisé", 38 | "info_protocolD0DeviceAddress": "(Vide par défaut)", 39 | "info_protocolD0ModeOverwrite": "(Vide si le mode de l'appareil doit être utilisé, sinon A, B, C, D ou E est autorisé à écraser", 40 | "info_protocolD0SignOnMessage": "(Par défaut si vide: ?
pour lire les valeurs obligatoires)", 41 | "info_protocolD0WakeupCharacters": "(Caractères NULL)
Valeur par défaut si vide: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Définition d'un autre codage des données d'entrée des données SML entrantes. Très probablement pertinent pour le transport TCP.", 44 | "info_requestInterval": "utilisez 0 pour la lecture / demande en continu,
Par défaut si vide: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Valeur par défaut si vide: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Si vide, les valeurs par défaut du protocole sont utilisées
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Si vide, les valeurs par défaut du protocole sont utilisées
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Définit combien de temps attendre un message valide avant de redémarrer. ", 52 | "info_transportSerialParity": "Si vide, les valeurs par défaut du protocole sont utilisées
(D0: pair, SML: aucun)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Si vide, les valeurs par défaut du protocole sont utilisées
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Paramètres de l'adaptateur Smartmeter", 58 | "value_no_overwrite": "Utiliser le mode défini par l'appareil", 59 | "value_obisFallbackMedium_0": "0: Résumé", 60 | "value_obisFallbackMedium_1": "1: électricité", 61 | "value_obisFallbackMedium_16": "16: Huile", 62 | "value_obisFallbackMedium_17": "17: Air comprimé", 63 | "value_obisFallbackMedium_18": "18: Azote", 64 | "value_obisFallbackMedium_4": "4: Allocateur des coûts de chauffage", 65 | "value_obisFallbackMedium_5": "5: refroidissement", 66 | "value_obisFallbackMedium_6": "6: Chaleur", 67 | "value_obisFallbackMedium_7": "7: Gaz", 68 | "value_obisFallbackMedium_8": "8: eau froide", 69 | "value_obisFallbackMedium_9": "9: eau chaude", 70 | "value_obisNameLanguage_de": "Allemand", 71 | "value_obisNameLanguage_en": "Anglais", 72 | "value_protocolSmlInputEncoding_binary": "Données binaires (par défaut)", 73 | "value_protocol_D0Protocol": "D0 (WakeUp, SignOn, données)", 74 | "value_protocol_JsonEfrProtocol": "SmartGridHub EFR au format JSON", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Protocol- / Transport-Default", 77 | "value_transport_HttpRequestTransport": "Lire les données à partir d'une URL HTTP", 78 | "value_transport_LocalFileTransport": "Lire les données à partir d'un fichier local", 79 | "value_transport_SerialRequestResponseTransport": "Périphérique série avec Bi-dir. ", 80 | "value_transport_SerialResponseTransport": "Périphérique série lisant uniquement les données", 81 | "value_transport_TCPTransport": "Périphérique TCP lisant uniquement les données" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/it/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Elenco delle porte non disponibile", 3 | "Please wait till port list is loaded ...": "Attendere il caricamento dell'elenco delle porte ...", 4 | "Use custom Serial port path": "Imposta un nome di porta seriale personalizzato", 5 | "field_anotherQueryDelay": "Ritardo tra più messaggi di accesso", 6 | "field_customSerialPort": "Percorso della porta seriale personalizzata", 7 | "field_obisFallbackMedium": "D0: Fallback OBIS-Medium", 8 | "field_obisNameLanguage": "Lingua per i nomi dei punti dati", 9 | "field_protocol": "Protocollo dati", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: Baudrate Changeover Overwrite", 11 | "field_protocolD0DeviceAddress": "D0: indirizzo del dispositivo", 12 | "field_protocolD0ModeOverwrite": "D0: modalità sovrascrittura", 13 | "field_protocolD0SignOnMessage": "D0: comando SignOn-Message", 14 | "field_protocolD0WakeupCharacters": "D0: numero di caratteri WakeUp", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: ignora l'errore di checksum CRC", 16 | "field_protocolSmlInputEncoding": "Codifica dei dati di input SML", 17 | "field_requestInterval": "Intervallo di richiesta dati", 18 | "field_transport": "Trasferimento dati", 19 | "field_transportHttpRequestTimeout": "Timeout richiesta HTTP", 20 | "field_transportHttpRequestUrl": "HTTP-URL", 21 | "field_transportLocalFilePath": "Percorso assoluto al file di dati locale", 22 | "field_transportSerialBaudrate": "Velocità in baud del dispositivo seriale", 23 | "field_transportSerialDataBits": "Bit di dati del dispositivo seriale", 24 | "field_transportSerialMessageTimeout": "Timeout risposta seriale", 25 | "field_transportSerialParity": "Parità dispositivo seriale", 26 | "field_transportSerialPort": "Nome dispositivo seriale", 27 | "field_transportSerialStopBits": "StopBits dispositivo seriale", 28 | "field_transportTcpHost": "Host TCP", 29 | "field_transportTcpPort": "Porta TCP", 30 | "heading_GeneralSettings": "impostazioni generali", 31 | "heading_ProtocolSpecificSettings": "Impostazioni del protocollo dati", 32 | "heading_TransportSpecificSettings": "Impostazioni di trasferimento dati", 33 | "info_anotherQueryDelay": "È necessario un ritardo perché l'adattatore invierà un nuovo messaggio e il dispositivo dovrà prima essere ripristinato,
Predefinito se vuoto: 1000", 34 | "info_obisFallbackMedium": "(utilizzato per la risoluzione del nome quando nessun supporto OBIS è definito nel messaggio di dati)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Vuoto se si utilizza Baudrate from Device", 38 | "info_protocolD0DeviceAddress": "(Vuoto per impostazione predefinita)", 39 | "info_protocolD0ModeOverwrite": "(Vuoto se deve essere utilizzata la modalità dal dispositivo, altrimenti è consentito sovrascrivere A, B, C, D o E.", 40 | "info_protocolD0SignOnMessage": "(Predefinito se vuoto: ?
per leggere i valori obbligatori)", 41 | "info_protocolD0WakeupCharacters": "(Caratteri NULL)
Predefinito se vuoto: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Definizione di una codifica dei dati di input alternativa dei dati SML in ingresso. Molto probabilmente rilevante per il trasporto TCP.", 44 | "info_requestInterval": "usa 0 per la lettura / richiesta continua,
Predefinito se vuoto: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Default se vuoto: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Se vuoto, vengono utilizzati i valori predefiniti del protocollo
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Se vuoto, vengono utilizzati i valori predefiniti del protocollo
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Definisce per quanto tempo attendere un messaggio valido prima del riavvio. ", 52 | "info_transportSerialParity": "Se vuoto, vengono utilizzati i valori predefiniti del protocollo
(D0: pari, SML: nessuno)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Se vuoto, vengono utilizzati i valori predefiniti del protocollo
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Impostazioni adattatore Smartmeter", 58 | "value_no_overwrite": "Modalità d'uso definita dal dispositivo", 59 | "value_obisFallbackMedium_0": "0: astratto", 60 | "value_obisFallbackMedium_1": "1: elettricità", 61 | "value_obisFallbackMedium_16": "16: Petrolio", 62 | "value_obisFallbackMedium_17": "17: Aria compressa", 63 | "value_obisFallbackMedium_18": "18: azoto", 64 | "value_obisFallbackMedium_4": "4: Allocatore dei costi di riscaldamento", 65 | "value_obisFallbackMedium_5": "5: raffreddamento", 66 | "value_obisFallbackMedium_6": "6: calore", 67 | "value_obisFallbackMedium_7": "7: gas", 68 | "value_obisFallbackMedium_8": "8: acqua fredda", 69 | "value_obisFallbackMedium_9": "9: acqua calda", 70 | "value_obisNameLanguage_de": "Tedesco", 71 | "value_obisNameLanguage_en": "inglese", 72 | "value_protocolSmlInputEncoding_binary": "Dati binari (impostazione predefinita)", 73 | "value_protocol_D0Protocol": "D0 (WakeUp, SignOn, Data)", 74 | "value_protocol_JsonEfrProtocol": "Formato JSON EFR SmartGridHub", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Predefinito protocollo / trasporto", 77 | "value_transport_HttpRequestTransport": "Leggi i dati da un URL HTTP", 78 | "value_transport_LocalFileTransport": "Leggi i dati da un file locale", 79 | "value_transport_SerialRequestResponseTransport": "Dispositivo seriale con Bi-dir. ", 80 | "value_transport_SerialResponseTransport": "Solo dati di lettura del dispositivo seriale", 81 | "value_transport_TCPTransport": "Solo lettura dei dati del dispositivo TCP" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/nl/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Poortlijst niet beschikbaar", 3 | "Please wait till port list is loaded ...": "Even geduld tot de poortlijst is geladen ...", 4 | "Use custom Serial port path": "Stel een aangepaste seriële poortnaam in", 5 | "field_anotherQueryDelay": "Vertraging tussen meerdere SignOn-berichten", 6 | "field_customSerialPort": "Aangepast serieel poortpad", 7 | "field_obisFallbackMedium": "D0: terugval OBIS-medium", 8 | "field_obisNameLanguage": "Taal voor datapuntnamen", 9 | "field_protocol": "Gegevensprotocol", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: overschrijven baudrate-omschakeling", 11 | "field_protocolD0DeviceAddress": "D0: apparaatadres", 12 | "field_protocolD0ModeOverwrite": "D0: modus overschrijven", 13 | "field_protocolD0SignOnMessage": "D0: SignOn-Message Command", 14 | "field_protocolD0WakeupCharacters": "D0: aantal WakeUp-tekens", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: Negeer CRC checksum-fout", 16 | "field_protocolSmlInputEncoding": "SML-invoergegevenscodering", 17 | "field_requestInterval": "Interval voor het opvragen van gegevens", 18 | "field_transport": "Data overdracht", 19 | "field_transportHttpRequestTimeout": "Time-out voor HTTP-verzoek", 20 | "field_transportHttpRequestUrl": "HTTP-URL", 21 | "field_transportLocalFilePath": "Absoluut pad naar lokaal gegevensbestand", 22 | "field_transportSerialBaudrate": "Baudrate van het seriële apparaat", 23 | "field_transportSerialDataBits": "Seriële apparaatdatabits", 24 | "field_transportSerialMessageTimeout": "Time-out voor seriële respons", 25 | "field_transportSerialParity": "Pariteit van seriële apparaten", 26 | "field_transportSerialPort": "Seriële apparaatnaam", 27 | "field_transportSerialStopBits": "StopBits voor seriële apparaten", 28 | "field_transportTcpHost": "TCP-host", 29 | "field_transportTcpPort": "TCP-poort", 30 | "heading_GeneralSettings": "Algemene instellingen", 31 | "heading_ProtocolSpecificSettings": "Instellingen voor gegevensprotocol", 32 | "heading_TransportSpecificSettings": "Instellingen voor gegevensoverdracht", 33 | "info_anotherQueryDelay": "Er is een vertraging nodig omdat de adapter een nieuw bericht verzendt en het apparaat eerst moet worden gereset,
Standaardwaarde indien leeg: 1000", 34 | "info_obisFallbackMedium": "(gebruikt voor naamomzetting wanneer er geen OBIS-medium is gedefinieerd in het gegevensbericht)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Leeg als baudrate van apparaat wordt gebruikt", 38 | "info_protocolD0DeviceAddress": "(Standaard leeg)", 39 | "info_protocolD0ModeOverwrite": "(Leeg als Mode from Device moet worden gebruikt, anders mag A, B, C, D of E worden overschreven", 40 | "info_protocolD0SignOnMessage": "(Standaard indien leeg: ?
om verplichte waarden te lezen)", 41 | "info_protocolD0WakeupCharacters": "(NULL-tekens)
Standaard indien leeg: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Definitie van een alternatieve invoergegevenscodering van de inkomende SML-gegevens. Waarschijnlijk relevant voor TCP-transport.", 44 | "info_requestInterval": "gebruik 0 voor continu lezen / vragen,
Standaard indien leeg: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Standaard indien leeg: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Indien leeg, worden standaardprotocolinstellingen gebruikt
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Indien leeg, worden standaardprotocolinstellingen gebruikt
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Bepaalt hoe lang op een geldig bericht moet worden gewacht voordat opnieuw wordt opgestart. ", 52 | "info_transportSerialParity": "Indien leeg, worden protocolstandaarden gebruikt
(D0: even, SML: geen)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Indien leeg, worden standaardprotocolinstellingen gebruikt
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Smartmeter Adapter-instellingen", 58 | "value_no_overwrite": "Gebruiksmodus gedefinieerd door apparaat", 59 | "value_obisFallbackMedium_0": "0: Samenvatting", 60 | "value_obisFallbackMedium_1": "1: Elektriciteit", 61 | "value_obisFallbackMedium_16": "16: Olie", 62 | "value_obisFallbackMedium_17": "17: Perslucht", 63 | "value_obisFallbackMedium_18": "18: Stikstof", 64 | "value_obisFallbackMedium_4": "4: Warmtekostenverdeler", 65 | "value_obisFallbackMedium_5": "5: Koeling", 66 | "value_obisFallbackMedium_6": "6: Warmte", 67 | "value_obisFallbackMedium_7": "7: Gas", 68 | "value_obisFallbackMedium_8": "8: Koud water", 69 | "value_obisFallbackMedium_9": "9: Heet water", 70 | "value_obisNameLanguage_de": "Duitse", 71 | "value_obisNameLanguage_en": "Engels", 72 | "value_protocolSmlInputEncoding_binary": "Binaire gegevens (standaard)", 73 | "value_protocol_D0Protocol": "D0 (WakeUp, SignOn, Data)", 74 | "value_protocol_JsonEfrProtocol": "EFR SmartGridHub in JSON-indeling", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Protocol- / transportstandaard", 77 | "value_transport_HttpRequestTransport": "Lees gegevens van een HTTP-URL", 78 | "value_transport_LocalFileTransport": "Lees gegevens uit een lokaal bestand", 79 | "value_transport_SerialRequestResponseTransport": "Serieel apparaat met Bi-dir. ", 80 | "value_transport_SerialResponseTransport": "Alleen seriële apparaten lezen gegevens", 81 | "value_transport_TCPTransport": "Alleen TCP-apparaat leest gegevens" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/pl/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Lista portów niedostępna", 3 | "Please wait till port list is loaded ...": "Poczekaj, aż lista portów zostanie załadowana ...", 4 | "Use custom Serial port path": "Ustaw niestandardową nazwę portu szeregowego", 5 | "field_anotherQueryDelay": "Opóźnienie między wieloma komunikatami SignOn", 6 | "field_customSerialPort": "Niestandardowa ścieżka portu szeregowego", 7 | "field_obisFallbackMedium": "D0: rezerwowy OBIS-Medium", 8 | "field_obisNameLanguage": "Język nazw punktów danych", 9 | "field_protocol": "Protokół danych", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: Nadpisanie zmiany szybkości transmisji", 11 | "field_protocolD0DeviceAddress": "D0: Adres urządzenia", 12 | "field_protocolD0ModeOverwrite": "D0: nadpisanie trybu", 13 | "field_protocolD0SignOnMessage": "D0: polecenie SignOn-Message", 14 | "field_protocolD0WakeupCharacters": "D0: Liczba znaków WakeUp", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: Ignoruj ​​błąd sumy kontrolnej CRC", 16 | "field_protocolSmlInputEncoding": "Kodowanie danych wejściowych SML", 17 | "field_requestInterval": "Interwał żądania danych", 18 | "field_transport": "Transfer danych", 19 | "field_transportHttpRequestTimeout": "Limit czasu żądania HTTP", 20 | "field_transportHttpRequestUrl": "URL HTTP", 21 | "field_transportLocalFilePath": "Bezwzględna ścieżka do lokalnego pliku danych", 22 | "field_transportSerialBaudrate": "Szybkość transmisji urządzenia szeregowego", 23 | "field_transportSerialDataBits": "Bity danych urządzenia szeregowego", 24 | "field_transportSerialMessageTimeout": "Limit czasu odpowiedzi szeregowej", 25 | "field_transportSerialParity": "Parzystość urządzeń szeregowych", 26 | "field_transportSerialPort": "Nazwa urządzenia szeregowego", 27 | "field_transportSerialStopBits": "Serial Device StopBits", 28 | "field_transportTcpHost": "Host TCP", 29 | "field_transportTcpPort": "Port TCP", 30 | "heading_GeneralSettings": "Ustawienia główne", 31 | "heading_ProtocolSpecificSettings": "Ustawienia protokołu danych", 32 | "heading_TransportSpecificSettings": "Ustawienia przenoszenia danych", 33 | "info_anotherQueryDelay": "Potrzebne jest opóźnienie, ponieważ adapter wyśle ​​nowy komunikat, a urządzenie musi najpierw zostać zresetowane.
Wartość domyślna, jeśli jest pusta: 1000", 34 | "info_obisFallbackMedium": "(używany do rozpoznawania nazw, gdy w komunikacie z danymi nie zdefiniowano nośnika OBIS)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Puste, jeśli używana jest szybkość transmisji z urządzenia", 38 | "info_protocolD0DeviceAddress": "(Domyślnie puste)", 39 | "info_protocolD0ModeOverwrite": "(Puste, jeśli ma być używany tryb z urządzenia, w przeciwnym razie A, B, C, D lub E mogą nadpisywać", 40 | "info_protocolD0SignOnMessage": "(Wartość domyślna, jeśli jest pusta: ?
, aby odczytać wartości obowiązkowe)", 41 | "info_protocolD0WakeupCharacters": "(Znaki NULL)
Domyślnie, jeśli puste: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Definicja alternatywnego kodowania danych wejściowych przychodzących danych SML. Najprawdopodobniej dotyczy transportu TCP.", 44 | "info_requestInterval": "użyj 0 do ciągłego odczytu / żądania,
Domyślnie, jeśli jest pusty: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Domyślnie, jeśli jest puste: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Jeśli jest pusta, używane są wartości domyślne protokołu
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Jeśli jest pusta, używane są wartości domyślne protokołu
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Określa, jak długo należy czekać na prawidłową wiadomość przed ponownym uruchomieniem. ", 52 | "info_transportSerialParity": "Jeśli jest pusta, używane są wartości domyślne protokołu
(D0: parzysty, SML: brak)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Jeśli jest pusta, używane są wartości domyślne protokołu
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Ustawienia adaptera Smartmeter", 58 | "value_no_overwrite": "Użyj trybu zdefiniowanego przez urządzenie", 59 | "value_obisFallbackMedium_0": "0: Streszczenie", 60 | "value_obisFallbackMedium_1": "1: Energia elektryczna", 61 | "value_obisFallbackMedium_16": "16: Olej", 62 | "value_obisFallbackMedium_17": "17: Sprężone powietrze", 63 | "value_obisFallbackMedium_18": "18: Azot", 64 | "value_obisFallbackMedium_4": "4: Alokator kosztów ciepła", 65 | "value_obisFallbackMedium_5": "5: Chłodzenie", 66 | "value_obisFallbackMedium_6": "6: Ciepło", 67 | "value_obisFallbackMedium_7": "7: gaz", 68 | "value_obisFallbackMedium_8": "8: Zimna woda", 69 | "value_obisFallbackMedium_9": "9: Gorąca woda", 70 | "value_obisNameLanguage_de": "Niemiecki", 71 | "value_obisNameLanguage_en": "język angielski", 72 | "value_protocolSmlInputEncoding_binary": "Dane binarne (domyślne)", 73 | "value_protocol_D0Protocol": "D0 (WakeUp, SignOn, Data)", 74 | "value_protocol_JsonEfrProtocol": "Format JSON EFR SmartGridHub", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Protokół- / Transport-Default", 77 | "value_transport_HttpRequestTransport": "Odczytaj dane z adresu URL HTTP", 78 | "value_transport_LocalFileTransport": "Odczytaj dane z pliku lokalnego", 79 | "value_transport_SerialRequestResponseTransport": "Urządzenie szeregowe z Bi-reż. ", 80 | "value_transport_SerialResponseTransport": "Urządzenie szeregowe odczytuje tylko dane", 81 | "value_transport_TCPTransport": "TCP Urządzenie odczytuje tylko dane" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/pt/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Lista de portas não disponível", 3 | "Please wait till port list is loaded ...": "Aguarde até a lista de portas ser carregada ...", 4 | "Use custom Serial port path": "Defina um nome de porta serial personalizado", 5 | "field_anotherQueryDelay": "Atraso entre várias mensagens SignOn", 6 | "field_customSerialPort": "Caminho da porta serial personalizado", 7 | "field_obisFallbackMedium": "D0: Fallback OBIS-Médio", 8 | "field_obisNameLanguage": "Linguagem para nomes de Datapoint", 9 | "field_protocol": "Protocolo de Dados", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: Sobrescrever Baudrate Changeover", 11 | "field_protocolD0DeviceAddress": "D0: Endereço do dispositivo", 12 | "field_protocolD0ModeOverwrite": "D0: Substituição de modo", 13 | "field_protocolD0SignOnMessage": "D0: Comando SignOn-Message", 14 | "field_protocolD0WakeupCharacters": "D0: Número de caracteres WakeUp", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: Ignorar erro de checksum CRC", 16 | "field_protocolSmlInputEncoding": "Codificação de dados de entrada SML", 17 | "field_requestInterval": "Intervalo de solicitação de dados", 18 | "field_transport": "Transferência de dados", 19 | "field_transportHttpRequestTimeout": "Tempo limite de solicitação de HTTP", 20 | "field_transportHttpRequestUrl": "HTTP-URL", 21 | "field_transportLocalFilePath": "Caminho absoluto para o arquivo de dados local", 22 | "field_transportSerialBaudrate": "Taxa de transmissão serial do dispositivo", 23 | "field_transportSerialDataBits": "Serial Device DataBits", 24 | "field_transportSerialMessageTimeout": "Tempo Limite de Resposta em Série", 25 | "field_transportSerialParity": "Paridade de dispositivo serial", 26 | "field_transportSerialPort": "Serial Device Name", 27 | "field_transportSerialStopBits": "StopBits de dispositivo serial", 28 | "field_transportTcpHost": "Host TCP", 29 | "field_transportTcpPort": "Porta TCP", 30 | "heading_GeneralSettings": "Configurações Gerais", 31 | "heading_ProtocolSpecificSettings": "Configurações de protocolo de dados", 32 | "heading_TransportSpecificSettings": "Configurações de transferência de dados", 33 | "info_anotherQueryDelay": "É necessário um atraso porque o adaptador enviará uma nova mesa e o dispositivo precisa ser redefinido primeiro,
Padrão se vazio: 1000", 34 | "info_obisFallbackMedium": "(usado para resolução de nome quando nenhum meio OBIS é definido na mensagem de dados)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Vazio se a taxa de transmissão do dispositivo for usada", 38 | "info_protocolD0DeviceAddress": "(Vazio por padrão)", 39 | "info_protocolD0ModeOverwrite": "(Vazio se o modo do dispositivo deve ser usado, caso contrário, A, B, C, D ou E pode substituir", 40 | "info_protocolD0SignOnMessage": "(Padrão se vazio: ?
para ler os valores obrigatórios)", 41 | "info_protocolD0WakeupCharacters": "(NULL caracteres)
Padrão se vazio: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Definição de uma codificação de dados de entrada alternativa dos dados SML de entrada. Provavelmente relevante para transporte TCP.", 44 | "info_requestInterval": "use 0 para leitura / solicitação contínua,
Padrão se vazio: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "Padrão se vazio: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Se vazio, os padrões de protocolo são usados ​​
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Se vazio, os padrões de protocolo são usados ​​
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Define quanto tempo esperar por uma mensagem válida antes de reiniciar. ", 52 | "info_transportSerialParity": "Se vazio, os padrões de protocolo são usados ​​
(D0: par, SML: nenhum)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Se vazio, os padrões de protocolo são usados ​​
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Configurações do adaptador Smartmeter", 58 | "value_no_overwrite": "Modo de uso definido pelo dispositivo", 59 | "value_obisFallbackMedium_0": "0: Resumo", 60 | "value_obisFallbackMedium_1": "1: Eletricidade", 61 | "value_obisFallbackMedium_16": "16: Óleo", 62 | "value_obisFallbackMedium_17": "17: Ar comprimido", 63 | "value_obisFallbackMedium_18": "18: Nitrogênio", 64 | "value_obisFallbackMedium_4": "4: Alocador de custo de calor", 65 | "value_obisFallbackMedium_5": "5: Resfriamento", 66 | "value_obisFallbackMedium_6": "6: Calor", 67 | "value_obisFallbackMedium_7": "7: Gás", 68 | "value_obisFallbackMedium_8": "8: água fria", 69 | "value_obisFallbackMedium_9": "9: água quente", 70 | "value_obisNameLanguage_de": "alemão", 71 | "value_obisNameLanguage_en": "inglês", 72 | "value_protocolSmlInputEncoding_binary": "Dados binários (padrão)", 73 | "value_protocol_D0Protocol": "D0 (WakeUp, SignOn, Data)", 74 | "value_protocol_JsonEfrProtocol": "Formato JSON EFR SmartGridHub", 75 | "value_protocol_SmlProtocol": "Smart Message Language 1.0.3/1.0.4", 76 | "value_protocol_default": "Protocolo- / Transporte-Padrão", 77 | "value_transport_HttpRequestTransport": "Ler dados de um URL HTTP", 78 | "value_transport_LocalFileTransport": "Ler dados de um arquivo local", 79 | "value_transport_SerialRequestResponseTransport": "Dispositivo serial com Bi-dir. ", 80 | "value_transport_SerialResponseTransport": "Dados de leitura do dispositivo serial apenas", 81 | "value_transport_TCPTransport": "Dados de leitura do dispositivo TCP apenas" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/ru/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "Список портов недоступен", 3 | "Please wait till port list is loaded ...": "Пожалуйста, дождитесь загрузки списка портов ...", 4 | "Use custom Serial port path": "Установите пользовательское имя последовательного порта", 5 | "field_anotherQueryDelay": "Задержка между несколькими сообщениями входа в систему", 6 | "field_customSerialPort": "Пользовательский последовательный порт", 7 | "field_obisFallbackMedium": "D0: резервный OBIS-средний", 8 | "field_obisNameLanguage": "Язык для имен точек данных", 9 | "field_protocol": "Протокол передачи данных", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0: перезапись при переключении скорости", 11 | "field_protocolD0DeviceAddress": "D0: Адрес устройства", 12 | "field_protocolD0ModeOverwrite": "D0: режим перезаписи", 13 | "field_protocolD0SignOnMessage": "D0: команда SignOn-Message", 14 | "field_protocolD0WakeupCharacters": "D0: количество символов пробуждения", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML: игнорировать ошибку контрольной суммы CRC", 16 | "field_protocolSmlInputEncoding": "Кодирование входных данных SML", 17 | "field_requestInterval": "Интервал запроса данных", 18 | "field_transport": "Обмен данными", 19 | "field_transportHttpRequestTimeout": "Тайм-аут HTTP-запроса", 20 | "field_transportHttpRequestUrl": "HTTP-URL", 21 | "field_transportLocalFilePath": "Абсолютный путь к локальному файлу данных", 22 | "field_transportSerialBaudrate": "Скорость передачи последовательного устройства", 23 | "field_transportSerialDataBits": "Биты данных последовательных устройств", 24 | "field_transportSerialMessageTimeout": "Тайм-аут последовательного ответа", 25 | "field_transportSerialParity": "Четность последовательного устройства", 26 | "field_transportSerialPort": "Имя последовательного устройства", 27 | "field_transportSerialStopBits": "Стоп-биты последовательного устройства", 28 | "field_transportTcpHost": "TCP Host", 29 | "field_transportTcpPort": "TCP порт", 30 | "heading_GeneralSettings": "Общие настройки", 31 | "heading_ProtocolSpecificSettings": "Настройки протокола передачи данных", 32 | "heading_TransportSpecificSettings": "Настройки передачи данных", 33 | "info_anotherQueryDelay": "Задержка необходима, потому что адаптер отправит новое сообщение, и устройство необходимо сначала перезагрузить,
По умолчанию, если пусто: 1000", 34 | "info_obisFallbackMedium": "(используется для разрешения имен, когда в сообщении данных не определен носитель OBIS)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "Пусто, если используется скорость передачи от устройства", 38 | "info_protocolD0DeviceAddress": "(По умолчанию пусто)", 39 | "info_protocolD0ModeOverwrite": "(Пусто, если следует использовать режим с устройства, иначе разрешено перезапись A, B, C, D или E", 40 | "info_protocolD0SignOnMessage": "(По умолчанию, если пусто: ?
читать обязательные значения)", 41 | "info_protocolD0WakeupCharacters": "(NULL-символы)
По умолчанию, если пусто: 0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "Определение альтернативного кодирования входных данных для входящих данных SML. Скорее всего, актуально для транспорта TCP.", 44 | "info_requestInterval": "используйте 0 для непрерывного чтения / запроса,
По умолчанию, если пусто: 300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "По умолчанию, если пусто: 2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "Если пусто, используются настройки протокола по умолчанию
(D0: 300, SML: 9600)", 50 | "info_transportSerialDataBits": "Если пусто, используются настройки протокола по умолчанию
(D0: 7, SML: 8)", 51 | "info_transportSerialMessageTimeout": "Определяет, как долго ждать действительного сообщения перед перезапуском. ", 52 | "info_transportSerialParity": "Если пусто, используются настройки протокола по умолчанию
(D0: четный, SML: нет)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "Если пусто, используются настройки протокола по умолчанию
(D0: 1, SML: 1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Настройки адаптера Smartmeter", 58 | "value_no_overwrite": "Режим использования определяется устройством", 59 | "value_obisFallbackMedium_0": "0: Абстракция", 60 | "value_obisFallbackMedium_1": "1: Электричество", 61 | "value_obisFallbackMedium_16": "16: Масло", 62 | "value_obisFallbackMedium_17": "17: Сжатый воздух", 63 | "value_obisFallbackMedium_18": "18: Азот", 64 | "value_obisFallbackMedium_4": "4: Распределитель затрат на тепло", 65 | "value_obisFallbackMedium_5": "5: Охлаждение", 66 | "value_obisFallbackMedium_6": "6: Нагрев", 67 | "value_obisFallbackMedium_7": "7: Газ", 68 | "value_obisFallbackMedium_8": "8: Холодная вода", 69 | "value_obisFallbackMedium_9": "9: Горячая вода", 70 | "value_obisNameLanguage_de": "Немецкий", 71 | "value_obisNameLanguage_en": "английский", 72 | "value_protocolSmlInputEncoding_binary": "Двоичные данные (по умолчанию)", 73 | "value_protocol_D0Protocol": "D0 (пробуждение, вход, данные)", 74 | "value_protocol_JsonEfrProtocol": "JSON-формат EFR SmartGridHub", 75 | "value_protocol_SmlProtocol": "Язык умных сообщений 1.0.3/1.0.4", 76 | "value_protocol_default": "Протокол / Транспорт по умолчанию", 77 | "value_transport_HttpRequestTransport": "Чтение данных из URL-адреса HTTP", 78 | "value_transport_LocalFileTransport": "Чтение данных из локального файла", 79 | "value_transport_SerialRequestResponseTransport": "Последовательное устройство с Bi-dir. ", 80 | "value_transport_SerialResponseTransport": "Только данные чтения последовательного устройства", 81 | "value_transport_TCPTransport": "Только данные устройства TCP для чтения" 82 | } 83 | -------------------------------------------------------------------------------- /admin/i18n/zh-cn/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not available": "串口列表不可用", 3 | "Please wait till port list is loaded ...": "请等待端口列表加载完毕...", 4 | "Use custom Serial port path": "设置自定义串口名称", 5 | "field_anotherQueryDelay": "多个登录消息之间的延迟", 6 | "field_customSerialPort": "自定义串行端口路径", 7 | "field_obisFallbackMedium": "D0:后备OBIS-中", 8 | "field_obisNameLanguage": "数据点名称的语言", 9 | "field_protocol": "数据协议", 10 | "field_protocolD0BaudrateChangeoverOverwrite": "D0:波特率转换覆盖", 11 | "field_protocolD0DeviceAddress": "D0:设备地址", 12 | "field_protocolD0ModeOverwrite": "D0:模式覆盖", 13 | "field_protocolD0SignOnMessage": "D0:登录消息命令", 14 | "field_protocolD0WakeupCharacters": "D0:唤醒字符数", 15 | "field_protocolSmlIgnoreInvalidCRC": "SML:忽略CRC校验和错误", 16 | "field_protocolSmlInputEncoding": "SML输入数据编码", 17 | "field_requestInterval": "数据请求间隔", 18 | "field_transport": "数据传输", 19 | "field_transportHttpRequestTimeout": "HTTP请求超时", 20 | "field_transportHttpRequestUrl": "HTTP网址", 21 | "field_transportLocalFilePath": "本地数据文件的绝对路径", 22 | "field_transportSerialBaudrate": "串行设备波特率", 23 | "field_transportSerialDataBits": "串行设备数据位", 24 | "field_transportSerialMessageTimeout": "串行响应超时", 25 | "field_transportSerialParity": "串行设备奇偶校验", 26 | "field_transportSerialPort": "串行设备名称", 27 | "field_transportSerialStopBits": "串行设备停止位", 28 | "field_transportTcpHost": "TCP主机", 29 | "field_transportTcpPort": "TCP端口", 30 | "heading_GeneralSettings": "通用设置", 31 | "heading_ProtocolSpecificSettings": "数据协议设置", 32 | "heading_TransportSpecificSettings": "数据传输设置", 33 | "info_anotherQueryDelay": "需要延迟,因为适配器将发送新的消息,并且设备需要先重置,
默认为空:1000", 34 | "info_obisFallbackMedium": "(用于在数据消息中未定义OBIS介质时用于名称解析)", 35 | "info_obisNameLanguage": " ", 36 | "info_protocol": " ", 37 | "info_protocolD0BaudrateChangeoverOverwrite": "如果使用设备的波特率,则为空", 38 | "info_protocolD0DeviceAddress": "(默认为空)", 39 | "info_protocolD0ModeOverwrite": "(如果应使用“来自设备的模式”,则为空,否则允许覆盖A,B,C,D或E", 40 | "info_protocolD0SignOnMessage": "(如果为空,则为默认值:
以读取必需值)", 41 | "info_protocolD0WakeupCharacters": "(空字符)
如果为空,则默认为:0", 42 | "info_protocolSmlIgnoreInvalidCRC": " ", 43 | "info_protocolSmlInputEncoding": "定义输入SML数据的替代输入数据编码。最可能与TCP传输有关。", 44 | "info_requestInterval": "使用0进行连续读取/请求,
如果为空则默认:300", 45 | "info_transport": " ", 46 | "info_transportHttpRequestTimeout": "如果为空则默认值:2000", 47 | "info_transportHttpRequestUrl": " ", 48 | "info_transportLocalFilePath": " ", 49 | "info_transportSerialBaudrate": "如果为空,则使用协议默认值
(D0:300,SML:9600)", 50 | "info_transportSerialDataBits": "如果为空,则使用协议默认值
(D0:7,SML:8)", 51 | "info_transportSerialMessageTimeout": "定义重新启动之前等待有效消息的时间。", 52 | "info_transportSerialParity": "如果为空,则使用协议默认值
(D0:偶数,SML:无)", 53 | "info_transportSerialPort": " ", 54 | "info_transportSerialStopBits": "如果为空,则使用协议默认值
(D0:1,SML:1)", 55 | "info_transportTcpHost": " ", 56 | "info_transportTcpPort": " ", 57 | "page_title": "Smartmeter适配器设置", 58 | "value_no_overwrite": "设备定义的使用模式", 59 | "value_obisFallbackMedium_0": "0:摘要", 60 | "value_obisFallbackMedium_1": "1:电力", 61 | "value_obisFallbackMedium_16": "16:油", 62 | "value_obisFallbackMedium_17": "17:压缩空气", 63 | "value_obisFallbackMedium_18": "18:氮", 64 | "value_obisFallbackMedium_4": "4:热量成本分配器", 65 | "value_obisFallbackMedium_5": "5:冷却", 66 | "value_obisFallbackMedium_6": "6:热", 67 | "value_obisFallbackMedium_7": "7:煤气", 68 | "value_obisFallbackMedium_8": "8:冷水", 69 | "value_obisFallbackMedium_9": "9:热水", 70 | "value_obisNameLanguage_de": "德语", 71 | "value_obisNameLanguage_en": "英语", 72 | "value_protocolSmlInputEncoding_binary": "二进制数据(默认)", 73 | "value_protocol_D0Protocol": "D0(唤醒,登录,数据)", 74 | "value_protocol_JsonEfrProtocol": "JSON格式的EFR SmartGridHub", 75 | "value_protocol_SmlProtocol": "智能消息语言1.0.3", 76 | "value_protocol_default": "协议/传输默认", 77 | "value_transport_HttpRequestTransport": "从HTTP URL读取数据", 78 | "value_transport_LocalFileTransport": "从本地文件读取数据", 79 | "value_transport_SerialRequestResponseTransport": "具有双向目录的串行设备。", 80 | "value_transport_SerialResponseTransport": "串行设备仅读取数据", 81 | "value_transport_TCPTransport": "TCP设备仅读取数据" 82 | } 83 | -------------------------------------------------------------------------------- /admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 232 | 233 | 234 |
235 | 236 | 237 | 238 | 239 |

page_title

240 | 241 | 242 | 243 | 244 | 253 | 255 | 256 | 257 | 258 | 273 | 275 | 276 | 277 | 278 | 291 | 293 | 294 | 295 | 296 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 316 | 318 | 319 | 320 | 321 | 330 | 332 | 333 | 334 | 335 | 348 | 350 | 351 | 352 | 353 | 364 | 366 | 367 | 368 | 369 | 383 | 385 | 386 | 387 | 388 | 395 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 412 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 434 | 436 | 437 | 438 | 439 | 440 | 441 | 448 | 450 | 451 | 452 | 453 | 460 | 462 | 463 | 464 | 465 | 474 | 476 | 477 | 478 | 479 | 495 | 497 | 498 | 499 | 500 | 509 | 511 | 512 | 513 | 514 | 521 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 543 | 545 | 546 | 547 | 548 | 567 | 569 | 570 |

heading_GeneralSettings

field_requestInterval 245 |
246 | 247 | 248 | 249 | info_requestInterval 250 |
251 | s 252 |
254 |
field_transport 259 |
260 | 261 | 262 | 263 | 264 |
265 | 272 |
274 |
field_protocol 279 |
280 | 281 | 282 | 283 | 284 |
285 | 290 |
292 |
field_obisNameLanguage 297 | 298 | 302 | info_obisNameLanguage

heading_TransportSpecificSettings

field_transportSerialPortinfo_transportSerialPort
field_customSerialPort 314 | 315 | 317 |
field_transportSerialBaudrate 322 |
323 | 324 | 325 | 326 | info_transportSerialBaudrate 327 |
328 | baud 329 |
331 |
field_transportSerialDataBits 336 |
337 | 338 | info_transportSerialDataBits 339 |
340 | 347 |
349 |
field_transportSerialStopBits 354 |
355 | 356 | info_transportSerialStopBits 357 |
358 | 363 |
365 |
field_transportSerialParity 370 |
371 | 372 | info_transportSerialParity 373 |
374 | 382 |
384 |
field_transportSerialMessageTimeout 389 |
390 | 391 | info_transportSerialMessageTimeout 392 |
393 | s 394 |
396 |
field_transportHttpRequestUrlinfo_transportHttpRequestUrl
field_transportHttpRequestTimeout 406 |
407 | 408 | info_transportHttpRequestTimeout 409 |
410 | ms 411 |
413 |
field_transportLocalFilePathinfo_transportLocalFilePath
field_transportTcpHostinfo_transportTcpHost
field_transportTcpPort 428 |
429 | 430 | info_transportTcpPort 431 |
432 | 433 |
435 |

heading_ProtocolSpecificSettings

heading_ProtocolSpecificSettings

field_protocolD0WakeupCharacters 442 |
443 | 444 | info_protocolD0WakeupCharacters 445 |
446 | 447 |
449 |
field_protocolD0DeviceAddress 454 |
455 | 456 | info_protocolD0DeviceAddress 457 |
458 | 459 |
461 |
field_protocolD0SignOnMessage 466 |
467 | 468 | 469 | 470 | info_protocolD0SignOnMessage 471 |
472 | 473 |
475 |
field_protocolD0ModeOverwrite 480 |
481 | 482 | 483 | 484 | info_protocolD0ModeOverwrite 485 |
486 | 494 |
496 |
field_protocolD0BaudrateChangeoverOverwrite 501 |
502 | 503 | 504 | 505 | info_protocolD0BaudrateChangeoverOverwrite 506 |
507 | baud 508 |
510 |
field_anotherQueryDelay 515 |
516 | 517 | info_anotherQueryDelay 518 |
519 | ms 520 |
522 |
field_protocolSmlIgnoreInvalidCRCinfo_protocolSmlIgnoreInvalidCRC
field_protocolSmlInputEncoding 532 |
533 | 534 | info_protocolSmlInputEncoding 535 |
536 | 542 |
544 |
field_obisFallbackMedium 549 |
550 | 551 | info_obisFallbackMedium 552 |
553 | 566 |
568 |
571 |
572 | 573 | 574 | -------------------------------------------------------------------------------- /admin/questionmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apollon77/ioBroker.smartmeter/43936cca56a602ab648851c13cfa2808fd846d6b/admin/questionmark.png -------------------------------------------------------------------------------- /admin/smartmeter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apollon77/ioBroker.smartmeter/43936cca56a602ab648851c13cfa2808fd846d6b/admin/smartmeter.jpg -------------------------------------------------------------------------------- /admin/smartmeter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apollon77/ioBroker.smartmeter/43936cca56a602ab648851c13cfa2808fd846d6b/admin/smartmeter.png -------------------------------------------------------------------------------- /admin/tooltip.css: -------------------------------------------------------------------------------- 1 | /* Tooltip container */ 2 | .tooltip { 3 | position: relative; 4 | display: inline-block; 5 | /*border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ 6 | } 7 | 8 | /* Tooltip text */ 9 | .tooltip .tooltiptext { 10 | visibility: hidden; 11 | width: 250px; 12 | background-color: #777; 13 | color: #fff; 14 | text-align: center; 15 | padding: 5px 0; 16 | border-radius: 6px; 17 | 18 | /* Position the tooltip text */ 19 | position: absolute; 20 | z-index: 1; 21 | bottom: 125%; 22 | left: 50%; 23 | margin-left: -60px; 24 | 25 | /* Fade in tooltip */ 26 | opacity: 0; 27 | transition: opacity 1s; 28 | 29 | font-size: 10px; 30 | } 31 | 32 | /* Tooltip arrow */ 33 | .tooltip .tooltiptext::after { 34 | content: ""; 35 | position: absolute; 36 | top: 100%; 37 | left: 23.5%; 38 | margin-left: -5px; 39 | border-width: 5px; 40 | border-style: solid; 41 | border-color: #555 transparent transparent transparent; 42 | } 43 | 44 | /* Show the tooltip text when you mouse over the tooltip container */ 45 | .tooltip:hover .tooltiptext { 46 | visibility: visible; 47 | opacity: 1; 48 | } 49 | -------------------------------------------------------------------------------- /io-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "name": "smartmeter", 4 | "version": "3.4.0", 5 | "news": { 6 | "3.4.0": { 7 | "en": "IMPORTANT: This version requires at least Node.js 16+\nPrevent some crash cases", 8 | "de": "WICHTIG: Diese Version erfordert mindestens Node.js 16+\nVerhindern einiger Crash-Fälle", 9 | "ru": "ВАЖНО: Эта версия требует по крайней мере Node.js 16+\nПредотвратить некоторые случаи аварии", 10 | "pt": "IMPORTANTE: Esta versão requer pelo menos Node.js 16+\nEvite casos de acidente", 11 | "nl": "Deze versie vereist minstens Node\nVertaling:", 12 | "fr": "IMPORTANT: Cette version nécessite au moins Node.js 16+\nPrévenir des cas de crash", 13 | "it": "IMPORTANTE: Questa versione richiede almeno Node.js 16+\nPrevenire alcuni casi di crash", 14 | "es": "IMPORTANTE: Esta versión requiere por lo menos Node.js 16+\nEvitar algunos casos de choque", 15 | "pl": "IMPORTANT: Wersja ta wymaga co najmniej nr 16+\nPrzygotowania do wypadku", 16 | "uk": "ВАЖЛИВО: Ця версія вимагає принаймні Node.js 16+\nЗапобігти виникненню аварійних випадків", 17 | "zh-cn": "IMPORTANT:这本版本至少需要第16+。\n预防一些事故" 18 | }, 19 | "3.3.4": { 20 | "en": "Restore functionality of Port list in Admin", 21 | "de": "Stellen Sie die Funktionalität der Portliste in Admin wieder her", 22 | "ru": "Восстановить функциональность списка портов в Admin", 23 | "pt": "Restaure a funcionalidade da lista de portas no administrador", 24 | "nl": "Herstel functionaliteit van poortlijst in Admin", 25 | "fr": "Restaurer la fonctionnalité de la liste des ports dans Admin", 26 | "it": "Ripristina la funzionalità dell'elenco delle porte in Admin", 27 | "es": "Restaurar la funcionalidad de la lista de puertos en Admin", 28 | "pl": "Przywróć funkcjonalność listy portów w Admin", 29 | "zh-cn": "在管理员中恢复端口列表的功能" 30 | }, 31 | "3.3.3": { 32 | "en": "IMPORTANT: This version requires at least Node.js 12.17+ (excluding non LTS like 13.x)\nPrevent some crash cases", 33 | "de": "WICHTIG: Diese Version erfordert mindestens Node.js 12.17+ (außer Nicht-LTS wie 13.x)\nVerhindern Sie einige Absturzfälle", 34 | "ru": "ВАЖНО: для этой версии требуется как минимум Node.js 12.17+ (за исключением не LTS, например 13.x).\nПредотвратить некоторые случаи сбоев", 35 | "pt": "IMPORTANTE: esta versão requer pelo menos Node.js 12.17+ (excluindo não LTS como 13.x)\nPrevenir alguns casos de travamento", 36 | "nl": "BELANGRIJK: deze versie vereist minimaal Node.js 12.17+ (exclusief niet-LTS zoals 13.x)\nVoorkom enkele crashgevallen", 37 | "fr": "IMPORTANT : Cette version nécessite au moins Node.js 12.17+ (hors non LTS comme 13.x)\nPrévenir certains cas de crash", 38 | "it": "IMPORTANTE: questa versione richiede almeno Node.js 12.17+ (escluso non LTS come 13.x)\nPrevenire alcuni casi di crash", 39 | "es": "IMPORTANTE: esta versión requiere al menos Node.js 12.17+ (excluyendo no LTS como 13.x)\nPrevenir algunos casos de accidentes", 40 | "pl": "WAŻNE: Ta wersja wymaga co najmniej Node.js 12.17+ (z wyłączeniem innych niż LTS, takich jak 13.x)\nZapobiegaj niektórym przypadkom awarii", 41 | "zh-cn": "重要提示:此版本至少需要 Node.js 12.17+(不包括像 13.x 这样的非 LTS)\n防止一些崩溃案例" 42 | }, 43 | "3.2.1": { 44 | "en": "Optimize for js-controller 3.3", 45 | "de": "Optimieren für js-controller 3.3", 46 | "ru": "Оптимизировать под js-controller 3.3", 47 | "pt": "Otimize para js-controller 3.3", 48 | "nl": "Optimaliseren voor js-controller 3.3", 49 | "fr": "Optimiser pour js-controller 3.3", 50 | "it": "Ottimizza per js-controller 3.3", 51 | "es": "Optimizar para js-controller 3.3", 52 | "pl": "Optymalizacja pod kątem kontrolera js 3.3", 53 | "zh-cn": "针对js-controller 3.3进行优化" 54 | }, 55 | "3.2.0": { 56 | "en": "Add new protocolSmlInputEncoding option for SML protocol. With this also ascii or base64 based encodings (e.g. with TCP transports) are possible.", 57 | "de": "Fügen Sie die neue Option protocolSmlInputEncoding für das SML-Protokoll hinzu. Damit sind auch auf ASCII oder Base64 basierende Codierungen (z. B. mit TCP-Transporten) möglich.", 58 | "ru": "Добавить новую опцию protocolSmlInputEncoding для протокола SML. С этим также возможны кодировки на основе ascii или base64 (например, с транспортом TCP).", 59 | "pt": "Adicione a nova opção protocolSmlInputEncoding para o protocolo SML. Com isso, também são possíveis codificações baseadas em ascii ou base64 (por exemplo, com transportes TCP).", 60 | "nl": "Nieuwe protocolSmlInputEncoding-optie voor SML-protocol toevoegen. Hiermee zijn ook ascii- of base64-gebaseerde coderingen (bijvoorbeeld met TCP-transporten) mogelijk.", 61 | "fr": "Ajoutez une nouvelle option protocolSmlInputEncoding pour le protocole SML. Avec cela, des encodages basés sur ascii ou base64 (par exemple avec les transports TCP) sont également possibles.", 62 | "it": "Aggiungere una nuova opzione protocolSmlInputEncoding per il protocollo SML. Con questo sono possibili anche codifiche basate su ascii o base64 (ad es. Con trasporti TCP).", 63 | "es": "Agregue la nueva opción protocolSmlInputEncoding para el protocolo SML. Con esto también son posibles codificaciones basadas en ascii o base64 (por ejemplo, con transportes TCP).", 64 | "pl": "Dodaj nową opcję protocolSmlInputEncoding dla protokołu SML. Dzięki temu możliwe jest również kodowanie oparte na ascii lub base64 (np. Z transportami TCP).", 65 | "zh-cn": "为SML协议添加新的protocolSmlInputEncoding选项。通过这种方式,基于ascii或base64的编码(例如TCP传输)也是可能的。" 66 | }, 67 | "3.1.9": { 68 | "en": "optimize stop handling (Sentry IOBROKER-SMARTMETER-10)", 69 | "de": "Optimieren Sie das Stop-Handling (Sentry IOBROKER-SMARTMETER-10).", 70 | "ru": "оптимизировать обработку останова (Sentry IOBROKER-SMARTMETER-10)", 71 | "pt": "otimizar o manuseio de parada (Sentry IOBROKER-SMARTMETER-10)", 72 | "nl": "stopafhandeling optimaliseren (Sentry IOBROKER-SMARTMETER-10)", 73 | "fr": "optimiser la gestion des arrêts (Sentry IOBROKER-SMARTMETER-10)", 74 | "it": "ottimizzare la gestione delle fermate (Sentry IOBROKER-SMARTMETER-10)", 75 | "es": "optimizar el manejo de la parada (Sentry IOBROKER-SMARTMETER-10)", 76 | "pl": "optymalizacja obsługi zatrzymań (Sentry IOBROKER-SMARTMETER-10)", 77 | "zh-cn": "优化停止处理(Sentry IOBROKER-SMARTMETER-10)" 78 | }, 79 | "3.1.8": { 80 | "en": "prevent last warnings with js-controller 3.2", 81 | "de": "Verhindern Sie letzte Warnungen mit js-controller 3.2", 82 | "ru": "предотвратить последние предупреждения с помощью js-controller 3.2", 83 | "pt": "evita os últimos avisos com js-controller 3.2", 84 | "nl": "voorkom laatste waarschuwingen met js-controller 3.2", 85 | "fr": "éviter les derniers avertissements avec js-controller 3.2", 86 | "it": "prevenire gli ultimi avvisi con js-controller 3.2", 87 | "es": "evitar las últimas advertencias con js-controller 3.2", 88 | "pl": "zapobiec ostatnim ostrzeżeniom za pomocą js-controller 3.2", 89 | "zh-cn": "防止使用js-controller 3.2发出最后警告" 90 | } 91 | }, 92 | "authors": [ 93 | "Apollon77 " 94 | ], 95 | "title": "Smartmeter devices support", 96 | "titleLang": { 97 | "en": "Smartmeter devices support", 98 | "de": "Unterstützung für Smartmeter-Geräte", 99 | "ru": "Поддержка устройств Smartmeter", 100 | "pt": "Suporte para dispositivos Smartmeter", 101 | "nl": "Ondersteuning voor smartmeter-apparaten", 102 | "fr": "Prise en charge des appareils Smartmeter", 103 | "it": "Supporto dispositivi Smartmeter", 104 | "es": "Soporte de dispositivos Smartmeter", 105 | "pl": "Obsługa urządzeń Smartmeter", 106 | "zh-cn": "Smartmeter设备支持" 107 | }, 108 | "desc": { 109 | "en": "Read data from Smartmeter Devices using various protocols like SML, D0 and others", 110 | "de": "Auslesen von Smartmeter-Geräten mit verschiedenen Protokollen wie SML, D0 und anderen", 111 | "ru": "Чтение данных с устройств Smartmeter с использованием различных протоколов, таких как SML, D0 и др.", 112 | "pt": "Leia dados de dispositivos Smartmeter usando vários protocolos como SML, D0 e outros", 113 | "nl": "Lees gegevens van Smartmeter-apparaten met behulp van verschillende protocollen zoals SML, D0 en anderen", 114 | "fr": "Lire des données à partir de périphériques Smartmeter en utilisant divers protocoles tels que SML, D0 et autres", 115 | "it": "Leggi i dati dai dispositivi Smartmeter utilizzando vari protocolli come SML, D0 e altri", 116 | "es": "Lea datos de dispositivos Smartmeter usando varios protocolos como SML, D0 y otros", 117 | "pl": "Odczytaj dane z urządzeń Smartmeter przy użyciu różnych protokołów, takich jak SML, D0 i inne", 118 | "zh-cn": "使用SML,D0等各种协议从Smartmeter设备读取数据" 119 | }, 120 | "platform": "Javascript/Node.js", 121 | "mode": "daemon", 122 | "messagebox": true, 123 | "subscribe": "messagebox", 124 | "stopBeforeUpdate": true, 125 | "icon": "smartmeter.png", 126 | "extIcon": "https://raw.githubusercontent.com/Apollon77/ioBroker.smartmeter/master/admin/smartmeter.png", 127 | "readme": "https://github.com/Apollon77/ioBroker.smartmeter/blob/master/README.md", 128 | "license": "MIT", 129 | "npmLibs": [], 130 | "type": "energy", 131 | "keywords": [ 132 | "iobroker", 133 | "smartmeter", 134 | "SML", 135 | "D0", 136 | "eHz", 137 | "OBIS" 138 | ], 139 | "loglevel": "info", 140 | "enabled": true, 141 | "compact": true, 142 | "connectionType": "local", 143 | "dataSource": "push", 144 | "tier": 2, 145 | "plugins": { 146 | "sentry": { 147 | "dsn": "https://2d8447c1f6514162b912af8d6892797e@sentry.iobroker.net/3", 148 | "pathWhitelist": [ 149 | "@apollon", 150 | "smartmeter-obis", 151 | "open-sml" 152 | ], 153 | "errorBlacklist": [ 154 | "SyntaxError" 155 | ] 156 | } 157 | }, 158 | "dependencies": [ 159 | { 160 | "js-controller": ">=2.0.0" 161 | } 162 | ] 163 | }, 164 | "native": { 165 | "protocol": "SmlProtocol", 166 | "transport": "SerialResponseTransport", 167 | "requestInterval": 300, 168 | "transportSerialPort": "", 169 | "transportSerialBaudrate": "", 170 | "transportSerialDataBits": "", 171 | "transportSerialStopBits": "", 172 | "transportSerialParity": "", 173 | "transportSerialMessageTimeout": "", 174 | "transportHttpRequestUrl": "", 175 | "transportHttpRequestTimeout": 2000, 176 | "transportLocalFilePath": "", 177 | "transportTcpHost": "", 178 | "transportTcpPort": "", 179 | "protocolD0WakeupCharacters": 0, 180 | "protocolD0DeviceAddress": "", 181 | "protocolD0SignOnMessage": "", 182 | "protocolD0ModeOverwrite": "", 183 | "protocolD0BaudrateChangeoverOverwrite": "", 184 | "protocolSmlIgnoreInvalidCRC": false, 185 | "protocolSmlInputEncoding": "", 186 | "obisFallbackMedium": "", 187 | "obisNameLanguage": "de", 188 | "anotherQueryDelay": 1000 189 | }, 190 | "objects": [], 191 | "instanceObjects": [ 192 | { 193 | "_id": "info", 194 | "type": "channel", 195 | "common": { 196 | "name": "Information" 197 | }, 198 | "native": {} 199 | }, 200 | { 201 | "_id": "info.connection", 202 | "type": "state", 203 | "common": { 204 | "role": "indicator.connected", 205 | "name": "If connected to Smartmeter device", 206 | "type": "boolean", 207 | "read": true, 208 | "write": false, 209 | "def": false 210 | }, 211 | "native": {} 212 | } 213 | ] 214 | } 215 | -------------------------------------------------------------------------------- /lib/tools.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios').default; 2 | 3 | /** 4 | * Tests whether the given variable is a real object and not an Array 5 | * @param {any} it The variable to test 6 | * @returns {it is Record} 7 | */ 8 | function isObject(it) { 9 | // This is necessary because: 10 | // typeof null === 'object' 11 | // typeof [] === 'object' 12 | // [] instanceof Object === true 13 | return Object.prototype.toString.call(it) === '[object Object]'; 14 | } 15 | 16 | /** 17 | * Tests whether the given variable is really an Array 18 | * @param {any} it The variable to test 19 | * @returns {it is any[]} 20 | */ 21 | function isArray(it) { 22 | if (typeof Array.isArray === 'function') return Array.isArray(it); 23 | return Object.prototype.toString.call(it) === '[object Array]'; 24 | } 25 | 26 | /** 27 | * Translates text to the target language. Automatically chooses the right translation API. 28 | * @param {string} text The text to translate 29 | * @param {string} targetLang The target languate 30 | * @param {string} [yandexApiKey] The yandex API key. You can create one for free at https://translate.yandex.com/developers 31 | * @returns {Promise} 32 | */ 33 | async function translateText(text, targetLang, yandexApiKey) { 34 | if (targetLang === 'en') { 35 | return text; 36 | } else if (!text) { 37 | return ''; 38 | } 39 | if (yandexApiKey) { 40 | return translateYandex(text, targetLang, yandexApiKey); 41 | } else { 42 | return translateGoogle(text, targetLang); 43 | } 44 | } 45 | 46 | /** 47 | * Translates text with Yandex API 48 | * @param {string} text The text to translate 49 | * @param {string} targetLang The target languate 50 | * @param {string} apiKey The yandex API key. You can create one for free at https://translate.yandex.com/developers 51 | * @returns {Promise} 52 | */ 53 | async function translateYandex(text, targetLang, apiKey) { 54 | if (targetLang === 'zh-cn') { 55 | targetLang = 'zh'; 56 | } 57 | try { 58 | const url = `https://translate.yandex.net/api/v1.5/tr.json/translate?key=${apiKey}&text=${encodeURIComponent(text)}&lang=en-${targetLang}`; 59 | const response = await axios({url, timeout: 15000}); 60 | if (response.data && response.data.text && isArray(response.data.text)) { 61 | return response.data.text[0]; 62 | } 63 | throw new Error('Invalid response for translate request'); 64 | } catch (e) { 65 | throw new Error(`Could not translate to "${targetLang}": ${e}`); 66 | } 67 | } 68 | 69 | /** 70 | * Translates text with Google API 71 | * @param {string} text The text to translate 72 | * @param {string} targetLang The target languate 73 | * @returns {Promise} 74 | */ 75 | async function translateGoogle(text, targetLang) { 76 | if (!text.trim().length) { 77 | return text; 78 | } 79 | 80 | try { 81 | const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`; 82 | const response = await axios({url, timeout: 15000}); 83 | if (isArray(response.data)) { 84 | // we got a valid response 85 | return response.data[0][0][0]; 86 | } 87 | throw new Error('Invalid response for translate request'); 88 | } catch (e) { 89 | if (e.response && e.response.status === 429) { 90 | throw new Error( 91 | `Could not translate to "${targetLang}": Rate-limited by Google Translate` 92 | ); 93 | } else { 94 | throw new Error(`Could not translate to "${targetLang}": ${e}`); 95 | } 96 | } 97 | } 98 | 99 | module.exports = { 100 | isArray, 101 | isObject, 102 | translateText 103 | }; 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iobroker.smartmeter", 3 | "version": "3.4.0", 4 | "description": "Smartmeter Adapter", 5 | "author": "Ingo Fischer ", 6 | "contributors": [], 7 | "homepage": "", 8 | "license": "MIT", 9 | "keywords": [ 10 | "iobroker", 11 | "smartmeter", 12 | "SML", 13 | "D0", 14 | "eHz", 15 | "OBIS" 16 | ], 17 | "engines": { 18 | "node": ">=16.0.0" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/Apollon77/ioBroker.smartmeter" 23 | }, 24 | "dependencies": { 25 | "smartmeter-obis": "^4.0.0", 26 | "serialport": "^12.0.0", 27 | "@iobroker/adapter-core": "^3.0.4", 28 | "@sentry/node": "^7.81.1", 29 | "@sentry/integrations": "^7.81.1", 30 | "source-map-support": "^0.5.21" 31 | }, 32 | "devDependencies": { 33 | "@alcalzone/release-script": "^3.6.0", 34 | "@alcalzone/release-script-plugin-iobroker": "^3.6.0", 35 | "@alcalzone/release-script-plugin-license": "^3.5.9", 36 | "@iobroker/adapter-dev": "^1.2.0", 37 | "mocha": "^10.2.0", 38 | "chai": "^4.3.10", 39 | "nyc": "^15.1.0" 40 | }, 41 | "bugs": { 42 | "url": "https://github.com/Apollon77/ioBroker.smartmeter/issues" 43 | }, 44 | "main": "smartmeter.js", 45 | "scripts": { 46 | "test": "nyc --reporter=lcov mocha --exit", 47 | "release": "release-script", 48 | "translate": "translate-adapter" 49 | }, 50 | "nyc": { 51 | "exclude": [ 52 | "!**/node_modules/" 53 | ], 54 | "include": [ 55 | "**/tmp/node_modules/iobroker.smartmeter/*.js" 56 | ], 57 | "produce-source-map": true 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /smartmeter.js: -------------------------------------------------------------------------------- 1 | /* jshint -W097 */ 2 | // jshint strict:true 3 | /*jslint node: true */ 4 | /*jslint esversion: 6 */ 5 | 'use strict'; 6 | /** 7 | * 8 | * Smartmeter adapter 9 | * 10 | * Adapter reading smartmeter data and pushing the values into ioBroker 11 | * 12 | */ 13 | 14 | const fs = require('fs'); 15 | const utils = require('@iobroker/adapter-core'); // Get common adapter utils 16 | const SmartmeterObis = require('smartmeter-obis'); 17 | let smTransport; 18 | const { SerialPort } = require('serialport'); 19 | 20 | const smValues = {}; 21 | 22 | let stopInProgress = false; 23 | let connected = null; 24 | let adapter; 25 | 26 | let Sentry; 27 | let SentryIntegrations; 28 | function initSentry(callback) { 29 | if (!adapter.ioPack.common || !adapter.ioPack.common.plugins || !adapter.ioPack.common.plugins.sentry) { 30 | return callback && callback(); 31 | } 32 | const sentryConfig = adapter.ioPack.common.plugins.sentry; 33 | if (!sentryConfig.dsn) { 34 | adapter.log.warn('Invalid Sentry definition, no dsn provided. Disable error reporting'); 35 | return callback && callback(); 36 | } 37 | // Require needed tooling 38 | Sentry = require('@sentry/node'); 39 | SentryIntegrations = require('@sentry/integrations'); 40 | // By installing source map support, we get the original source 41 | // locations in error messages 42 | require('source-map-support').install(); 43 | 44 | let sentryPathWhitelist = []; 45 | if (sentryConfig.pathWhitelist && Array.isArray(sentryConfig.pathWhitelist)) { 46 | sentryPathWhitelist = sentryConfig.pathWhitelist; 47 | } 48 | if (adapter.pack.name && !sentryPathWhitelist.includes(adapter.pack.name)) { 49 | sentryPathWhitelist.push(adapter.pack.name); 50 | } 51 | let sentryErrorBlacklist = []; 52 | if (sentryConfig.errorBlacklist && Array.isArray(sentryConfig.errorBlacklist)) { 53 | sentryErrorBlacklist = sentryConfig.errorBlacklist; 54 | } 55 | if (!sentryErrorBlacklist.includes('SyntaxError')) { 56 | sentryErrorBlacklist.push('SyntaxError'); 57 | } 58 | 59 | Sentry.init({ 60 | release: adapter.pack.name + '@' + adapter.pack.version, 61 | dsn: sentryConfig.dsn, 62 | integrations: [ 63 | new SentryIntegrations.Dedupe() 64 | ] 65 | }); 66 | Sentry.configureScope(scope => { 67 | scope.setTag('version', adapter.common.installedVersion || adapter.common.version); 68 | if (adapter.common.installedFrom) { 69 | scope.setTag('installedFrom', adapter.common.installedFrom); 70 | } 71 | else { 72 | scope.setTag('installedFrom', adapter.common.installedVersion || adapter.common.version); 73 | } 74 | scope.addEventProcessor(function(event, hint) { 75 | // Try to filter out some events 76 | if (event.exception && event.exception.values && event.exception.values[0]) { 77 | const eventData = event.exception.values[0]; 78 | // if error type is one from blacklist we ignore this error 79 | if (eventData.type && sentryErrorBlacklist.includes(eventData.type)) { 80 | return null; 81 | } 82 | if (eventData.stacktrace && eventData.stacktrace.frames && Array.isArray(eventData.stacktrace.frames) && eventData.stacktrace.frames.length) { 83 | // if last exception frame is from an nodejs internal method we ignore this error 84 | if (eventData.stacktrace.frames[eventData.stacktrace.frames.length - 1].filename && (eventData.stacktrace.frames[eventData.stacktrace.frames.length - 1].filename.startsWith('internal/') || eventData.stacktrace.frames[eventData.stacktrace.frames.length - 1].filename.startsWith('Module.'))) { 85 | return null; 86 | } 87 | // Check if any entry is whitelisted from pathWhitelist 88 | const whitelisted = eventData.stacktrace.frames.find(frame => { 89 | if (frame.function && frame.function.startsWith('Module.')) { 90 | return false; 91 | } 92 | if (frame.filename && frame.filename.startsWith('internal/')) { 93 | return false; 94 | } 95 | if (frame.filename && !sentryPathWhitelist.find(path => path && path.length && frame.filename.includes(path))) { 96 | return false; 97 | } 98 | return true; 99 | }); 100 | if (!whitelisted) { 101 | return null; 102 | } 103 | } 104 | } 105 | 106 | return event; 107 | }); 108 | 109 | adapter.getForeignObject('system.config', (err, obj) => { 110 | if (obj && obj.common && obj.common.diag) { 111 | adapter.getForeignObject('system.meta.uuid', (err, obj) => { 112 | // create uuid 113 | if (!err && obj) { 114 | Sentry.configureScope(scope => { 115 | scope.setUser({ 116 | id: obj.native.uuid 117 | }); 118 | }); 119 | } 120 | callback && callback(); 121 | }); 122 | } 123 | else { 124 | callback && callback(); 125 | } 126 | }); 127 | }); 128 | } 129 | 130 | function stopIt(logMessage) { 131 | setConnected(false); 132 | adapter.log.error(logMessage); 133 | adapter.extendForeignObject('system.adapter.' + adapter.namespace, { 134 | common: { 135 | enabled: false 136 | } 137 | }); 138 | adapter.stop(); 139 | } 140 | 141 | function setConnected(isConnected) { 142 | if (connected !== isConnected) { 143 | connected = isConnected; 144 | adapter && adapter.setState('info.connection', connected, true, (err) => { 145 | // analyse if the state could be set (because of permissions) 146 | if (err && adapter && adapter.log) adapter.log.error('Can not update connected state: ' + err); 147 | else if (adapter && adapter.log) adapter.log.debug('connected set to ' + connected); 148 | }); 149 | } 150 | } 151 | 152 | function startAdapter(options) { 153 | options = options || {}; 154 | Object.assign(options, { 155 | name: 'smartmeter' 156 | }); 157 | adapter = new utils.Adapter(options); 158 | 159 | adapter.on('ready', () => { 160 | setConnected(false); 161 | 162 | if (adapter.supportsFeature && adapter.supportsFeature('PLUGINS')) { 163 | main(); 164 | } 165 | else { 166 | initSentry(main); 167 | } 168 | }); 169 | 170 | adapter.on('message', msg => { 171 | processMessage(msg); 172 | }); 173 | 174 | /* 175 | adapter.on('stateChange', (id, state) => { 176 | adapter.log.debug('stateChange ' + id + ' ' + JSON.stringify(state)); 177 | }); 178 | */ 179 | 180 | adapter.on('unload', callback => { 181 | stopInProgress = true; 182 | setConnected(false); 183 | if (smTransport) { 184 | smTransport.stop(callback); 185 | } else { 186 | callback(); 187 | } 188 | }); 189 | 190 | return adapter; 191 | } 192 | 193 | process.on('SIGINT', () => { 194 | setConnected(false); 195 | if (smTransport) smTransport.stop(); 196 | }); 197 | 198 | process.on('uncaughtException', err => { 199 | setConnected(false); 200 | if (adapter && adapter.log) { 201 | adapter.log.warn('Exception: ' + err); 202 | } 203 | if (smTransport) smTransport.stop(); 204 | }); 205 | 206 | function main() { 207 | const smOptions = {}; 208 | if (adapter.common.loglevel === 'debug' || adapter.common.loglevel === 'silly') { 209 | smOptions.debug = 2; 210 | smOptions.logger = adapter.log.debug; 211 | } 212 | else if (adapter.common.loglevel === 'info') { 213 | smOptions.debug = 1; 214 | smOptions.logger = adapter.log.info; 215 | } 216 | else { 217 | smOptions.debug = 0; 218 | smOptions.logger = adapter.log.warn; 219 | } 220 | if (!adapter.config.protocol) { 221 | adapter.log.error('Smartmeter Protocol is undefined, check your configuration!'); 222 | return; 223 | } 224 | smOptions.protocol = adapter.config.protocol; 225 | if (!adapter.config.transport) { 226 | adapter.log.error('Smartmeter Transfer is undefined, check your configuration!'); 227 | return; 228 | } 229 | smOptions.transport = adapter.config.transport; 230 | smOptions.requestInterval = adapter.config.requestInterval = adapter.config.requestInterval || 300; 231 | if (adapter.config.anotherQueryDelay) smOptions.anotherQueryDelay = adapter.config.anotherQueryDelay; 232 | if (adapter.config.transport.indexOf('Serial') === 0) { // we have a Serial connection 233 | if (!adapter.config.transportSerialPort) { 234 | adapter.log.error('Serial port device is undefined, check your configuration!'); 235 | return; 236 | } 237 | smOptions.transportSerialPort = adapter.config.transportSerialPort; 238 | if (adapter.config.transportSerialBaudrate !== null && adapter.config.transportSerialBaudrate !== undefined) { 239 | adapter.config.transportSerialBaudrate = parseInt(adapter.config.transportSerialBaudrate, 10); 240 | 241 | if (adapter.config.transportSerialBaudrate < 300) { 242 | adapter.log.error('Serial port baudrate invalid, check your configuration!'); 243 | return; 244 | } 245 | smOptions.transportSerialBaudrate = adapter.config.transportSerialBaudrate; 246 | } 247 | if (adapter.config.transportSerialDataBits !== null && adapter.config.transportSerialDataBits !== undefined && adapter.config.transportSerialDataBits !== "") { 248 | adapter.config.transportSerialDataBits = parseInt(adapter.config.transportSerialDataBits, 10); 249 | 250 | if ((adapter.config.transportSerialDataBits < 5) || (adapter.config.transportSerialDataBits > 8)) { 251 | adapter.log.error('Serial port data bits ' + adapter.config.transportSerialDataBits + ' invalid, check your configuration!'); 252 | return; 253 | } 254 | smOptions.transportSerialDataBits = adapter.config.transportSerialDataBits; 255 | } 256 | if (adapter.config.transportSerialStopBits !== null && adapter.config.transportSerialStopBits !== undefined && adapter.config.transportSerialStopBits !== "") { 257 | adapter.config.transportSerialStopBits = parseInt(adapter.config.transportSerialStopBits, 10); 258 | 259 | if ((adapter.config.transportSerialStopBits !== 1) && (adapter.config.transportSerialStopBits !== 2)) { 260 | adapter.log.error('Serial port stopbits ' + adapter.config.transportSerialStopBits + ' invalid, check your configuration!'); 261 | return; 262 | } 263 | smOptions.transportSerialStopBits = adapter.config.transportSerialStopBits; 264 | } 265 | if (adapter.config.transportSerialParity !== null && adapter.config.transportSerialParity !== undefined && adapter.config.transportSerialParity !== "") { 266 | if ((adapter.config.transportSerialParity !== "none") && (adapter.config.transportSerialParity !== "even") && 267 | (adapter.config.transportSerialParity !== "mark") && (adapter.config.transportSerialParity !== "odd") && 268 | (adapter.config.transportSerialParity !== "space")) { 269 | 270 | adapter.log.error('Serial port parity ' + adapter.config.transportSerialParity + ' invalid, check your configuration!'); 271 | return; 272 | } 273 | smOptions.transportSerialParity = adapter.config.transportSerialParity; 274 | } 275 | if (adapter.config.transportSerialMessageTimeout !== null && adapter.config.transportSerialMessageTimeout !== undefined) { 276 | adapter.config.transportSerialMessageTimeout = parseInt(adapter.config.transportSerialMessageTimeout, 10)*1000; 277 | if (adapter.config.transportSerialMessageTimeout < 1000) { 278 | adapter.log.error('HTTP Request timeout ' + adapter.config.transportSerialMessageTimeout + ' invalid, check your configuration!'); 279 | return; 280 | } 281 | smOptions.transportSerialMessageTimeout = adapter.config.transportSerialMessageTimeout; 282 | } 283 | 284 | //adapter.config.transportSerialMaxBufferSize 285 | } 286 | else if (adapter.config.transport === 'HttpRequestTransport') { // we have a Serial connection 287 | if (!adapter.config.transportHttpRequestUrl) { 288 | adapter.log.error('HTTP Request URL is undefined, check your configuration!'); 289 | return; 290 | } 291 | smOptions.transportHttpRequestUrl = adapter.config.transportHttpRequestUrl; 292 | if (adapter.config.transportHttpRequestTimeout !== null && adapter.config.transportHttpRequestTimeout !== undefined) { 293 | adapter.config.transportHttpRequestTimeout = parseInt(adapter.config.transportHttpRequestTimeout, 10); 294 | if (adapter.config.transportHttpRequestTimeout < 500) { 295 | adapter.log.error('HTTP Request timeout ' + adapter.config.transportHttpRequestTimeout + ' invalid, check your configuration!'); 296 | return; 297 | } 298 | smOptions.transportHttpRequestTimeout = adapter.config.transportHttpRequestTimeout; 299 | } 300 | } 301 | else if (adapter.config.transport === 'LocalFileTransport') { // we have a LocalFile connection 302 | if (!adapter.config.transportLocalFilePath) { 303 | adapter.log.error('HTTP Request URL is undefined, check your configuration!'); 304 | return; 305 | } 306 | smOptions.transportLocalFilePath = adapter.config.transportLocalFilePath; 307 | } 308 | else if (adapter.config.transport === 'TCPTransport') { // we have a TCP connection 309 | if (!adapter.config.transportTcpHost) { 310 | adapter.log.error('TCP Host is undefined, check your configuration!'); 311 | return; 312 | } 313 | if (!adapter.config.transportTcpPort) { 314 | adapter.log.error('TCP Port is undefined, check your configuration!'); 315 | return; 316 | } 317 | smOptions.transportTcpHost = adapter.config.transportTcpHost; 318 | smOptions.transportTcpPort = adapter.config.transportTcpPort; 319 | } 320 | 321 | if (adapter.config.protocol === 'D0Protocol') { // we have a Serial connection 322 | if (adapter.config.protocolD0WakeupCharacters !== null && adapter.config.protocolD0WakeupCharacters !== undefined) { 323 | adapter.config.protocolD0WakeupCharacters = parseInt(adapter.config.protocolD0WakeupCharacters, 10); 324 | if (adapter.config.protocolD0WakeupCharacters < 0) { 325 | adapter.log.error('D0 Number of Wakeup NULL characters ' + adapter.config.protocolD0WakeupCharacters + ' invalid, check your configuration!'); 326 | return; 327 | } 328 | smOptions.protocolD0WakeupCharacters = adapter.config.protocolD0WakeupCharacters; 329 | } 330 | if (adapter.config.protocolD0DeviceAddress) smOptions.protocolD0DeviceAddress = adapter.config.protocolD0DeviceAddress; 331 | if (adapter.config.protocolD0SignOnMessage) smOptions.protocolD0SignOnMessage = adapter.config.protocolD0SignOnMessage; 332 | if (adapter.config.protocolD0SignOnMessage) smOptions.protocolD0SignOnMessage = adapter.config.protocolD0SignOnMessage; 333 | if (adapter.config.protocolD0BaudrateChangeoverOverwrite !== null && adapter.config.protocolD0BaudrateChangeoverOverwrite !== undefined && adapter.config.protocolD0BaudrateChangeoverOverwrite !== "") { 334 | adapter.config.protocolD0BaudrateChangeoverOverwrite = parseInt(adapter.config.protocolD0BaudrateChangeoverOverwrite, 10); 335 | 336 | if (adapter.config.protocolD0BaudrateChangeoverOverwrite < 300) { 337 | adapter.log.error('D0 baudrate changeover overwrite ' + adapter.config.protocolD0BaudrateChangeoverOverwrite + ' invalid, check your configuration!'); 338 | return; 339 | } 340 | smOptions.protocolD0BaudrateChangeoverOverwrite = adapter.config.protocolD0BaudrateChangeoverOverwrite; 341 | } 342 | } 343 | if (adapter.config.protocol === 'SmlProtocol') { // we have a Serial connection 344 | smOptions.protocolSmlIgnoreInvalidCRC = adapter.config.protocolSmlIgnoreInvalidCRC = adapter.config.protocolSmlIgnoreInvalidCRC === 'true' || adapter.config.protocolSmlIgnoreInvalidCRC === true; 345 | if (adapter.config.protocolSmlInputEncoding) { 346 | smOptions.protocolSmlInputEncoding = adapter.config.protocolSmlInputEncoding; 347 | } 348 | } 349 | if (adapter.config.obisFallbackMedium !== null && adapter.config.obisFallbackMedium !== undefined) { 350 | adapter.config.obisFallbackMedium = parseInt(adapter.config.obisFallbackMedium, 10); 351 | if (adapter.config.obisFallbackMedium < 0 || adapter.config.obisFallbackMedium > 18 ) { 352 | adapter.log.error('OBIS Fallback medium code ' + adapter.config.obisFallbackMedium + ' invalid, check your configuration!'); 353 | return; 354 | } 355 | smOptions.obisFallbackMedium = adapter.config.obisFallbackMedium; 356 | } 357 | adapter.log.debug('SmartmeterObis options: ' + JSON.stringify(smOptions)); 358 | 359 | smTransport = SmartmeterObis.init(smOptions, storeObisData); 360 | 361 | smTransport.process(); 362 | } 363 | 364 | async function storeObisData(err, obisResult) { 365 | if (stopInProgress) return; 366 | if (err) { 367 | adapter.log.warn(err.message); 368 | adapter.log.debug(err); 369 | setConnected(false); 370 | return; 371 | } 372 | setConnected(true); 373 | let updateCount = 0; 374 | for (const obisId in obisResult) { 375 | if (stopInProgress) return; 376 | if (!obisResult.hasOwnProperty(obisId)) continue; 377 | 378 | adapter.log.debug(obisResult[obisId].idToString() + ': ' + SmartmeterObis.ObisNames.resolveObisName(obisResult[obisId], adapter.config.obisNameLanguage).obisName + ' = ' + obisResult[obisId].valueToString()); 379 | let i; 380 | let ioChannelId = obisResult[obisId].idToString().replace(/[\]\[*,;'"`<>\\?]/g, '__'); 381 | ioChannelId = ioChannelId.replace(/\./g, '_'); 382 | if (!smValues[obisId]) { 383 | let ioChannelName = SmartmeterObis.ObisNames.resolveObisName(obisResult[obisId], adapter.config.obisNameLanguage).obisName; 384 | adapter.log.debug('Create Channel ' + ioChannelId + ' with name ' + ioChannelName); 385 | try { 386 | await adapter.setObjectNotExistsAsync(ioChannelId, { 387 | type: 'channel', 388 | common: { 389 | name: ioChannelName 390 | }, 391 | native: {} 392 | }); 393 | } catch (err) { 394 | adapter.log.error('Error creating Channel: ' + err); 395 | } 396 | 397 | if (obisResult[obisId].getRawValue() !== undefined) { 398 | adapter.log.debug('Create State ' + ioChannelId + '.rawvalue'); 399 | try { 400 | await adapter.setObjectNotExistsAsync(ioChannelId + '.rawvalue', { 401 | type: 'state', 402 | common: { 403 | name: ioChannelId + '.rawvalue', 404 | type: 'string', 405 | read: true, 406 | role: 'value', 407 | write: false 408 | }, 409 | native: { 410 | id: ioChannelId + '.rawvalue' 411 | } 412 | }); 413 | } catch (err) { 414 | adapter.log.error('Error creating State: ' + err); 415 | } 416 | } 417 | 418 | adapter.log.debug('Create State ' + ioChannelId + '.value'); 419 | try { 420 | await adapter.setObjectNotExistsAsync(ioChannelId + '.value', { 421 | type: 'state', 422 | common: { 423 | name: ioChannelId + '.value', 424 | type: (typeof obisResult[obisId].getValue(0).value), 425 | read: true, 426 | unit: obisResult[obisId].getValue(0).unit, 427 | role: 'value', 428 | write: false 429 | }, 430 | native: { 431 | id: ioChannelId + '.value' 432 | } 433 | }); 434 | } catch (err) { 435 | adapter.log.error('Error creating State: ' + err); 436 | } 437 | 438 | if (obisResult[obisId].getValueLength() > 1) { 439 | for (i = 1; i < obisResult[obisId].getValueLength(); i++) { 440 | if (stopInProgress) return; 441 | adapter.log.debug('Create State ' + ioChannelId + '.value' + (i + 1)); 442 | try { 443 | await adapter.setObjectNotExistsAsync(ioChannelId + '.value' + (i + 1), { 444 | type: 'state', 445 | common: { 446 | name: ioChannelId + '.value' + (i + 1), 447 | type: (typeof obisResult[obisId].getValue(i).value), 448 | read: true, 449 | unit: obisResult[obisId].getValue(i).unit, 450 | role: 'value', 451 | write: false 452 | }, 453 | native: { 454 | id: ioChannelId + '.value' + (i + 1) 455 | } 456 | }); 457 | } catch (err) { 458 | adapter.log.error('Error creating State: ' + err); 459 | } 460 | } 461 | } 462 | } 463 | if (!smValues[obisId] || smValues[obisId].valueToString() !== obisResult[obisId].valueToString()) { 464 | if (obisResult[obisId].getRawValue() !== undefined) { 465 | adapter.log.debug('Set State ' + ioChannelId + '.rawvalue = ' + obisResult[obisId].getRawValue()); 466 | await adapter.setStateAsync(ioChannelId + '.rawvalue', {ack: true, val: obisResult[obisId].getRawValue()}); 467 | } 468 | 469 | adapter.log.debug('Set State ' + ioChannelId + '.value = ' + obisResult[obisId].getValue(0).value); 470 | await adapter.setStateAsync(ioChannelId + '.value', {ack: true, val: obisResult[obisId].getValue(0).value}); 471 | 472 | if (obisResult[obisId].getValueLength() > 1) { 473 | for (i = 1; i < obisResult[obisId].getValueLength(); i++) { 474 | if (stopInProgress) return; 475 | adapter.log.debug('Set State '+ ioChannelId + '.value' + (i + 1) + ' = ' + obisResult[obisId].getValue(i).value); 476 | await adapter.setStateAsync(ioChannelId + '.value' + (i + 1), {ack: true, val: obisResult[obisId].getValue(i).value}); 477 | } 478 | } 479 | smValues[obisId] = obisResult[obisId]; 480 | updateCount++; 481 | } 482 | else { 483 | adapter.log.debug('Data for '+ ioChannelId + ' unchanged'); 484 | } 485 | } 486 | adapter.log.info('Received ' + Object.keys(obisResult).length + ' values, ' + updateCount + ' updated'); 487 | } 488 | 489 | function processMessage(obj) { 490 | if (!obj) return; 491 | 492 | adapter.log.debug('Message received = ' + JSON.stringify(obj)); 493 | 494 | switch (obj.command) { 495 | case 'listUart': 496 | if (obj.callback) { 497 | if (SerialPort) { 498 | // read all found serial ports 499 | SerialPort.list().then(ports => { 500 | adapter.log.info('List of port: ' + JSON.stringify(ports)); 501 | if (process.platform !== 'win32') { 502 | ports.forEach(port => { 503 | if (port.pnpId) { 504 | try { 505 | const pathById = '/dev/serial/by-id/' + port.pnpId; 506 | if (fs.existsSync(pathById)) { 507 | port.realPath = port.path; 508 | port.path = pathById; 509 | } 510 | } catch (err) { 511 | adapter.log.debug('pnpId ' + port.pnpId + ' not existing: ' + err); 512 | } 513 | return port; 514 | } 515 | }); 516 | } 517 | adapter.sendTo(obj.from, obj.command, ports, obj.callback); 518 | }).catch(err => { 519 | adapter.log.warn('Can not get Serial port list: ' + err); 520 | adapter.sendTo(obj.from, obj.command, [{path: 'Not available'}], obj.callback); 521 | }); 522 | } else { 523 | adapter.log.warn('Module serialport is not available'); 524 | adapter.sendTo(obj.from, obj.command, [{path: 'Not available'}], obj.callback); 525 | } 526 | } 527 | break; 528 | } 529 | } 530 | 531 | // If started as allInOne/compact mode => return function to create instance 532 | if (module && module.parent) { 533 | module.exports = startAdapter; 534 | } else { 535 | // or start the instance directly 536 | startAdapter(); 537 | } 538 | -------------------------------------------------------------------------------- /test/lib/setup.js: -------------------------------------------------------------------------------- 1 | /* jshint -W097 */// jshint strict:false 2 | /*jslint node: true */ 3 | // check if tmp directory exists 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const child_process = require('child_process'); 7 | const rootDir = path.normalize(__dirname + '/../../'); 8 | const pkg = require(rootDir + 'package.json'); 9 | const debug = typeof v8debug === 'object'; 10 | pkg.main = pkg.main || 'main.js'; 11 | 12 | let JSONLDB; 13 | 14 | let adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/'); 15 | adapterName = adapterName[adapterName.length - 2]; 16 | let adapterStarted = false; 17 | 18 | function getAppName() { 19 | const parts = __dirname.replace(/\\/g, '/').split('/'); 20 | return parts[parts.length - 3].split('.')[0]; 21 | } 22 | 23 | function loadJSONLDB() { 24 | if (!JSONLDB) { 25 | const dbPath = require.resolve('@alcalzone/jsonl-db', { 26 | paths: [rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller'] 27 | }); 28 | console.log('JSONLDB path: ' + dbPath); 29 | try { 30 | const { JsonlDB } = require(dbPath); 31 | JSONLDB = JsonlDB; 32 | } catch (err) { 33 | console.log('Jsonl require error: ' + err); 34 | } 35 | } 36 | } 37 | 38 | const appName = getAppName().toLowerCase(); 39 | 40 | let objects; 41 | let states; 42 | 43 | let pid = null; 44 | 45 | let systemConfig = null; 46 | 47 | function copyFileSync(source, target) { 48 | 49 | let targetFile = target; 50 | 51 | //if target is a directory a new file with the same name will be created 52 | if (fs.existsSync(target)) { 53 | if ( fs.lstatSync( target ).isDirectory() ) { 54 | targetFile = path.join(target, path.basename(source)); 55 | } 56 | } 57 | 58 | try { 59 | fs.writeFileSync(targetFile, fs.readFileSync(source)); 60 | } 61 | catch (err) { 62 | console.log('file copy error: ' +source +' -> ' + targetFile + ' (error ignored)'); 63 | } 64 | } 65 | 66 | function copyFolderRecursiveSync(source, target, ignore) { 67 | let files = []; 68 | 69 | let base = path.basename(source); 70 | if (base === adapterName) { 71 | base = pkg.name; 72 | } 73 | //check if folder needs to be created or integrated 74 | const targetFolder = path.join(target, base); 75 | if (!fs.existsSync(targetFolder)) { 76 | fs.mkdirSync(targetFolder); 77 | } 78 | 79 | //copy 80 | if (fs.lstatSync(source).isDirectory()) { 81 | files = fs.readdirSync(source); 82 | files.forEach(function (file) { 83 | if (ignore && ignore.indexOf(file) !== -1) { 84 | return; 85 | } 86 | 87 | const curSource = path.join(source, file); 88 | const curTarget = path.join(targetFolder, file); 89 | if (fs.lstatSync(curSource).isDirectory()) { 90 | // ignore grunt files 91 | if (file.indexOf('grunt') !== -1) return; 92 | if (file === 'chai') return; 93 | if (file === 'mocha') return; 94 | copyFolderRecursiveSync(curSource, targetFolder, ignore); 95 | } else { 96 | copyFileSync(curSource, curTarget); 97 | } 98 | }); 99 | } 100 | } 101 | 102 | if (!fs.existsSync(rootDir + 'tmp')) { 103 | fs.mkdirSync(rootDir + 'tmp'); 104 | } 105 | 106 | async function storeOriginalFiles() { 107 | console.log('Store original files...'); 108 | const dataDir = rootDir + 'tmp/' + appName + '-data/'; 109 | 110 | if (fs.existsSync(dataDir + 'objects.json')) { 111 | const f = fs.readFileSync(dataDir + 'objects.json'); 112 | const objects = JSON.parse(f.toString()); 113 | if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) { 114 | objects['system.adapter.admin.0'].common.enabled = false; 115 | } 116 | if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) { 117 | objects['system.adapter.admin.1'].common.enabled = false; 118 | } 119 | 120 | fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects)); 121 | console.log('Store original objects.json'); 122 | } 123 | 124 | if (fs.existsSync(dataDir + 'states.json')) { 125 | try { 126 | const f = fs.readFileSync(dataDir + 'states.json'); 127 | fs.writeFileSync(dataDir + 'states.json.original', f); 128 | console.log('Store original states.json'); 129 | } catch (err) { 130 | console.log('no states.json found - ignore'); 131 | } 132 | } 133 | 134 | if (fs.existsSync(dataDir + 'objects.jsonl')) { 135 | loadJSONLDB(); 136 | const db = new JSONLDB(dataDir + 'objects.jsonl'); 137 | await db.open(); 138 | 139 | const admin0 = db.get('system.adapter.admin.0'); 140 | if (admin0) { 141 | if (admin0.common) { 142 | admin0.common.enabled = false; 143 | db.set('system.adapter.admin.0', admin0); 144 | } 145 | } 146 | 147 | const admin1 = db.get('system.adapter.admin.1'); 148 | if (admin1) { 149 | if (admin1.common) { 150 | admin1.common.enabled = false; 151 | db.set('system.adapter.admin.1', admin1); 152 | } 153 | } 154 | await db.close(); 155 | 156 | const f = fs.readFileSync(dataDir + 'objects.jsonl'); 157 | fs.writeFileSync(dataDir + 'objects.jsonl.original', f); 158 | console.log('Store original objects.jsonl'); 159 | } 160 | 161 | if (fs.existsSync(dataDir + 'states.jsonl')) { 162 | const f = fs.readFileSync(dataDir + 'states.jsonl'); 163 | fs.writeFileSync(dataDir + 'states.jsonl.original', f); 164 | console.log('Store original states.jsonl'); 165 | } 166 | } 167 | 168 | function restoreOriginalFiles() { 169 | console.log('restoreOriginalFiles...'); 170 | const dataDir = rootDir + 'tmp/' + appName + '-data/'; 171 | 172 | if (fs.existsSync(dataDir + 'objects.json.original')) { 173 | const f = fs.readFileSync(dataDir + 'objects.json.original'); 174 | fs.writeFileSync(dataDir + 'objects.json', f); 175 | } 176 | if (fs.existsSync(dataDir + 'objects.json.original')) { 177 | const f = fs.readFileSync(dataDir + 'states.json.original'); 178 | fs.writeFileSync(dataDir + 'states.json', f); 179 | } 180 | 181 | if (fs.existsSync(dataDir + 'objects.jsonl.original')) { 182 | const f = fs.readFileSync(dataDir + 'objects.jsonl.original'); 183 | fs.writeFileSync(dataDir + 'objects.jsonl', f); 184 | } 185 | if (fs.existsSync(dataDir + 'objects.jsonl.original')) { 186 | const f = fs.readFileSync(dataDir + 'states.jsonl.original'); 187 | fs.writeFileSync(dataDir + 'states.jsonl', f); 188 | } 189 | } 190 | 191 | async function checkIsAdapterInstalled(cb, counter, customName) { 192 | customName = customName || pkg.name.split('.').pop(); 193 | counter = counter || 0; 194 | const dataDir = rootDir + 'tmp/' + appName + '-data/'; 195 | console.log('checkIsAdapterInstalled...'); 196 | 197 | try { 198 | if (fs.existsSync(dataDir + 'objects.json')) { 199 | const f = fs.readFileSync(dataDir + 'objects.json'); 200 | const objects = JSON.parse(f.toString()); 201 | if (objects['system.adapter.' + customName + '.0']) { 202 | console.log('checkIsAdapterInstalled: ready!'); 203 | setTimeout(function () { 204 | if (cb) cb(); 205 | }, 100); 206 | return; 207 | } else { 208 | console.warn('checkIsAdapterInstalled: still not ready'); 209 | } 210 | } else if (fs.existsSync(dataDir + 'objects.jsonl')) { 211 | loadJSONLDB(); 212 | const db = new JSONLDB(dataDir + 'objects.jsonl'); 213 | try { 214 | await db.open(); 215 | } catch (err) { 216 | if (err.message.includes('Failed to lock DB file')) { 217 | console.log('checkIsAdapterInstalled: DB still opened ...'); 218 | } 219 | throw err; 220 | } 221 | 222 | const obj = db.get('system.adapter.' + customName + '.0'); 223 | await db.close(); 224 | 225 | if (obj) { 226 | console.log('checkIsAdapterInstalled: ready!'); 227 | setTimeout(function () { 228 | if (cb) cb(); 229 | }, 100); 230 | return; 231 | } else { 232 | console.warn('checkIsAdapterInstalled: still not ready'); 233 | } 234 | } else { 235 | console.error('checkIsAdapterInstalled: No objects file found in datadir ' + dataDir); 236 | } 237 | 238 | } catch (err) { 239 | console.log('checkIsAdapterInstalled: catch ' + err); 240 | } 241 | 242 | if (counter > 20) { 243 | console.error('checkIsAdapterInstalled: Cannot install!'); 244 | if (cb) cb('Cannot install'); 245 | } else { 246 | console.log('checkIsAdapterInstalled: wait...'); 247 | setTimeout(function() { 248 | checkIsAdapterInstalled(cb, counter + 1); 249 | }, 1000); 250 | } 251 | } 252 | 253 | async function checkIsControllerInstalled(cb, counter) { 254 | counter = counter || 0; 255 | const dataDir = rootDir + 'tmp/' + appName + '-data/'; 256 | 257 | console.log('checkIsControllerInstalled...'); 258 | try { 259 | if (fs.existsSync(dataDir + 'objects.json')) { 260 | const f = fs.readFileSync(dataDir + 'objects.json'); 261 | const objects = JSON.parse(f.toString()); 262 | if (objects['system.certificates']) { 263 | console.log('checkIsControllerInstalled: installed!'); 264 | setTimeout(function () { 265 | if (cb) cb(); 266 | }, 100); 267 | return; 268 | } 269 | } else if (fs.existsSync(dataDir + 'objects.jsonl')) { 270 | loadJSONLDB(); 271 | const db = new JSONLDB(dataDir + 'objects.jsonl'); 272 | try { 273 | await db.open(); 274 | } catch (err) { 275 | if (err.message.includes('Failed to lock DB file')) { 276 | console.log('checkIsControllerInstalled: DB still opened ...'); 277 | } 278 | throw err; 279 | } 280 | 281 | const obj = db.get('system.certificates'); 282 | await db.close(); 283 | 284 | if (obj) { 285 | console.log('checkIsControllerInstalled: installed!'); 286 | setTimeout(function () { 287 | if (cb) cb(); 288 | }, 100); 289 | return; 290 | } 291 | 292 | } else { 293 | console.error('checkIsControllerInstalled: No objects file found in datadir ' + dataDir); 294 | } 295 | } catch (err) { 296 | 297 | } 298 | 299 | if (counter > 20) { 300 | console.log('checkIsControllerInstalled: Cannot install!'); 301 | if (cb) cb('Cannot install'); 302 | } else { 303 | console.log('checkIsControllerInstalled: wait...'); 304 | setTimeout(function() { 305 | checkIsControllerInstalled(cb, counter + 1); 306 | }, 1000); 307 | } 308 | } 309 | 310 | function installAdapter(customName, cb) { 311 | if (typeof customName === 'function') { 312 | cb = customName; 313 | customName = null; 314 | } 315 | customName = customName || pkg.name.split('.').pop(); 316 | console.log('Install adapter...'); 317 | const startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js'; 318 | // make first install 319 | if (debug) { 320 | child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', { 321 | cwd: rootDir + 'tmp', 322 | stdio: [0, 1, 2] 323 | }); 324 | checkIsAdapterInstalled(function (error) { 325 | if (error) console.error(error); 326 | console.log('Adapter installed.'); 327 | if (cb) cb(); 328 | }); 329 | } else { 330 | // add controller 331 | const _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], { 332 | cwd: rootDir + 'tmp', 333 | stdio: [0, 1, 2, 'ipc'] 334 | }); 335 | 336 | waitForEnd(_pid, function () { 337 | checkIsAdapterInstalled(function (error) { 338 | if (error) console.error(error); 339 | console.log('Adapter installed.'); 340 | if (cb) cb(); 341 | }); 342 | }); 343 | } 344 | } 345 | 346 | function waitForEnd(_pid, cb) { 347 | if (!_pid) { 348 | cb(-1, -1); 349 | return; 350 | } 351 | _pid.on('exit', function (code, signal) { 352 | if (_pid) { 353 | _pid = null; 354 | cb(code, signal); 355 | } 356 | }); 357 | _pid.on('close', function (code, signal) { 358 | if (_pid) { 359 | _pid = null; 360 | cb(code, signal); 361 | } 362 | }); 363 | } 364 | 365 | function installJsController(cb) { 366 | console.log('installJsController...'); 367 | if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') || 368 | !fs.existsSync(rootDir + 'tmp/' + appName + '-data')) { 369 | // try to detect appName.js-controller in node_modules/appName.js-controller 370 | // travis CI installs js-controller into node_modules 371 | if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) { 372 | console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"'); 373 | // copy all 374 | // stop controller 375 | console.log('Stop controller if running...'); 376 | let _pid; 377 | if (debug) { 378 | // start controller 379 | _pid = child_process.exec('node ' + appName + '.js stop', { 380 | cwd: rootDir + 'node_modules/' + appName + '.js-controller', 381 | stdio: [0, 1, 2] 382 | }); 383 | } else { 384 | _pid = child_process.fork(appName + '.js', ['stop'], { 385 | cwd: rootDir + 'node_modules/' + appName + '.js-controller', 386 | stdio: [0, 1, 2, 'ipc'] 387 | }); 388 | } 389 | 390 | waitForEnd(_pid, function () { 391 | // copy all files into 392 | if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp'); 393 | if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules'); 394 | 395 | if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){ 396 | console.log('Copy js-controller...'); 397 | copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/'); 398 | } 399 | 400 | console.log('Setup js-controller...'); 401 | let __pid; 402 | if (debug) { 403 | // start controller 404 | _pid = child_process.exec('node ' + appName + '.js setup first --console', { 405 | cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', 406 | stdio: [0, 1, 2] 407 | }); 408 | } else { 409 | __pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], { 410 | cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', 411 | stdio: [0, 1, 2, 'ipc'] 412 | }); 413 | } 414 | waitForEnd(__pid, function () { 415 | checkIsControllerInstalled(function () { 416 | // change ports for object and state DBs 417 | const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); 418 | config.objects.port = 19001; 419 | config.states.port = 19000; 420 | 421 | // TEST WISE! 422 | //config.objects.type = 'jsonl'; 423 | //config.states.type = 'jsonl'; 424 | fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2)); 425 | console.log('Setup finished.'); 426 | 427 | copyAdapterToController(); 428 | 429 | installAdapter(async function () { 430 | await storeOriginalFiles(); 431 | if (cb) cb(true); 432 | }); 433 | }); 434 | }); 435 | }); 436 | } else { 437 | // check if port 9000 is free, else admin adapter will be added to running instance 438 | const client = new require('net').Socket(); 439 | client.on('error', () => {}); 440 | client.connect(9000, '127.0.0.1', function() { 441 | console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.'); 442 | process.exit(0); 443 | }); 444 | 445 | setTimeout(function () { 446 | client.destroy(); 447 | if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) { 448 | console.log('installJsController: no js-controller => install dev build from npm'); 449 | 450 | child_process.execSync('npm install ' + appName + '.js-controller@dev --prefix ./ --production', { 451 | cwd: rootDir + 'tmp/', 452 | stdio: [0, 1, 2] 453 | }); 454 | } else { 455 | console.log('Setup js-controller...'); 456 | let __pid; 457 | if (debug) { 458 | // start controller 459 | child_process.exec('node ' + appName + '.js setup first', { 460 | cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', 461 | stdio: [0, 1, 2] 462 | }); 463 | } else { 464 | child_process.fork(appName + '.js', ['setup', 'first'], { 465 | cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', 466 | stdio: [0, 1, 2, 'ipc'] 467 | }); 468 | } 469 | } 470 | 471 | // let npm install admin and run setup 472 | checkIsControllerInstalled(function () { 473 | let _pid; 474 | 475 | if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) { 476 | _pid = child_process.fork(appName + '.js', ['stop'], { 477 | cwd: rootDir + 'node_modules/' + appName + '.js-controller', 478 | stdio: [0, 1, 2, 'ipc'] 479 | }); 480 | } 481 | 482 | waitForEnd(_pid, function () { 483 | // change ports for object and state DBs 484 | const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); 485 | config.objects.port = 19001; 486 | config.states.port = 19000; 487 | 488 | // TEST WISE! 489 | //config.objects.type = 'jsonl'; 490 | //config.states.type = 'jsonl'; 491 | fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2)); 492 | 493 | copyAdapterToController(); 494 | 495 | installAdapter(async function () { 496 | await storeOriginalFiles(); 497 | if (cb) cb(true); 498 | }); 499 | }); 500 | }); 501 | }, 1000); 502 | } 503 | } else { 504 | setTimeout(function () { 505 | console.log('installJsController: js-controller installed'); 506 | if (cb) cb(false); 507 | }, 0); 508 | } 509 | } 510 | 511 | function copyAdapterToController() { 512 | console.log('Copy adapter...'); 513 | // Copy adapter to tmp/node_modules/appName.adapter 514 | copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']); 515 | console.log('Adapter copied.'); 516 | } 517 | 518 | function clearControllerLog() { 519 | const dirPath = rootDir + 'tmp/log'; 520 | let files; 521 | try { 522 | if (fs.existsSync(dirPath)) { 523 | console.log('Clear controller log...'); 524 | files = fs.readdirSync(dirPath); 525 | } else { 526 | console.log('Create controller log directory...'); 527 | files = []; 528 | fs.mkdirSync(dirPath); 529 | } 530 | } catch(e) { 531 | console.error('Cannot read "' + dirPath + '"'); 532 | return; 533 | } 534 | if (files.length > 0) { 535 | try { 536 | for (let i = 0; i < files.length; i++) { 537 | const filePath = dirPath + '/' + files[i]; 538 | fs.unlinkSync(filePath); 539 | } 540 | console.log('Controller log cleared'); 541 | } catch (err) { 542 | console.error('cannot clear log: ' + err); 543 | } 544 | } 545 | } 546 | 547 | function clearDB() { 548 | const dirPath = rootDir + 'tmp/iobroker-data/sqlite'; 549 | let files; 550 | try { 551 | if (fs.existsSync(dirPath)) { 552 | console.log('Clear sqlite DB...'); 553 | files = fs.readdirSync(dirPath); 554 | } else { 555 | console.log('Create controller log directory...'); 556 | files = []; 557 | fs.mkdirSync(dirPath); 558 | } 559 | } catch(e) { 560 | console.error('Cannot read "' + dirPath + '"'); 561 | return; 562 | } 563 | if (files.length > 0) { 564 | try { 565 | for (let i = 0; i < files.length; i++) { 566 | const filePath = dirPath + '/' + files[i]; 567 | fs.unlinkSync(filePath); 568 | } 569 | console.log('Clear sqlite DB'); 570 | } catch (err) { 571 | console.error('cannot clear DB: ' + err); 572 | } 573 | } 574 | } 575 | 576 | function setupController(cb) { 577 | installJsController(async function (isInited) { 578 | try { 579 | clearControllerLog(); 580 | clearDB(); 581 | 582 | if (!isInited) { 583 | restoreOriginalFiles(); 584 | copyAdapterToController(); 585 | } 586 | // read system.config object 587 | const dataDir = rootDir + 'tmp/' + appName + '-data/'; 588 | 589 | if (fs.existsSync(dataDir + 'objects.json')) { 590 | let objs; 591 | try { 592 | objs = fs.readFileSync(dataDir + 'objects.json'); 593 | objs = JSON.parse(objs); 594 | } catch (e) { 595 | console.log('ERROR reading/parsing system configuration. Ignore'); 596 | objs = {'system.config': {}}; 597 | } 598 | if (!objs || !objs['system.config']) { 599 | objs = {'system.config': {}}; 600 | } 601 | 602 | systemConfig = objs['system.config']; 603 | if (cb) cb(objs['system.config']); 604 | } else if (fs.existsSync(dataDir + 'objects.jsonl')) { 605 | loadJSONLDB(); 606 | const db = new JSONLDB(dataDir + 'objects.jsonl'); 607 | await db.open(); 608 | 609 | let config = db.get('system.config'); 610 | systemConfig = config || {}; 611 | 612 | await db.close(); 613 | 614 | if (cb) cb(systemConfig); 615 | } else { 616 | console.error('read SystemConfig: No objects file found in datadir ' + dataDir); 617 | } 618 | } catch (err) { 619 | console.error('setupController: ' + err); 620 | } 621 | }); 622 | } 623 | 624 | async function getSecret() { 625 | var dataDir = rootDir + 'tmp/' + appName + '-data/'; 626 | 627 | if (systemConfig) { 628 | return systemConfig.native.secret; 629 | } 630 | if (fs.existsSync(dataDir + 'objects.json')) { 631 | let objs; 632 | try { 633 | objs = fs.readFileSync(dataDir + 'objects.json'); 634 | objs = JSON.parse(objs); 635 | } 636 | catch (e) { 637 | console.warn("Could not load secret. Reason: " + e); 638 | return null; 639 | } 640 | if (!objs || !objs['system.config']) { 641 | objs = {'system.config': {}}; 642 | } 643 | 644 | return objs['system.config'].native.secre; 645 | } else if (fs.existsSync(dataDir + 'objects.jsonl')) { 646 | loadJSONLDB(); 647 | const db = new JSONLDB(dataDir + 'objects.jsonl'); 648 | await db.open(); 649 | 650 | let config = db.get('system.config'); 651 | config = config || {}; 652 | 653 | await db.close(); 654 | 655 | return config.native.secret; 656 | } else { 657 | console.error('read secret: No objects file found in datadir ' + dataDir); 658 | } 659 | 660 | } 661 | 662 | function encrypt (key, value) { 663 | var result = ''; 664 | for (var i = 0; i < value.length; ++i) { 665 | result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i)); 666 | } 667 | return result; 668 | } 669 | 670 | function startAdapter(objects, states, callback) { 671 | if (adapterStarted) { 672 | console.log('Adapter already started ...'); 673 | if (callback) callback(objects, states); 674 | return; 675 | } 676 | adapterStarted = true; 677 | console.log('startAdapter...'); 678 | if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) { 679 | try { 680 | if (debug) { 681 | // start controller 682 | pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', { 683 | cwd: rootDir + 'tmp', 684 | stdio: [0, 1, 2] 685 | }); 686 | } else { 687 | // start controller 688 | pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], { 689 | cwd: rootDir + 'tmp', 690 | stdio: [0, 1, 2, 'ipc'] 691 | }); 692 | } 693 | } catch (error) { 694 | console.error(JSON.stringify(error)); 695 | } 696 | } else { 697 | console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main); 698 | } 699 | if (callback) callback(objects, states); 700 | } 701 | 702 | function startController(isStartAdapter, onObjectChange, onStateChange, callback) { 703 | if (typeof isStartAdapter === 'function') { 704 | callback = onStateChange; 705 | onStateChange = onObjectChange; 706 | onObjectChange = isStartAdapter; 707 | isStartAdapter = true; 708 | } 709 | 710 | if (onStateChange === undefined) { 711 | callback = onObjectChange; 712 | onObjectChange = undefined; 713 | } 714 | 715 | if (pid) { 716 | console.error('Controller is already started!'); 717 | } else { 718 | console.log('startController...'); 719 | try { 720 | const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); 721 | 722 | adapterStarted = false; 723 | let isObjectConnected; 724 | let isStatesConnected; 725 | 726 | // rootDir + 'tmp/node_modules 727 | const objPath = require.resolve(`@iobroker/db-objects-${config.objects.type}`, { 728 | paths: [ rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller'] 729 | }); 730 | console.log('Objects Path: ' + objPath); 731 | const Objects = require(objPath).Server; 732 | objects = new Objects({ 733 | connection: { 734 | 'type': config.objects.type, 735 | 'host': '127.0.0.1', 736 | 'port': 19001, 737 | 'user': '', 738 | 'pass': '', 739 | 'noFileCache': false, 740 | 'connectTimeout': 2000 741 | }, 742 | logger: { 743 | silly: function (msg) { 744 | console.log(msg); 745 | }, 746 | debug: function (msg) { 747 | console.log(msg); 748 | }, 749 | info: function (msg) { 750 | console.log(msg); 751 | }, 752 | warn: function (msg) { 753 | console.warn(msg); 754 | }, 755 | error: function (msg) { 756 | console.error(msg); 757 | } 758 | }, 759 | connected: function () { 760 | isObjectConnected = true; 761 | if (isStatesConnected) { 762 | console.log('startController: started!'); 763 | if (isStartAdapter) { 764 | startAdapter(objects, states, callback); 765 | } else { 766 | if (callback) { 767 | callback(objects, states); 768 | callback = null; 769 | } 770 | } 771 | } 772 | }, 773 | change: onObjectChange 774 | }); 775 | 776 | // Just open in memory DB itself 777 | const statePath = require.resolve(`@iobroker/db-states-${config.states.type}`, { 778 | paths: [ rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller'] 779 | }); 780 | console.log('States Path: ' + statePath); 781 | const States = require(statePath).Server; 782 | states = new States({ 783 | connection: { 784 | type: config.states.type, 785 | host: '127.0.0.1', 786 | port: 19000, 787 | options: { 788 | auth_pass: null, 789 | retry_max_delay: 15000 790 | } 791 | }, 792 | logger: { 793 | silly: function (msg) { 794 | console.log(msg); 795 | }, 796 | debug: function (msg) { 797 | console.log(msg); 798 | }, 799 | info: function (msg) { 800 | console.log(msg); 801 | }, 802 | warn: function (msg) { 803 | console.log(msg); 804 | }, 805 | error: function (msg) { 806 | console.log(msg); 807 | } 808 | }, 809 | connected: function () { 810 | isStatesConnected = true; 811 | if (isObjectConnected) { 812 | console.log('startController: started!!'); 813 | if (isStartAdapter) { 814 | startAdapter(objects, states, callback); 815 | } else { 816 | if (callback) { 817 | callback(objects, states); 818 | callback = null; 819 | } 820 | } 821 | } 822 | }, 823 | change: onStateChange 824 | }); 825 | } catch (err) { 826 | console.log(err); 827 | } 828 | } 829 | } 830 | 831 | function stopAdapter(cb) { 832 | if (!pid) { 833 | console.error('Controller is not running!'); 834 | if (cb) { 835 | setTimeout(function () { 836 | cb(false); 837 | }, 0); 838 | } 839 | } else { 840 | adapterStarted = false; 841 | pid.on('exit', function (code, signal) { 842 | if (pid) { 843 | console.log('child process terminated due to receipt of signal ' + signal); 844 | if (cb) cb(); 845 | pid = null; 846 | } 847 | }); 848 | 849 | pid.on('close', function (code, signal) { 850 | if (pid) { 851 | if (cb) cb(); 852 | pid = null; 853 | } 854 | }); 855 | 856 | pid.kill('SIGTERM'); 857 | } 858 | } 859 | 860 | function _stopController() { 861 | if (objects) { 862 | objects.destroy(); 863 | objects = null; 864 | } 865 | if (states) { 866 | states.destroy(); 867 | states = null; 868 | } 869 | } 870 | 871 | function stopController(cb) { 872 | let timeout; 873 | if (objects) { 874 | console.log('Set system.adapter.' + pkg.name + '.0'); 875 | objects.setObject('system.adapter.' + pkg.name + '.0', { 876 | common:{ 877 | enabled: false 878 | } 879 | }); 880 | } 881 | 882 | stopAdapter(function () { 883 | if (timeout) { 884 | clearTimeout(timeout); 885 | timeout = null; 886 | } 887 | 888 | _stopController(); 889 | 890 | if (cb) { 891 | cb(true); 892 | cb = null; 893 | } 894 | }); 895 | 896 | timeout = setTimeout(function () { 897 | timeout = null; 898 | console.log('child process NOT terminated'); 899 | 900 | _stopController(); 901 | 902 | if (cb) { 903 | cb(false); 904 | cb = null; 905 | } 906 | pid = null; 907 | }, 5000); 908 | } 909 | 910 | // Setup the adapter 911 | async function setAdapterConfig(common, native, instance) { 912 | const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0); 913 | if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.json')) { 914 | const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString()); 915 | if (common) objects[id].common = common; 916 | if (native) objects[id].native = native; 917 | fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects)); 918 | } else if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.jsonl')) { 919 | loadJSONLDB(); 920 | const db = new JSONLDB(rootDir + 'tmp/' + appName + '-data/objects.jsonl'); 921 | await db.open(); 922 | 923 | let obj = db.get(id); 924 | if (common) obj.common = common; 925 | if (native) obj.native = native; 926 | db.set(id, obj); 927 | 928 | await db.close(); 929 | } else { 930 | console.error('setAdapterConfig: No objects file found in datadir ' + rootDir + 'tmp/' + appName + '-data/'); 931 | } 932 | } 933 | 934 | // Read config of the adapter 935 | async function getAdapterConfig(instance) { 936 | const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0); 937 | if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.json')) { 938 | const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString()); 939 | return objects[id]; 940 | } else if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.jsonl')) { 941 | loadJSONLDB(); 942 | const db = new JSONLDB(rootDir + 'tmp/' + appName + '-data/objects.jsonl'); 943 | await db.open(); 944 | 945 | let obj = db.get(id); 946 | 947 | await db.close(); 948 | return obj; 949 | } else { 950 | console.error('getAdapterConfig: No objects file found in datadir ' + rootDir + 'tmp/' + appName + '-data/'); 951 | } 952 | } 953 | 954 | if (typeof module !== undefined && module.parent) { 955 | module.exports.getAdapterConfig = getAdapterConfig; 956 | module.exports.setAdapterConfig = setAdapterConfig; 957 | module.exports.startController = startController; 958 | module.exports.stopController = stopController; 959 | module.exports.setupController = setupController; 960 | module.exports.stopAdapter = stopAdapter; 961 | module.exports.startAdapter = startAdapter; 962 | module.exports.installAdapter = installAdapter; 963 | module.exports.appName = appName; 964 | module.exports.adapterName = adapterName; 965 | module.exports.adapterStarted = adapterStarted; 966 | module.exports.getSecret = getSecret; 967 | module.exports.encrypt = encrypt; 968 | } 969 | -------------------------------------------------------------------------------- /test/mocha.setup.js: -------------------------------------------------------------------------------- 1 | process.on("unhandledRejection", (r) => { throw r; }); 2 | -------------------------------------------------------------------------------- /test/testAdapter.js: -------------------------------------------------------------------------------- 1 | /* jshint -W097 */// jshint strict:false 2 | /*jslint node: true */ 3 | /*jshint expr: true*/ 4 | var expect = require('chai').expect; 5 | var setup = require(__dirname + '/lib/setup'); 6 | var fs = require('fs'); 7 | 8 | var objects = null; 9 | var states = null; 10 | var onStateChanged = null; 11 | var onObjectChanged = null; 12 | var sendToID = 1; 13 | 14 | var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1); 15 | 16 | function checkConnectionOfAdapter(cb, counter) { 17 | counter = counter || 0; 18 | console.log('Try check #' + counter); 19 | if (counter > 30) { 20 | if (cb) cb('Cannot check connection'); 21 | return; 22 | } 23 | 24 | states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) { 25 | if (err) console.error(err); 26 | if (state && state.val) { 27 | if (cb) cb(); 28 | } else { 29 | setTimeout(function () { 30 | checkConnectionOfAdapter(cb, counter + 1); 31 | }, 1000); 32 | } 33 | }); 34 | } 35 | 36 | function checkValueOfState(id, value, cb, counter) { 37 | counter = counter || 0; 38 | if (counter > 20) { 39 | if (cb) cb('Cannot check value Of State ' + id); 40 | return; 41 | } 42 | 43 | states.getState(id, function (err, state) { 44 | if (err) console.error(err); 45 | if (value === null && !state) { 46 | if (cb) cb(); 47 | } else 48 | if (state && (value === undefined || state.val === value)) { 49 | if (cb) cb(); 50 | } else { 51 | setTimeout(function () { 52 | checkValueOfState(id, value, cb, counter + 1); 53 | }, 500); 54 | } 55 | }); 56 | } 57 | 58 | function sendTo(target, command, message, callback) { 59 | onStateChanged = function (id, state) { 60 | if (id === 'messagebox.system.adapter.test.0') { 61 | callback(state.message); 62 | } 63 | }; 64 | 65 | states.pushMessage('system.adapter.' + target, { 66 | command: command, 67 | message: message, 68 | from: 'system.adapter.test.0', 69 | callback: { 70 | message: message, 71 | id: sendToID++, 72 | ack: false, 73 | time: (new Date()).getTime() 74 | } 75 | }); 76 | } 77 | 78 | describe('Test ' + adapterShortName + ' adapter', function() { 79 | before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) { 80 | this.timeout(45*60*60*1000); // because of first install from npm 81 | 82 | setup.setupController(async function () { 83 | var config = await setup.getAdapterConfig(); 84 | // enable adapter 85 | config.common.enabled = true; 86 | config.common.loglevel = 'debug'; 87 | 88 | config.native.protocol = 'D0Protocol'; 89 | config.native.transport = 'LocalFileTransport'; 90 | config.native.transportLocalFilePath = __dirname + '/test.d0'; 91 | config.native.requestInterval = 10; 92 | config.native.obisFallbackMedium = 6; 93 | 94 | var testData = '/?Bla0!\r\n6.8(0029.055*MWh)6.26(01589.28*m3)9.21(00010213)6.26*01(01563.92*m3)6.8*01(0028.086*MWh)F(0)9.20(64030874)6.35(60*m)6.6(0017.2*kW)6.6*01(0017.2*kW)6.33(001.476*m3ph)9.4(088*C&082*C)6.31(0030710*h)6.32(0000194*h)9.22(R)9.6(000&00010213&0)9.7(20000)6.32*01(0000194*h)6.36(01-01)6.33*01(001.476*m3ph)6.8.1()6.8.2()6.8.3()6.8.4()6.8.5()6.8.1*01()6.8.2*01()6.8.3*01()\r\n6.8.4*01()6.8.5*01()9.4*01(088*C&082*C)6.36.1(2013-11-28)6.36.1*01(2013-11-28)6.36.2(2016-09-24)6.36.2*01(2016-09-24)6.36.3(2015-03-26)6.36.3*01(2015-03-26)6.36.4(2013-09-27)6.36.4*01(2013-09-27)6.36.5(2000-00-00)6.36*02(01)9.36(2017-01-18&01:36:47)9.24(0.6*m3ph)9.17(0)9.18()9.19()9.25()9.1(0&1&0&-&CV&3&2.14)9.2(&&)0.0(00010213)!\r\n'; 95 | fs.writeFileSync(__dirname + '/test.d0', testData); 96 | 97 | 98 | await setup.setAdapterConfig(config.common, config.native); 99 | 100 | setup.startController(true, function(id, obj) {}, function (id, state) { 101 | if (onStateChanged) onStateChanged(id, state); 102 | }, 103 | function (_objects, _states) { 104 | objects = _objects; 105 | states = _states; 106 | _done(); 107 | }); 108 | }); 109 | }); 110 | 111 | it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) { 112 | this.timeout(60000); 113 | checkConnectionOfAdapter(function (res) { 114 | if (res) console.log(res); 115 | expect(res).not.to.be.equal('Cannot check connection'); 116 | objects.setObject('system.adapter.test.0', { 117 | common: { 118 | 119 | }, 120 | type: 'instance' 121 | }, 122 | function () { 123 | states.subscribeMessage('system.adapter.test.0'); 124 | done(); 125 | }); 126 | }); 127 | }); 128 | 129 | it('Test ' + adapterShortName + ' adapter: test stored data', function (done) { 130 | this.timeout(25000); 131 | 132 | setTimeout(function() { 133 | states.getState('smartmeter.0.6-0:6_8.value', function (err, state) { 134 | if (err) console.error(err); 135 | expect(state).to.exist; 136 | if (!state) { 137 | console.error('state "smartmeter.0.6-0:6_8.value" not set'); 138 | } 139 | else { 140 | console.log('check smartmeter.0.6-0:6_8.value ... ' + state.val); 141 | expect(state.val).to.exist; 142 | expect(state.val).to.be.equal(29.055); 143 | } 144 | states.getState('smartmeter.0.6-0:6_8.rawvalue', function (err, state) { 145 | if (err) console.error(err); 146 | expect(state).to.exist; 147 | if (!state) { 148 | console.error('state "smartmeter.0.6-0:6_8.rawvalue" not set'); 149 | } 150 | else { 151 | console.log('check smartmeter.0.6-0:6_8.rawvalue ... ' + state.val); 152 | } 153 | expect(state.val).to.exist; 154 | expect(state.val).to.be.equal("0029.055*MWh"); 155 | states.getState('smartmeter.0.6-0:9_4.value', function (err, state) { 156 | if (err) console.error(err); 157 | expect(state).to.exist; 158 | if (!state) { 159 | console.error('state "smartmeter.0.6-0:9_4.value" not set'); 160 | } 161 | else { 162 | console.log('check smartmeter.0.6-0:9_4.value ... ' + state.val); 163 | } 164 | expect(state.val).to.exist; 165 | expect(state.val).to.be.equal(88); 166 | states.getState('smartmeter.0.6-0:9_4.value2', function (err, state) { 167 | if (err) console.error(err); 168 | expect(state).to.exist; 169 | if (!state) { 170 | console.error('state "smartmeter.0.6-0:9_4.value2" not set'); 171 | } 172 | else { 173 | console.log('check smartmeter.0.6-0:9_4.value2 ... ' + state.val); 174 | } 175 | expect(state.val).to.exist; 176 | expect(state.val).to.be.equal(82); 177 | done(); 178 | }); 179 | }); 180 | }); 181 | }); 182 | }, 17000); 183 | }); 184 | 185 | after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) { 186 | this.timeout(10000); 187 | 188 | setup.stopController(function (normalTerminated) { 189 | console.log('Adapter normal terminated: ' + normalTerminated); 190 | done(); 191 | }); 192 | }); 193 | }); 194 | -------------------------------------------------------------------------------- /test/testPackageFiles.js: -------------------------------------------------------------------------------- 1 | /* jshint -W097 */ 2 | /* jshint strict:false */ 3 | /* jslint node: true */ 4 | /* jshint expr: true */ 5 | 'use strict'; 6 | 7 | const expect = require('chai').expect; 8 | const fs = require('fs'); 9 | 10 | describe('Test package.json and io-package.json', () => { 11 | it('Test package files', done => { 12 | console.log(); 13 | 14 | const fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8'); 15 | const ioPackage = JSON.parse(fileContentIOPackage); 16 | 17 | const fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8'); 18 | const npmPackage = JSON.parse(fileContentNPMPackage); 19 | 20 | expect(ioPackage).to.be.an('object'); 21 | expect(npmPackage).to.be.an('object'); 22 | 23 | expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist; 24 | expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist; 25 | 26 | expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version); 27 | 28 | if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) { 29 | console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!'); 30 | console.log(); 31 | } 32 | 33 | expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist; 34 | expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist; 35 | 36 | expect(ioPackage.common.license, 'ERROR: License missing in io-package in common.license').to.exist; 37 | 38 | if (ioPackage.common.name.indexOf('template') !== 0) { 39 | if (Array.isArray(ioPackage.common.authors)) { 40 | expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0); 41 | if (ioPackage.common.authors.length === 1) { 42 | expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name '); 43 | } 44 | } 45 | else { 46 | expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name '); 47 | } 48 | } 49 | else { 50 | console.log('WARNING: Testing for set authors field in io-package skipped because template adapter'); 51 | console.log(); 52 | } 53 | expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true; 54 | if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') { 55 | console.log('WARNING: titleLang is not existing in io-package.json. Please add'); 56 | console.log(); 57 | } 58 | if ( 59 | ioPackage.common.title.indexOf('iobroker') !== -1 || 60 | ioPackage.common.title.indexOf('ioBroker') !== -1 || 61 | ioPackage.common.title.indexOf('adapter') !== -1 || 62 | ioPackage.common.title.indexOf('Adapter') !== -1 63 | ) { 64 | console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.'); 65 | console.log(); 66 | } 67 | 68 | if (!ioPackage.common.controller && !ioPackage.common.onlyWWW && !ioPackage.common.noConfig) { 69 | if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html')) { 70 | console.log('WARNING: Admin3 support is missing! Please add it'); 71 | console.log(); 72 | } 73 | if (ioPackage.common.materialize) { 74 | expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true; 75 | } 76 | } 77 | 78 | const licenseFileExists = fs.existsSync(__dirname + '/../LICENSE'); 79 | const fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8'); 80 | if (fileContentReadme.indexOf('## Changelog') === -1) { 81 | console.log('Warning: The README.md should have a section ## Changelog'); 82 | console.log(); 83 | } 84 | expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true; 85 | if (!licenseFileExists) { 86 | console.log('Warning: The License should also exist as LICENSE file'); 87 | console.log(); 88 | } 89 | if (fileContentReadme.indexOf('## License') === -1) { 90 | console.log('Warning: The README.md should also have a section ## License to be shown in Admin3'); 91 | console.log(); 92 | } 93 | done(); 94 | }); 95 | }); 96 | --------------------------------------------------------------------------------