├── .releaseconfig.json ├── .vscode ├── extensions.json ├── settings.json └── launch.json ├── admin ├── wolf-smartset.png ├── tsconfig.json ├── i18n │ ├── zh-cn │ │ └── translations.json │ ├── en │ │ └── translations.json │ ├── pl │ │ └── translations.json │ ├── nl │ │ └── translations.json │ ├── ru │ │ └── translations.json │ ├── uk │ │ └── translations.json │ ├── de │ │ └── translations.json │ ├── pt │ │ └── translations.json │ ├── it │ │ └── translations.json │ ├── es │ │ └── translations.json │ └── fr │ │ └── translations.json ├── admin.d.ts └── jsonConfig.json ├── test ├── tsconfig.json ├── package.js ├── mocharc.custom.json ├── integration.js └── mocha.setup.js ├── prettier.config.mjs ├── .gitignore ├── tsconfig.check.json ├── .github ├── dependabot.yml ├── auto-merge.yml ├── workflows │ ├── dependabot-auto-merge.yml │ ├── test-and-release.yml │ └── check-copilot-template.yml ├── ISSUE_TEMPLATE │ └── bug_report.md └── copilot-instructions.md ├── lib ├── adapter-config.d.ts └── tools.js ├── .create-adapter.json ├── eslint.config.mjs ├── tsconfig.json ├── LICENSE ├── package.json ├── CHANGELOG_OLD.md ├── README.md ├── io-package.json └── main.js /.releaseconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["iobroker", "license", "manual-review"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint" 4 | ] 5 | } -------------------------------------------------------------------------------- /admin/wolf-smartset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iobroker-community-adapters/ioBroker.wolf-smartset/HEAD/admin/wolf-smartset.png -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noImplicitAny": false 5 | }, 6 | "include": ["./**/*.js"] 7 | } 8 | -------------------------------------------------------------------------------- /test/package.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { tests } = require('@iobroker/testing'); 3 | 4 | // Validate the package files 5 | tests.packageFiles(path.join(__dirname, '..')); 6 | -------------------------------------------------------------------------------- /test/mocharc.custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": ["test/mocha.setup.js"], 3 | "watch-files": ["!(node_modules|test)/**/*.test.js", "*.test.js", "test/**/test!(PackageFiles|Startup).js"] 4 | } 5 | -------------------------------------------------------------------------------- /admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": [ 4 | "./admin.d.ts", 5 | "./**/*.js", 6 | // include the adapter-config definition if it exists 7 | "../src/lib/adapter-config.d.ts", 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/integration.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { tests } = require('@iobroker/testing'); 3 | 4 | // Run integration tests - See https://github.com/ioBroker/testing for a detailed explanation and further options 5 | tests.integration(path.join(__dirname, '..')); 6 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | // iobroker prettier configuration file 2 | import prettierConfig from '@iobroker/eslint-config/prettier.config.mjs'; 3 | 4 | export default { 5 | ...prettierConfig, 6 | // uncomment next line if you prefer double quotes 7 | // singleQuote: false, 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | .idea 3 | *.code-workspace 4 | node_modules 5 | nbproject 6 | 7 | # npm package files 8 | iobroker.*.tgz 9 | 10 | Thumbs.db 11 | 12 | # i18n intermediate files 13 | admin/i18n/flat.txt 14 | admin/i18n/*/flat.txt 15 | 16 | # ioBroker dev-server 17 | .dev-server/ -------------------------------------------------------------------------------- /tsconfig.check.json: -------------------------------------------------------------------------------- 1 | // Specialized tsconfig for type-checking js files 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": {}, 5 | "include": [ 6 | "**/*.js", 7 | "**/*.d.ts" 8 | ], 9 | "exclude": [ 10 | "**/build", 11 | "node_modules/", 12 | "widgets/", 13 | "gulpfile.js" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/mocha.setup.js: -------------------------------------------------------------------------------- 1 | // Don't silently swallow unhandled rejections 2 | process.on('unhandledRejection', (e) => { 3 | throw e; 4 | }); 5 | 6 | // enable the should interface with sinon 7 | // and load chai-as-promised and sinon-chai by default 8 | const sinonChai = require('sinon-chai'); 9 | const chaiAsPromised = require('chai-as-promised'); 10 | const { should, use } = require('chai'); 11 | 12 | should(); 13 | use(sinonChai); 14 | use(chaiAsPromised); 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot will run on day 28 of each month at 02:08 (Europe/Berlin timezone) 2 | version: 2 3 | updates: 4 | 5 | - package-ecosystem: "npm" 6 | directory: "/" 7 | schedule: 8 | interval: "cron" 9 | timezone: "Europe/Berlin" 10 | cronjob: "8 2 28 * *" 11 | open-pull-requests-limit: 15 12 | versioning-strategy: "increase" 13 | 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "cron" 18 | timezone: "Europe/Berlin" 19 | cronjob: "8 2 28 * *" 20 | open-pull-requests-limit: 15 21 | -------------------------------------------------------------------------------- /lib/adapter-config.d.ts: -------------------------------------------------------------------------------- 1 | // This file extends the AdapterConfig type from "@types/iobroker" 2 | // using the actual properties present in io-package.json 3 | // in order to provide typings for adapter.config properties 4 | 5 | import { native } from '../io-package.json'; 6 | 7 | type _AdapterConfig = typeof native; 8 | 9 | // Augment the globally declared type ioBroker.AdapterConfig 10 | declare global { 11 | namespace ioBroker { 12 | interface AdapterConfig extends _AdapterConfig { 13 | // Do not enter anything here! 14 | } 15 | } 16 | } 17 | 18 | // this is required so the above AdapterConfig is found by TypeScript / type checking 19 | export {}; -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": true, 3 | "editor.formatOnSave": true, 4 | "json.schemas": [ 5 | { 6 | "fileMatch": [ 7 | "io-package.json" 8 | ], 9 | "url": "https://raw.githubusercontent.com/ioBroker/ioBroker.js-controller/master/schemas/io-package.json" 10 | }, 11 | { 12 | "fileMatch": [ 13 | "admin/jsonConfig.json", 14 | "admin/jsonConfig.json5", 15 | "admin/jsonCustom.json", 16 | "admin/jsonCustom.json5", 17 | "admin/jsonTab.json", 18 | "admin/jsonTab.json5" 19 | ], 20 | "url": "https://raw.githubusercontent.com/ioBroker/ioBroker.admin/master/packages/jsonConfig/schemas/jsonConfig.json" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /.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/workflows/dependabot-auto-merge.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@v5 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 23 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.create-adapter.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": true, 3 | "adapterName": "wolf-smartset", 4 | "title": "Wolf SmartSet", 5 | "description": "Connect Wolf cloud to IoBroker", 6 | "keywords": [ 7 | "wolf", 8 | "heating", 9 | "cloud" 10 | ], 11 | "contributors": [ 12 | "MeisterTR" 13 | ], 14 | "expert": "no", 15 | "features": [ 16 | "adapter" 17 | ], 18 | "adminFeatures": [], 19 | "type": "climate-control", 20 | "startMode": "daemon", 21 | "connectionType": "cloud", 22 | "dataSource": "poll", 23 | "connectionIndicator": "no", 24 | "language": "JavaScript", 25 | "adminReact": "no", 26 | "tools": [ 27 | "ESLint", 28 | "type checking" 29 | ], 30 | "indentation": "Tab", 31 | "quotes": "single", 32 | "es6class": "yes", 33 | "authorName": "MeisterTR", 34 | "authorGithub": "MeisterTR", 35 | "authorEmail": "meistertr.smarthome@gmail.com", 36 | "gitRemoteProtocol": "HTTPS", 37 | "gitCommit": "no", 38 | "license": "MIT License", 39 | "ci": "gh-actions", 40 | "dependabot": "no", 41 | "creatorVersion": "1.31.0" 42 | } -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // ioBroker eslint template configuration file for js and ts files 2 | // Please note that esm or react based modules need additional modules loaded. 3 | import config from '@iobroker/eslint-config'; 4 | 5 | export default [ 6 | ...config, 7 | 8 | { 9 | // specify files to exclude from linting here 10 | ignores: [ 11 | '.dev-server/', 12 | '.vscode/', 13 | '*.test.js', 14 | 'test/**/*.js', 15 | '*.config.mjs', 16 | 'build', 17 | 'admin/build', 18 | 'admin/words.js', 19 | 'admin/admin.d.ts', 20 | '**/adapter-config.d.ts' 21 | ] 22 | }, 23 | 24 | { 25 | // you may disable some 'jsdoc' warnings - but using jsdoc is highly recommended 26 | // as this improves maintainability. jsdoc warnings will not block buiuld process. 27 | rules: { 28 | // 'jsdoc/require-jsdoc': 'off', 29 | }, 30 | }, 31 | 32 | ]; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | // Root tsconfig to set the settings and power editor support for all TS files 2 | { 3 | "compileOnSave": true, 4 | "compilerOptions": { 5 | // do not compile anything, this file is just to configure type checking 6 | "noEmit": true, 7 | 8 | // check JS files 9 | "allowJs": true, 10 | "checkJs": true, 11 | 12 | "module": "commonjs", 13 | "moduleResolution": "node", 14 | "esModuleInterop": true, 15 | // this is necessary for the automatic typing of the adapter config 16 | "resolveJsonModule": true, 17 | 18 | // Set this to false if you want to disable the very strict rules (not recommended) 19 | "strict": true, 20 | // Or enable some of those features for more fine-grained control 21 | // "strictNullChecks": true, 22 | // "strictPropertyInitialization": true, 23 | // "strictBindCallApply": true, 24 | "noImplicitAny": false, 25 | // "noUnusedLocals": true, 26 | // "noUnusedParameters": true, 27 | 28 | // Consider targetting es2019 or higher if you only support Node.js 12+ 29 | "target": "es2018", 30 | "useUnknownInCatchVariables": false, 31 | }, 32 | "include": [ 33 | "**/*.js", 34 | "**/*.d.ts" 35 | ], 36 | "exclude": [ 37 | "node_modules/**" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-2025 iobroker-community-adapters 4 | Copyright (c) 2021-2023 MeisterTR , ioBroker Community 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Verwendet IntelliSense zum Ermitteln möglicher Attribute. 3 | // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen. 4 | // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch normal", 9 | "program": "${workspaceFolder}/main.js", 10 | "args": ["--instance", "0", "--force", "--logs", "--debug"], 11 | "request": "launch", 12 | "stopOnEntry": true, 13 | "console": "internalConsole", 14 | "outputCapture": "std", 15 | "skipFiles": [ 16 | "/**" 17 | ], 18 | "type": "node", 19 | "env": {"NODE_PATH":"${workspaceFolder}/.dev-server/default/node_modules"} 20 | }, 21 | 22 | { 23 | "name": "Launch install", 24 | "program": "${workspaceFolder}/main.js", 25 | "args": ["--instance", "0", "--force", "--logs", "--debug", "--install"], 26 | "request": "launch", 27 | "stopOnEntry": true, 28 | "console": "internalConsole", 29 | "outputCapture": "std", 30 | "skipFiles": [ 31 | "/**" 32 | ], 33 | "type": "node" 34 | }, 35 | 36 | 37 | { 38 | "name": "Attach by Process ID", 39 | "processId": "${command:PickProcess}", 40 | "request": "attach", 41 | "skipFiles": [ 42 | "/**" 43 | ], 44 | "type": "node" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /admin/i18n/zh-cn/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "高级设置", 3 | "API Profiling": "API分析", 4 | "BundleId for Long Poll Cycle": "长期民意调查周期的束", 5 | "BundleId for Short Poll Cycle": "短投票周期的束", 6 | "Configure poll intervals and the set of parameters to poll": "配置轮询间隔和一组参数", 7 | "Configure Short and Long Poll Cycle Interval": "配置短轮和长轮循环间隔", 8 | "Device": "设备", 9 | "Do Expert Login": "进行专家登录", 10 | "donateInformation": "随意通过Github或Iobroker论坛建议新功能或设备。如果您喜欢这个适配器,欢迎您捐款。", 11 | "Enable API Profiling": "启用API分析", 12 | "Enable API profiling to track number of parameters requested and returned": "启用API分析到跟踪请求的参数数量并返回", 13 | "Enter username and password of your Wolf Smartset account": "输入您的Wolf SmartSet帐户的用户名和密码", 14 | "Expert Login": "专家登录", 15 | "Expert Password": "专家密码", 16 | "List of Wolf Devices": "狼设备清单", 17 | "Long Poll Cycle Interval (min)": "长轮循环间隔(最小)", 18 | "Main Settings": "主要设置", 19 | "Password": "密码", 20 | "Poll Cycle Intervals and Parameter Lists": "投票周期间隔和参数列表", 21 | "Select a different Wolf device for this adapter instance": "为此适配器实例选择其他狼设备", 22 | "Set the BundleId to be used in poll requests": "设置要在民意调查请求中使用的捆绑包", 23 | "Short Poll Cycle Interval (sec)": "简短的轮询周期间隔(SEC)", 24 | "Username": "用户名", 25 | "Use this Device": "使用此设备", 26 | "Wolf Device": "狼设备", 27 | "Wolf Smartset Account": "Wolf Smartset帐户", 28 | "Check for Public IP Changes": "检查公共IP更改", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "启用通过ipify.org检查您的公共IP,以便更换公共IP后更快的会话恢复", 30 | "Enable Public IP Checking": "启用公共IP检查", 31 | "Expert Login is required to poll expert parameters": "需要专家登录到民意调查专家参数", 32 | "Parameters of Bundle": "捆绑包的参数", 33 | "Include in Short Poll Cycle": "在简短的民意调查周期中包括", 34 | "Include in Long Poll Cycle": "包括长期民意调查周期", 35 | "Poll all Parameters": "轮询所有参数", 36 | "This poll strategy is backward compatible with adpater version 1.x": "此民意调查策略与Adpater版本1.X兼容" 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iobroker.wolf-smartset", 3 | "version": "2.1.2", 4 | "description": "Connect Wolf cloud to IoBroker", 5 | "author": { 6 | "name": "MeisterTR", 7 | "email": "meistertr.smarthome@gmail.com" 8 | }, 9 | "contributors": [ 10 | "MeisterTR ", 11 | "flingo64 ", 12 | "mcm1957 " 13 | ], 14 | "homepage": "https://github.com/iobroker-community-adapters/ioBroker.wolf-smartset", 15 | "license": "MIT", 16 | "keywords": [ 17 | "ioBroker", 18 | "wolf", 19 | "heating", 20 | "cloud" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/iobroker-community-adapters/ioBroker.wolf-smartset" 25 | }, 26 | "engines": { 27 | "node": ">=20" 28 | }, 29 | "dependencies": { 30 | "@iobroker/adapter-core": "^3.3.2", 31 | "axios": "^1.11.0", 32 | "openid-client": "^5.6.5" 33 | }, 34 | "devDependencies": { 35 | "@alcalzone/release-script": "^5.0.0", 36 | "@alcalzone/release-script-plugin-iobroker": "^3.7.2", 37 | "@alcalzone/release-script-plugin-license": "^3.7.0", 38 | "@alcalzone/release-script-plugin-manual-review": "^4.0.0", 39 | "@iobroker/adapter-dev": "^1.5.0", 40 | "@iobroker/eslint-config": "^2.2.0", 41 | "@iobroker/testing": "^5.2.2", 42 | "@tsconfig/node14": "^14.1.8", 43 | "@types/node": "^24.10.1", 44 | "typescript": "~5.9.3" 45 | }, 46 | "main": "main.js", 47 | "files": [ 48 | "admin{,/!(src)/**}/!(tsconfig|tsconfig.*|.eslintrc).json", 49 | "admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}", 50 | "lib/", 51 | "www/", 52 | "io-package.json", 53 | "LICENSE", 54 | "main.js" 55 | ], 56 | "scripts": { 57 | "test:js": "mocha --config test/mocharc.custom.json \"{!(node_modules|test)/**/*.test.js,*.test.js,test/**/test!(PackageFiles|Startup).js}\"", 58 | "test:package": "mocha test/package --exit", 59 | "test:integration": "mocha test/integration --exit", 60 | "test": "npm run test:js && npm run test:package", 61 | "check": "tsc --noEmit -p tsconfig.check.json", 62 | "lint": "eslint -c eslint.config.mjs .", 63 | "translate": "translate-adapter", 64 | "release": "release-script" 65 | }, 66 | "bugs": { 67 | "url": "https://github.com/iobroker-community-adapters/ioBroker.wolf-smartset/issues" 68 | }, 69 | "readmeFilename": "README.md" 70 | } -------------------------------------------------------------------------------- /admin/admin.d.ts: -------------------------------------------------------------------------------- 1 | declare let systemDictionary: Record>; 2 | 3 | declare let load: (settings: Record, onChange: (hasChanges: boolean) => void) => void; 4 | declare let save: (callback: (settings: Record) => void) => void; 5 | 6 | // make load and save exist on the window object 7 | interface Window { 8 | load: typeof load; 9 | save: typeof save; 10 | } 11 | 12 | declare const instance: number; 13 | declare const adapter: string; 14 | /** Translates text */ 15 | declare function _(text: string): string; 16 | declare const socket: ioBrokerSocket; 17 | declare function sendTo( 18 | instance: any | null, 19 | command: string, 20 | message: any, 21 | callback: (result: SendToResult) => void | Promise, 22 | ): void; 23 | 24 | interface SendToResult { 25 | error?: string | Error; 26 | result?: any; 27 | } 28 | 29 | // tslint:disable-next-line:class-name 30 | interface ioBrokerSocket { 31 | emit( 32 | command: 'subscribeObjects', 33 | pattern: string, 34 | callback?: (err?: string) => void | Promise, 35 | ): void; 36 | emit( 37 | command: 'subscribeStates', 38 | pattern: string, 39 | callback?: (err?: string) => void | Promise, 40 | ): void; 41 | emit( 42 | command: 'unsubscribeObjects', 43 | pattern: string, 44 | callback?: (err?: string) => void | Promise, 45 | ): void; 46 | emit( 47 | command: 'unsubscribeStates', 48 | pattern: string, 49 | callback?: (err?: string) => void | Promise, 50 | ): void; 51 | 52 | emit( 53 | event: 'getObjectView', 54 | view: 'system', 55 | type: 'device', 56 | options: ioBroker.GetObjectViewParams, 57 | callback: ( 58 | err: string | undefined, 59 | result?: any, 60 | ) => void | Promise, 61 | ): void; 62 | emit( 63 | event: 'getStates', 64 | callback: ( 65 | err: string | undefined, 66 | result?: Record, 67 | ) => void, 68 | ): void; 69 | emit( 70 | event: 'getState', 71 | id: string, 72 | callback: (err: string | undefined, result?: ioBroker.State) => void, 73 | ): void; 74 | emit( 75 | event: 'setState', 76 | id: string, 77 | state: unknown, 78 | callback: (err: string | undefined, result?: any) => void, 79 | ): void; 80 | 81 | on(event: 'objectChange', handler: ioBroker.ObjectChangeHandler): void; 82 | on(event: 'stateChange', handler: ioBroker.StateChangeHandler): void; 83 | removeEventHandler( 84 | event: 'objectChange', 85 | handler: ioBroker.ObjectChangeHandler, 86 | ): void; 87 | removeEventHandler( 88 | event: 'stateChange', 89 | handler: ioBroker.StateChangeHandler, 90 | ): void; 91 | 92 | // TODO: other events 93 | } 94 | -------------------------------------------------------------------------------- /admin/i18n/en/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Advanced Settings", 3 | "API Profiling": "API Profiling", 4 | "BundleId for Long Poll Cycle": "BundleId for Long Poll Cycle", 5 | "BundleId for Short Poll Cycle": "BundleId for Short Poll Cycle", 6 | "Configure poll intervals and the set of parameters to poll": "Configure poll intervals and the set of parameters to poll", 7 | "Configure Short and Long Poll Cycle Interval": "Configure Short and Long Poll Cycle Interval", 8 | "Device": "Device", 9 | "Do Expert Login": "Do Expert Login", 10 | "donateInformation": "Feel free to suggest new features or devices via Github or ioBroker forum. If you like this adapter, you are very welcome to donate.", 11 | "Enable API Profiling": "Enable API Profiling", 12 | "Enable API profiling to track number of parameters requested and returned": "Enable API profiling to track number of parameters requested and returned", 13 | "Enter username and password of your Wolf Smartset account": "Enter username and password of your Wolf Smartset account", 14 | "Expert Login": "Expert Login", 15 | "Expert Password": "Expert Password", 16 | "List of Wolf Devices": "List of Wolf Devices", 17 | "Long Poll Cycle Interval (min)": "Long Poll Cycle Interval (min)", 18 | "Main Settings": "Main Settings", 19 | "Password": "Password", 20 | "Poll Cycle Intervals and Parameter Lists": "Poll Cycle Intervals and Parameter Lists", 21 | "Select a different Wolf device for this adapter instance": "Select a different Wolf device for this adapter instance", 22 | "Set the BundleId to be used in poll requests": "Set the BundleId to be used in poll requests", 23 | "Short Poll Cycle Interval (sec)": "Short Poll Cycle Interval (sec)", 24 | "Username": "Username", 25 | "Use this Device": "Use this Device", 26 | "Wolf Device": "Wolf Device", 27 | "Wolf Smartset Account": "Wolf Smartset Account", 28 | "Check for Public IP Changes": "Check for Public IP Changes", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Enable checking your public IP via ipify.org for faster session recovery after changed public IP", 30 | "Enable Public IP Checking": "Enable Public IP Checking", 31 | "Expert Login is required to poll expert parameters": "Expert Login is required to poll expert parameters", 32 | "Parameters of Bundle": "Parameters of Bundle", 33 | "Include in Short Poll Cycle": "Include in Short Poll Cycle", 34 | "Include in Long Poll Cycle": "Include in Long Poll Cycle", 35 | "Poll all Parameters": "Poll all Parameters", 36 | "This poll strategy is backward compatible with adpater version 1.x": "This poll strategy is backward compatible with adpater version 1.x" 37 | } -------------------------------------------------------------------------------- /admin/i18n/pl/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Zaawansowane ustawienia", 3 | "API Profiling": "Profilowanie API", 4 | "BundleId for Long Poll Cycle": "Pakiet do długiego cyklu ankiet", 5 | "BundleId for Short Poll Cycle": "Pakiet do krótkiego cyklu ankiety", 6 | "Configure poll intervals and the set of parameters to poll": "Skonfiguruj interwały ankiet i zestaw parametrów do ankiety", 7 | "Configure Short and Long Poll Cycle Interval": "Skonfiguruj krótki i długi interwał cyklu ankiet", 8 | "Device": "Urządzenie", 9 | "Do Expert Login": "Wykonaj logowanie ekspertów", 10 | "donateInformation": "Zaproponuj nowe funkcje lub urządzenia za pośrednictwem GitHub lub IOBRAKER Forum. Jeśli podoba Ci się ten adapter, możesz przekazać darowiznę.", 11 | "Enable API Profiling": "Włącz profilowanie API", 12 | "Enable API profiling to track number of parameters requested and returned": "Włącz profilowanie API w celu śledzenia liczby żądanych i zwróconych parametrów", 13 | "Enter username and password of your Wolf Smartset account": "Wprowadź nazwę użytkownika i hasło swojego konta Wolf Smartset", 14 | "Expert Login": "Login ekspertów", 15 | "Expert Password": "Hasło eksperckie", 16 | "List of Wolf Devices": "Lista urządzeń wilków", 17 | "Long Poll Cycle Interval (min)": "Długi przedział cyklu ankiety (min)", 18 | "Main Settings": "Główne ustawienia", 19 | "Password": "Hasło", 20 | "Poll Cycle Intervals and Parameter Lists": "Interwały cyklu ankiety i listy parametrów", 21 | "Select a different Wolf device for this adapter instance": "Wybierz inne urządzenie wilka do tej instancji adaptera", 22 | "Set the BundleId to be used in poll requests": "Ustaw pakiet, który ma być używany w żądaniach ankiety", 23 | "Short Poll Cycle Interval (sec)": "Interwał cyklu sondażowego (SEC)", 24 | "Username": "Nazwa użytkownika", 25 | "Use this Device": "Użyj tego urządzenia", 26 | "Wolf Device": "Urządzenie wilka", 27 | "Wolf Smartset Account": "Konto Wolf Smartset", 28 | "Check for Public IP Changes": "Sprawdź zmiany IP publicznych", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Włącz sprawdzanie publicznego adresu IP za pośrednictwem iPify.org w celu szybszego odzyskiwania sesji po zmianie publicznego adresu IP", 30 | "Enable Public IP Checking": "Włącz publiczne sprawdzanie IP", 31 | "Expert Login is required to poll expert parameters": "Login ekspertów jest wymagany do ankietowania parametrów ekspertów", 32 | "Parameters of Bundle": "Parametry pakietu", 33 | "Include in Short Poll Cycle": "Uwzględnij w krótkim cyklu ankiet", 34 | "Include in Long Poll Cycle": "Uwzględnij w długim cyklu ankiet", 35 | "Poll all Parameters": "Sondaż wszystkie parametry", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Ta strategia parolowa jest zgodna wstecz z adpaterką w wersji 1.x" 37 | } 38 | -------------------------------------------------------------------------------- /admin/i18n/nl/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Geavanceerde instellingen", 3 | "API Profiling": "API -profilering", 4 | "BundleId for Long Poll Cycle": "Bundleid voor een lange pollcyclus", 5 | "BundleId for Short Poll Cycle": "Bundleid voor een korte pollcyclus", 6 | "Configure poll intervals and the set of parameters to poll": "Configureer poll -intervallen en de set parameters om te peilen", 7 | "Configure Short and Long Poll Cycle Interval": "Configureer een korte en lange peilcyclusinterval", 8 | "Device": "Apparaat", 9 | "Do Expert Login": "Log in deskundige inloggen", 10 | "donateInformation": "Voel je vrij om nieuwe functies of apparaten voor te stellen via GitHub of Iobroker Forum. Als je deze adapter leuk vindt, ben je van harte welkom om te doneren.", 11 | "Enable API Profiling": "Schakel API -profilering in", 12 | "Enable API profiling to track number of parameters requested and returned": "Schakel API -profilering in om het aantal gevraagde en geretourneerde parameters bij te houden", 13 | "Enter username and password of your Wolf Smartset account": "Voer de gebruikersnaam en het wachtwoord van uw Wolf Smartset -account in", 14 | "Expert Login": "Login van deskundige", 15 | "Expert Password": "Deskundige wachtwoord", 16 | "List of Wolf Devices": "Lijst met wolfsapparaten", 17 | "Long Poll Cycle Interval (min)": "Lang poll cyclusinterval (min)", 18 | "Main Settings": "Hoofdinstellingen", 19 | "Password": "Wachtwoord", 20 | "Poll Cycle Intervals and Parameter Lists": "Poll -cyclusintervallen en parameterlijsten", 21 | "Select a different Wolf device for this adapter instance": "Selecteer een ander wolfapparaat voor dit adapterinstantie", 22 | "Set the BundleId to be used in poll requests": "Stel de bundelid in die moet worden gebruikt in poll -aanvragen", 23 | "Short Poll Cycle Interval (sec)": "Kort poll cyclusinterval (SEC)", 24 | "Username": "Gebruikersnaam", 25 | "Use this Device": "Gebruik dit apparaat", 26 | "Wolf Device": "Wolvenapparaat", 27 | "Wolf Smartset Account": "Wolf Smartset -account", 28 | "Check for Public IP Changes": "Controleer op openbare IP -wijzigingen", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Schakel uw openbare IP in via iPify.org voor sneller herstel van sessie na veranderd openbaar IP", 30 | "Enable Public IP Checking": "Schakel openbare IP -controle in", 31 | "Expert Login is required to poll expert parameters": "Inloggen in deskundigen is vereist om deskundige parameters te bestrijden", 32 | "Parameters of Bundle": "Parameters van bundel", 33 | "Include in Short Poll Cycle": "Neem in korte peilcyclus op", 34 | "Include in Long Poll Cycle": "Inclusief in een lange pollcyclus", 35 | "Poll all Parameters": "Peil alle parameters", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Deze poll -strategie is achterwaarts compatibel met Adpater -versie 1.x" 37 | } 38 | -------------------------------------------------------------------------------- /admin/i18n/ru/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Расширенные настройки", 3 | "API Profiling": "Профилирование API", 4 | "BundleId for Long Poll Cycle": "Bundleid для длительного цикла опросов", 5 | "BundleId for Short Poll Cycle": "Bundleid для короткого цикла опросов", 6 | "Configure poll intervals and the set of parameters to poll": "Настроить интервалы опросов и набор параметров для опроса", 7 | "Configure Short and Long Poll Cycle Interval": "Настройка коротких и длинных интервалов цикла опроса", 8 | "Device": "Устройство", 9 | "Do Expert Login": "Сделайте экспертный вход", 10 | "donateInformation": "Не стесняйтесь предлагать новые функции или устройства через GitHub или Iobroker Forum. Если вам нравится этот адаптер, вы можете пожертвовать.", 11 | "Enable API Profiling": "Включить профилирование API", 12 | "Enable API profiling to track number of parameters requested and returned": "Включите профилирование API для отслеживания количества запрошенных параметров и возвращений", 13 | "Enter username and password of your Wolf Smartset account": "Введите имя пользователя и пароль своей учетной записи Wolf Smartset", 14 | "Expert Login": "Экспертный вход", 15 | "Expert Password": "Экспертный пароль", 16 | "List of Wolf Devices": "Список устройств волков", 17 | "Long Poll Cycle Interval (min)": "Длинный интервал цикла опроса (мин)", 18 | "Main Settings": "Основные настройки", 19 | "Password": "Пароль", 20 | "Poll Cycle Intervals and Parameter Lists": "Интервалы цикла опросов и списки параметров", 21 | "Select a different Wolf device for this adapter instance": "Выберите другое устройство Wolf для этого экземпляра адаптера", 22 | "Set the BundleId to be used in poll requests": "Установите Bundleid для использования в запросах опроса", 23 | "Short Poll Cycle Interval (sec)": "Короткий интервал цикла опроса (SEC)", 24 | "Username": "Имя пользователя", 25 | "Use this Device": "Используйте это устройство", 26 | "Wolf Device": "Устройство волков", 27 | "Wolf Smartset Account": "Учетная запись Wolf Smartset", 28 | "Check for Public IP Changes": "Проверить на наличие изменений публичных IP", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Включить проверку вашего публичного IP -адреса через ipify.org для более быстрого восстановления сессии после изменения публичного IP", 30 | "Enable Public IP Checking": "Включить публичную проверку ИС", 31 | "Expert Login is required to poll expert parameters": "Экспертный вход требуется для опроса экспертных параметров", 32 | "Parameters of Bundle": "Параметры пакета", 33 | "Include in Short Poll Cycle": "Включите в короткий цикл опроса", 34 | "Include in Long Poll Cycle": "Включите в длительный цикл опроса", 35 | "Poll all Parameters": "Опрос всех параметров", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Эта стратегия опроса обратно совместима с версией ADPATER 1.X" 37 | } 38 | -------------------------------------------------------------------------------- /admin/i18n/uk/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Розширені налаштування", 3 | "API Profiling": "Профілювання API", 4 | "BundleId for Long Poll Cycle": "Bundleid для тривалого циклу опитування", 5 | "BundleId for Short Poll Cycle": "Bundleid для короткого циклу опитування", 6 | "Configure poll intervals and the set of parameters to poll": "Налаштуйте інтервали опитування та набір параметрів для опитування", 7 | "Configure Short and Long Poll Cycle Interval": "Налаштуйте короткий та довгий інтервал циклу опитування", 8 | "Device": "Пристрій", 9 | "Do Expert Login": "Робити експертний вхід", 10 | "donateInformation": "Не соромтеся пропонувати нові функції або пристрої через форум Github або iObroker. Якщо вам подобається цей адаптер, ви дуже можете пожертвувати.", 11 | "Enable API Profiling": "Увімкнути профілювання API", 12 | "Enable API profiling to track number of parameters requested and returned": "Увімкнути профілювання API відстежувати кількість запитуваних параметрів та повернути", 13 | "Enter username and password of your Wolf Smartset account": "Введіть ім'я користувача та пароль свого облікового запису Wolf SmartSet", 14 | "Expert Login": "Експертний вхід", 15 | "Expert Password": "Експертний пароль", 16 | "List of Wolf Devices": "Список вовчих пристроїв", 17 | "Long Poll Cycle Interval (min)": "Довгий інтервал циклу опитування (хв)", 18 | "Main Settings": "Основні налаштування", 19 | "Password": "Пароль", 20 | "Poll Cycle Intervals and Parameter Lists": "Інтервали циклу опитування та списки параметрів", 21 | "Select a different Wolf device for this adapter instance": "Виберіть інший вовчий пристрій для цього екземпляра адаптера", 22 | "Set the BundleId to be used in poll requests": "Встановіть BundleID для використання в запитах опитування", 23 | "Short Poll Cycle Interval (sec)": "Короткий інтервал циклу опитування (SEC)", 24 | "Username": "Ім'я користувача", 25 | "Use this Device": "Використовуйте цей пристрій", 26 | "Wolf Device": "Вовчий пристрій", 27 | "Wolf Smartset Account": "Вовк SmartSet обліковий запис", 28 | "Check for Public IP Changes": "Перевірте наявність загальнодоступних змін IP", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Увімкніть перевірку публічного IP за допомогою ipify.org для швидшого відновлення сеансу після зміни публічного IP", 30 | "Enable Public IP Checking": "Увімкнути публічну перевірку IP", 31 | "Expert Login is required to poll expert parameters": "Експертний вхід повинен опитувати експертних параметрів", 32 | "Parameters of Bundle": "Параметри пакету", 33 | "Include in Short Poll Cycle": "Включіть у короткий цикл опитування", 34 | "Include in Long Poll Cycle": "Включіть у тривалий цикл опитування", 35 | "Poll all Parameters": "Опитування всіх параметрів", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Ця стратегія опитування сумісна назад з версією Adpater 1.x" 37 | } 38 | -------------------------------------------------------------------------------- /admin/i18n/de/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Erweiterte Einstellungen", 3 | "API Profiling": "API-Profiling", 4 | "BundleId for Long Poll Cycle": "BundleId für langen Pollzyklus", 5 | "BundleId for Short Poll Cycle": "BundleId für kurzen Pollzyklus", 6 | "Configure poll intervals and the set of parameters to poll": "Konfigurieren Sie die Pollzyklus-Intervalle und den Satz der abzufragenden Parameter", 7 | "Configure Short and Long Poll Cycle Interval": "Konfigurieren Sie das kurze und lange Pollzyklus-Intervall", 8 | "Device": "Anlage", 9 | "Do Expert Login": "Einloggen als Fachmann", 10 | "donateInformation": "Neue Geräte / Features können gerne über Github oder das ioBroker Forum angefragt werden. Wenn dieser Adapter gefällt / nützlich ist, sind Spenden herzlich Willkommen.", 11 | "Enable API Profiling": "API-Profiling aktivieren", 12 | "Enable API profiling to track number of parameters requested and returned": "Aktivieren Sie die API-Profiling, um die Anzahl der angeforderten und zurückgegebenen Parameter zu verfolgen", 13 | "Enter username and password of your Wolf Smartset account": "Geben Sie den Benutzernamen und das Passwort Ihres Wolf Smartset-Kontos ein", 14 | "Expert Login": "Fachmann Login", 15 | "Expert Password": "Fachmann-Passwort", 16 | "List of Wolf Devices": "Liste der Wolf Anlagen", 17 | "Long Poll Cycle Interval (min)": "Langes Pollzyklus-Intervall (min)", 18 | "Main Settings": "Haupteinstellungen", 19 | "Password": "Passwort", 20 | "Poll Cycle Intervals and Parameter Lists": "Pollzyklus-Intervalle und Parameterlisten", 21 | "Select a different Wolf device for this adapter instance": "Eine andere Wolf Anlage für diese Adapterinstanz auswählen", 22 | "Set the BundleId to be used in poll requests": "Stellen Sie die BundleId für Anfragen ein", 23 | "Short Poll Cycle Interval (sec)": "Kurzes Pollzyklus-Intervall (sek)", 24 | "Username": "Benutzername", 25 | "Use this Device": "Verwende diese Anlage", 26 | "Wolf Device": "Wolf Anlage", 27 | "Wolf Smartset Account": "Wolf Smartset-Konto", 28 | "Check for Public IP Changes": "Änderung der öffentlichen IP-Adresse überwachen", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Aktivieren Sie die Überwachung Ihrer öffentliche IP-Adresse über ipify.org für eine schnellere Wiederherstellung der Sitzung nach Änderung der öffentlichen IP-Adresse", 30 | "Enable Public IP Checking": "Überwachung der öffentliche IP-Adresse aktivieren", 31 | "Expert Login is required to poll expert parameters": "Die Fachmann-Anmeldung ist erforderlich, um Fachmann-Parameter abzufragen", 32 | "Parameters of Bundle": "Parameter des Bundles ...", 33 | "Include in Short Poll Cycle": "... im kurzen Pollzyklus einbeziehen", 34 | "Include in Long Poll Cycle": "... im langen Pollzyklus einbeziehen", 35 | "Poll all Parameters": "Alle Parameter abfragen", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Diese Abfragestrategie ist abwärtskompatibel mit Adpater Version 1.x" 37 | } -------------------------------------------------------------------------------- /admin/i18n/pt/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Configurações avançadas", 3 | "API Profiling": "Perfil da API", 4 | "BundleId for Long Poll Cycle": "BundleID para um ciclo de pesquisa longa", 5 | "BundleId for Short Poll Cycle": "BundleID para ciclo de pesquisa curto", 6 | "Configure poll intervals and the set of parameters to poll": "Configurar intervalos de pesquisa e o conjunto de parâmetros para pesquisar", 7 | "Configure Short and Long Poll Cycle Interval": "Configurar intervalo de ciclo de pesquisa curto e longo", 8 | "Device": "Dispositivo", 9 | "Do Expert Login": "Faça login especializado", 10 | "donateInformation": "Sinta -se à vontade para sugerir novos recursos ou dispositivos via fórum Github ou IoBroker. Se você gosta deste adaptador, é muito bem -vindo doar.", 11 | "Enable API Profiling": "Ativar perfil da API", 12 | "Enable API profiling to track number of parameters requested and returned": "Ativar o perfil da API para rastrear o número de parâmetros solicitados e retornados", 13 | "Enter username and password of your Wolf Smartset account": "Digite o nome de usuário e a senha da sua conta do Wolf SmartSet", 14 | "Expert Login": "Login especializado", 15 | "Expert Password": "Senha especializada", 16 | "List of Wolf Devices": "Lista de dispositivos de lobo", 17 | "Long Poll Cycle Interval (min)": "Intervalo de ciclo de pesquisa longa (min)", 18 | "Main Settings": "Configurações principais", 19 | "Password": "Senha", 20 | "Poll Cycle Intervals and Parameter Lists": "Intervalos de ciclo de pesquisa e listas de parâmetros", 21 | "Select a different Wolf device for this adapter instance": "Selecione um dispositivo de lobo diferente para esta instância do adaptador", 22 | "Set the BundleId to be used in poll requests": "Defina o BundleID para ser usado em solicitações de pesquisa", 23 | "Short Poll Cycle Interval (sec)": "Intervalo de ciclo de pesquisa curto (SEC)", 24 | "Username": "Nome de usuário", 25 | "Use this Device": "Use este dispositivo", 26 | "Wolf Device": "Dispositivo de lobo", 27 | "Wolf Smartset Account": "Conta do SmartSet Wolf", 28 | "Check for Public IP Changes": "Verifique se há mudanças públicas de IP", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Habilite a verificação do seu IP público via ipify.org para uma recuperação de sessão mais rápida após alterado IP público", 30 | "Enable Public IP Checking": "Habilitar verificação de IP público", 31 | "Expert Login is required to poll expert parameters": "O login especialista é necessário para pesquisar parâmetros de especialistas", 32 | "Parameters of Bundle": "Parâmetros do pacote", 33 | "Include in Short Poll Cycle": "Inclua em curto ciclo de pesquisa", 34 | "Include in Long Poll Cycle": "Inclua no ciclo de pesquisa longa", 35 | "Poll all Parameters": "Enquete todos os parâmetros", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Esta estratégia de pesquisa é compatível com versões anteriores com a versão 1.x do Adpater" 37 | } 38 | -------------------------------------------------------------------------------- /admin/i18n/it/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Impostazioni avanzate", 3 | "API Profiling": "Profilazione dell'API", 4 | "BundleId for Long Poll Cycle": "Bundleid per un lungo ciclo di sondaggio", 5 | "BundleId for Short Poll Cycle": "Bundleid per breve ciclo di sondaggio", 6 | "Configure poll intervals and the set of parameters to poll": "Configurare gli intervalli di sondaggio e l'insieme di parametri per il sondaggio", 7 | "Configure Short and Long Poll Cycle Interval": "Configurare intervallo di ciclo di sondaggio breve e lungo", 8 | "Device": "Dispositivo", 9 | "Do Expert Login": "Accedi esperti", 10 | "donateInformation": "Sentiti libero di suggerire nuove funzionalità o dispositivi tramite GitHub o IoBroker Forum. Se ti piace questo adattatore, sei il benvenuto a donare.", 11 | "Enable API Profiling": "Abilita la profilazione dell'API", 12 | "Enable API profiling to track number of parameters requested and returned": "Abilita la profilazione dell'API per tenere traccia del numero di parametri richiesti e restituiti", 13 | "Enter username and password of your Wolf Smartset account": "Inserisci nome utente e password del tuo account Wolf Smartset", 14 | "Expert Login": "Accesso esperto", 15 | "Expert Password": "Password esperta", 16 | "List of Wolf Devices": "Elenco dei dispositivi di lupo", 17 | "Long Poll Cycle Interval (min)": "Intervallo di cicli di sondaggio a lungo (min)", 18 | "Main Settings": "Impostazioni principali", 19 | "Password": "Password", 20 | "Poll Cycle Intervals and Parameter Lists": "Intervalli del ciclo del sondaggio e elenchi di parametri", 21 | "Select a different Wolf device for this adapter instance": "Seleziona un dispositivo di lupo diverso per questa istanza dell'adattatore", 22 | "Set the BundleId to be used in poll requests": "Imposta il bundleid da utilizzare nelle richieste di sondaggio", 23 | "Short Poll Cycle Interval (sec)": "Intervallo di cicli di sondaggio breve (sec)", 24 | "Username": "Nome utente", 25 | "Use this Device": "Usa questo dispositivo", 26 | "Wolf Device": "Dispositivo lupo", 27 | "Wolf Smartset Account": "Account Wolf SmartSet", 28 | "Check for Public IP Changes": "Controlla le modifiche all'IP pubblica", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Abilita il controllo del tuo IP pubblico tramite ipify.org per un recupero di sessione più rapido dopo aver modificato l'IP pubblica", 30 | "Enable Public IP Checking": "Abilita il controllo dell'IP pubblico", 31 | "Expert Login is required to poll expert parameters": "L'accesso esperto è tenuto a sondaggio dei parametri degli esperti", 32 | "Parameters of Bundle": "Parametri del pacchetto", 33 | "Include in Short Poll Cycle": "Includere in breve ciclo di sondaggio", 34 | "Include in Long Poll Cycle": "Includere nel ciclo di sondaggio lungo", 35 | "Poll all Parameters": "Sondaggio tutti i parametri", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Questa strategia di sondaggio è arretrata compatibile con Adpater versione 1.x" 37 | } 38 | -------------------------------------------------------------------------------- /admin/i18n/es/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Configuración avanzada", 3 | "API Profiling": "Perfil de API", 4 | "BundleId for Long Poll Cycle": "Bundleid para un ciclo largo de encuesta", 5 | "BundleId for Short Poll Cycle": "Bundleid para ciclo de votación corta", 6 | "Configure poll intervals and the set of parameters to poll": "Configurar intervalos de encuesta y el conjunto de parámetros para encuestar", 7 | "Configure Short and Long Poll Cycle Interval": "Configurar un intervalo de ciclo de votación corto y largo", 8 | "Device": "Dispositivo", 9 | "Do Expert Login": "Iniciar sesión en el experto", 10 | "donateInformation": "Siéntase libre de sugerir nuevas funciones o dispositivos a través de GitHub o Iobroker Forum. Si te gusta este adaptador, puedes donar.", 11 | "Enable API Profiling": "Habilitar el perfil API", 12 | "Enable API profiling to track number of parameters requested and returned": "Habilitar el perfil de API para rastrear el número de parámetros solicitados y devueltos", 13 | "Enter username and password of your Wolf Smartset account": "Ingrese el nombre de usuario y la contraseña de su cuenta de Wolf SmartSet", 14 | "Expert Login": "Iniciar sesión en el experto", 15 | "Expert Password": "Contraseña de expertos", 16 | "List of Wolf Devices": "Lista de dispositivos de lobo", 17 | "Long Poll Cycle Interval (min)": "Intervalo de ciclo de votación largo (min)", 18 | "Main Settings": "Configuración principal", 19 | "Password": "Contraseña", 20 | "Poll Cycle Intervals and Parameter Lists": "Intervalos del ciclo de votación y listas de parámetros", 21 | "Select a different Wolf device for this adapter instance": "Seleccione un dispositivo de lobo diferente para esta instancia de adaptador", 22 | "Set the BundleId to be used in poll requests": "Establezca el paquete para ser utilizado en solicitudes de encuestas", 23 | "Short Poll Cycle Interval (sec)": "Intervalo de ciclo de votación corto (SEC)", 24 | "Username": "Nombre de usuario", 25 | "Use this Device": "Usar este dispositivo", 26 | "Wolf Device": "Dispositivo de lobo", 27 | "Wolf Smartset Account": "Cuenta de lobo smartset", 28 | "Check for Public IP Changes": "Verifique los cambios públicos de IP", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Habilite la comprobación de su IP pública a través de ipify.org para una recuperación de sesión más rápida después de cambiar la IP pública", 30 | "Enable Public IP Checking": "Habilitar la verificación de IP pública", 31 | "Expert Login is required to poll expert parameters": "Se requiere inicio de sesión de expertos para encuestar los parámetros de expertos", 32 | "Parameters of Bundle": "Parámetros del paquete", 33 | "Include in Short Poll Cycle": "Incluir en breve ciclo de votación", 34 | "Include in Long Poll Cycle": "Incluir en un ciclo de votación largo", 35 | "Poll all Parameters": "Encuesta todos los parámetros", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Esta estrategia de encuesta es compatible con ADPATER versión 1.x" 37 | } 38 | -------------------------------------------------------------------------------- /admin/i18n/fr/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Advanced Settings": "Paramètres avancés", 3 | "API Profiling": "Profilage API", 4 | "BundleId for Long Poll Cycle": "Bundleid pour le cycle de sondage long", 5 | "BundleId for Short Poll Cycle": "Bundleid pour le cycle de sondage court", 6 | "Configure poll intervals and the set of parameters to poll": "Configurer les intervalles de sondage et l'ensemble des paramètres pour sonder", 7 | "Configure Short and Long Poll Cycle Interval": "Configurer l'intervalle de cycle de sondage court et long", 8 | "Device": "Appareil", 9 | "Do Expert Login": "Faire la connexion experte", 10 | "donateInformation": "N'hésitez pas à suggérer de nouvelles fonctionnalités ou appareils via GitHub ou Iobroker Forum. Si vous aimez cet adaptateur, vous êtes les bienvenus pour faire un don.", 11 | "Enable API Profiling": "Activer le profilage de l'API", 12 | "Enable API profiling to track number of parameters requested and returned": "Activer le profil API pour suivre le nombre de paramètres demandés et retournés", 13 | "Enter username and password of your Wolf Smartset account": "Entrez le nom d'utilisateur et le mot de passe de votre compte Smartset Wolf", 14 | "Expert Login": "Connexion experte", 15 | "Expert Password": "Mot de passe expert", 16 | "List of Wolf Devices": "Liste des appareils de loup", 17 | "Long Poll Cycle Interval (min)": "Intervalle de cycle de sondage long (min)", 18 | "Main Settings": "Paramètres principaux", 19 | "Password": "Mot de passe", 20 | "Poll Cycle Intervals and Parameter Lists": "Intervalles de cycle de sondage et listes de paramètres", 21 | "Select a different Wolf device for this adapter instance": "Sélectionnez un autre appareil Wolf pour cette instance d'adaptateur", 22 | "Set the BundleId to be used in poll requests": "Définissez le bundleid à utiliser dans les demandes de sondage", 23 | "Short Poll Cycle Interval (sec)": "Intervalle de cycle de sondage court (SEC)", 24 | "Username": "Nom d'utilisateur", 25 | "Use this Device": "Utilisez cet appareil", 26 | "Wolf Device": "Dispositif de loup", 27 | "Wolf Smartset Account": "Compte Smartset Wolf", 28 | "Check for Public IP Changes": "Vérifiez les modifications publiques IP", 29 | "Enable checking your public IP via ipify.org for faster session recovery after changed public IP": "Activer la vérification de votre IP publique via ipify.org pour une récupération de session plus rapide après la modification de l'IP publique", 30 | "Enable Public IP Checking": "Activer la vérification publique de la propriété intellectuelle", 31 | "Expert Login is required to poll expert parameters": "La connexion des experts est nécessaire pour interroger les paramètres d'experts", 32 | "Parameters of Bundle": "Paramètres du faisceau", 33 | "Include in Short Poll Cycle": "Inclure dans le cycle de sondage court", 34 | "Include in Long Poll Cycle": "Inclure dans le cycle de sondage long", 35 | "Poll all Parameters": "Sonder tous les paramètres", 36 | "This poll strategy is backward compatible with adpater version 1.x": "Cette stratégie de sondage est en arrière compatible avec ADPATAT Version 1.x" 37 | } 38 | -------------------------------------------------------------------------------- /CHANGELOG_OLD.md: -------------------------------------------------------------------------------- 1 | # Older Changelogs 2 | ## 2.0.0-internal 3 | - (flingo64) further internal changes omitted from news section due to size limitations 4 | - Demystified (decoded) API constants (array _021a[]) 5 | - All API strings (URL, paths, params) as constants 6 | - Fixed various typechecker and eslint/prettier warnings 7 | - Replaced deprecated ioBroker async functions by sync function equivalents 8 | - Re-ordered and renamed private functions in main.js and admin/wss.js 9 | - Reorganized adapter initialization / openIdInit for more robust error handling 10 | - Added openId logout on instance unload to force a fresh AuthN on next adapter start 11 | - Added API Profiling option to track requested BundleId / # of requested params and # of returned params / # of returned values 12 | - Migrated translations from words.js to i18n 13 | - Added complete translation for all adapter instance setting strings 14 | - Disabled code for caching of auth data to allow a clean re-auth when required by server or on adapter reload 15 | - Added optional Check for public IP changes for faster Wolf Smartset expert session recovery 16 | - README: added descriptions on all instance settings and adpater operation 17 | 18 | ## 1.2.4 (2024-12-22) 19 | - (flingo64) Bugfix for issues #281, #329, #365, #406: ioBroker object limits min/max use Wolf Smartset Min/MaxValueCondition if available, otherwise use Min/MaxValue now. 20 | - (flingo64) Added some comments on Wolf Smartset ControlTypes 21 | - (flingo64) Modified misspelled variable name to 'SubMenuEntry' 22 | - (flingo64) Add NamePrefix, if exists, to object names (e.g. 'A1: ', 'WP001: ') for better parameter identification 23 | - (mcm1957) Adapter has been adapted to @iobroker/eslint-config and eslint 9.x. 24 | - (mcm1957) Dependencies have been updated 25 | 26 | ## 1.2.3 (2024-04-29) 27 | - (mcm1957) Dependencies have been updated 28 | 29 | ## 1.2.2 (2024-04-22) 30 | - (flingo64) A crash during re-authentication has been fixed. OpenIdInit will be called only once to avoid endless loop during re-authentication. 31 | 32 | ## 1.2.1 (2024-04-19) 33 | - (flingo64) Initialization added to openId. This fixes GET_AUTH_TOKEN_ERROR [#304, #330] 34 | 35 | ## 1.2.0 (2024-04-19) 36 | - (mcm1957) Adapter requires node.js >= 18 and js-controller >= 5 now 37 | - (mcm1957) Dependencies have been updated 38 | 39 | ## 1.1.1 (2023-01-26) 40 | * (Apollon77) Adjusted to new Login procedure 41 | * (Apollon77) Tokens are now stored and tried to be refreshed automatically 42 | * (Apollon77) Errors in session updates will try to create new session or authenticate anew 43 | * (Apollon77) Generates folder and channel structures for created states 44 | * (Apollon77) Fix some more crash cases 45 | * (Apollon77) make sure adapter is stopped correctly in all cases 46 | 47 | ## 1.0.0 (2021-07-31) 48 | * (MeisterTR) fix Sentry: IOBROKER-WOLF-SMARTSET-6,IOBROKER-WOLF-SMARTSET-5, IOBROKER-WOLF-SMARTSET-7,IOBROKER-WOLF-SMARTSET-8,IOBROKER-WOLF-SMARTSET-1,IOBROKER-WOLF-SMARTSET-3,IOBROKER-WOLF-SMARTSET-4 49 | * (MeisterTR) Change api from app data to Web PEASE DELETE ADAPTER AND REINSTALL OR DELETE ALL OBJECTS 50 | * (MEISTERTR) added "FACHMANN" states 51 | 52 | ## 0.2.2 (26.03.2021) 53 | * (MeisterTR) fix timeouts, fix conection 54 | 55 | ## 0.2.1 56 | * (MeisterTR) Rebuild api and objects, breaking change 57 | 58 | ## 0.1.2 59 | * (MeisterTR) Poll and set Values 60 | * (MeisterTR) Fix error at start 61 | 62 | ## 0.1.0 63 | * (MeisterTR) First running Version, Poll Param Only 64 | -------------------------------------------------------------------------------- /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 | * 6 | * @param it The variable to test 7 | * @returns 8 | */ 9 | function isObject(it) { 10 | // This is necessary because: 11 | // typeof null === 'object' 12 | // typeof [] === 'object' 13 | // [] instanceof Object === true 14 | return Object.prototype.toString.call(it) === '[object Object]'; 15 | } 16 | 17 | /** 18 | * Tests whether the given variable is really an Array 19 | * 20 | * @param it The variable to test 21 | * @returns 22 | */ 23 | function isArray(it) { 24 | if (typeof Array.isArray === 'function') { 25 | return Array.isArray(it); 26 | } 27 | return Object.prototype.toString.call(it) === '[object Array]'; 28 | } 29 | 30 | /** 31 | * Translates text to the target language. Automatically chooses the right translation API. 32 | * 33 | * @param text The text to translate 34 | * @param targetLang The target languate 35 | * @param [yandexApiKey] The yandex API key. You can create one for free at https://translate.yandex.com/developers 36 | * @returns 37 | */ 38 | async function translateText(text, targetLang, yandexApiKey) { 39 | if (targetLang === 'en') { 40 | return text; 41 | } else if (!text) { 42 | return ''; 43 | } 44 | if (yandexApiKey) { 45 | return translateYandex(text, targetLang, yandexApiKey); 46 | } 47 | return translateGoogle(text, targetLang); 48 | } 49 | 50 | /** 51 | * Translates text with Yandex API 52 | * 53 | * @param text The text to translate 54 | * @param targetLang The target languate 55 | * @param apiKey The yandex API key. You can create one for free at https://translate.yandex.com/developers 56 | * @returns 57 | */ 58 | async function translateYandex(text, targetLang, apiKey) { 59 | if (targetLang === 'zh-cn') { 60 | targetLang = 'zh'; 61 | } 62 | try { 63 | const url = `https://translate.yandex.net/api/v1.5/tr.json/translate?key=${apiKey}&text=${encodeURIComponent(text)}&lang=en-${targetLang}`; 64 | const response = await axios({ url, timeout: 15000 }); 65 | if (response.data && response.data.text && isArray(response.data.text)) { 66 | return response.data.text[0]; 67 | } 68 | throw new Error('Invalid response for translate request'); 69 | } catch (e) { 70 | throw new Error(`Could not translate to "${targetLang}": ${e}`); 71 | } 72 | } 73 | 74 | /** 75 | * Translates text with Google API 76 | * 77 | * @param text The text to translate 78 | * @param targetLang The target languate 79 | * @returns 80 | */ 81 | async function translateGoogle(text, targetLang) { 82 | try { 83 | 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`; 84 | const response = await axios({ url, timeout: 15000 }); 85 | if (isArray(response.data)) { 86 | // we got a valid response 87 | return response.data[0][0][0]; 88 | } 89 | throw new Error('Invalid response for translate request'); 90 | } catch (e) { 91 | if (e.response && e.response.status === 429) { 92 | throw new Error(`Could not translate to "${targetLang}": Rate-limited by Google Translate`); 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 | -------------------------------------------------------------------------------- /.github/workflows/test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: Test and Release 2 | 3 | # Run this job on all pushes and pull requests 4 | # as well as tags with a semantic version 5 | on: 6 | push: 7 | branches: 8 | - 'master' 9 | tags: 10 | # normal versions 11 | - 'v[0-9]+.[0-9]+.[0-9]+' 12 | # pre-releases 13 | - 'v[0-9]+.[0-9]+.[0-9]+-**' 14 | pull_request: {} 15 | 16 | # Cancel previous PR/branch runs when a new commit is pushed 17 | concurrency: 18 | group: ${{ github.ref }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | # Performs quick checks before the expensive test runs 23 | check-and-lint: 24 | if: contains(github.event.head_commit.message, '[skip ci]') == false 25 | 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - uses: ioBroker/testing-action-check@v1 30 | with: 31 | node-version: '22.x' 32 | # Uncomment the following line if your adapter cannot be installed using 'npm ci' 33 | # install-command: 'npm install' 34 | lint: true 35 | 36 | # Runs adapter tests on all supported node versions and OSes 37 | adapter-tests: 38 | needs: [check-and-lint] 39 | 40 | if: contains(github.event.head_commit.message, '[skip ci]') == false 41 | 42 | runs-on: ${{ matrix.os }} 43 | strategy: 44 | matrix: 45 | node-version: [20.x, 22.x, 24.x] 46 | os: [ubuntu-latest, windows-latest, macos-latest] 47 | 48 | steps: 49 | - uses: ioBroker/testing-action-adapter@v1 50 | with: 51 | node-version: ${{ matrix.node-version }} 52 | os: ${{ matrix.os }} 53 | # Uncomment the following line if your adapter cannot be installed using 'npm ci' 54 | # install-command: 'npm install' 55 | 56 | # TODO: To enable automatic npm releases, create a token on npmjs.org 57 | # Enter this token as a GitHub secret (with name NPM_TOKEN) in the repository options 58 | # Then uncomment the following block: 59 | 60 | # Deploys the final package to NPM 61 | deploy: 62 | needs: [check-and-lint, adapter-tests] 63 | 64 | # Trigger this step only when a commit on any branch is tagged with a version number 65 | if: | 66 | contains(github.event.head_commit.message, '[skip ci]') == false && 67 | github.event_name == 'push' && 68 | startsWith(github.ref, 'refs/tags/v') 69 | 70 | runs-on: ubuntu-latest 71 | 72 | # Write permissions are required to create Github releases 73 | permissions: 74 | contents: write 75 | id-token: write 76 | 77 | steps: 78 | - uses: ioBroker/testing-action-deploy@v1 79 | with: 80 | node-version: '22.x' 81 | # Uncomment the following line if your adapter cannot be installed using 'npm ci' 82 | # install-command: 'npm install' 83 | # npm-token: ${{ secrets.NPM_TOKEN }} # Commented out for migration to Trusted Publishing 84 | github-token: ${{ secrets.GITHUB_TOKEN }} 85 | 86 | # When using Sentry for error reporting, Sentry can be informed about new releases 87 | # To enable create a API-Token in Sentry (User settings, API keys) 88 | # Enter this token as a GitHub secret (with name SENTRY_AUTH_TOKEN) in the repository options 89 | # Then uncomment and customize the following block: 90 | #sentry: true 91 | #sentry-token: ${{ secrets.SENTRY_AUTH_TOKEN }} 92 | #sentry-url: "https://sentry.io" 93 | #sentry-org: "mcm4iob" 94 | #sentry-project: "iobroker-pid" 95 | #sentry-version-prefix: "iobroker.pid" 96 | ## If your sentry project is linked to a GitHub repository, you can enable the following option 97 | #sentry-github-integration: true 98 | -------------------------------------------------------------------------------- /.github/workflows/check-copilot-template.yml: -------------------------------------------------------------------------------- 1 | # GitHub Action Template: Automated Version Check and Update for ioBroker Copilot Instructions 2 | # Version: 0.4.0 3 | # 4 | # This action automatically checks for template updates and creates issues when updates are available 5 | # Copy this to your repository as .github/workflows/check-copilot-template.yml 6 | 7 | name: Check ioBroker Copilot Template Version 8 | 9 | on: 10 | schedule: 11 | - cron: '23 3 * * 0' # Weekly check optimized for off-peak hours (3:23 AM UTC Sunday) 12 | workflow_dispatch: # Allow manual triggering 13 | 14 | jobs: 15 | check-template: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | issues: write 19 | contents: read 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Dynamic template version check 26 | id: version-check 27 | run: | 28 | echo "🔍 Starting dynamic ioBroker Copilot template version check..." 29 | 30 | # Get current version from local copilot instructions 31 | if [ -f ".github/copilot-instructions.md" ]; then 32 | CURRENT_VERSION=$(awk '/Version:|Template Version:/ {match($0, /([0-9]+(\.[0-9]+)*)/, arr); if (arr[1] != "") print arr[1]}' .github/copilot-instructions.md | head -1) 33 | if [ -z "$CURRENT_VERSION" ]; then CURRENT_VERSION="unknown"; fi 34 | echo "📋 Current local version: $CURRENT_VERSION" 35 | else 36 | CURRENT_VERSION="none" 37 | echo "❌ No .github/copilot-instructions.md file found" 38 | fi 39 | 40 | # Get latest version from centralized metadata 41 | echo "🌐 Fetching latest template version from centralized config..." 42 | LATEST_VERSION=$(curl -s https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/config/metadata.json | jq -r '.version' 2>/dev/null || echo "unknown") 43 | if [ -z "$LATEST_VERSION" ] || [ "$LATEST_VERSION" = "null" ]; then 44 | LATEST_VERSION="unknown" 45 | fi 46 | echo "📋 Latest available version: $LATEST_VERSION" 47 | 48 | # Determine repository status 49 | COPILOT_INITIALIZED="false" 50 | UPDATE_NEEDED="false" 51 | SETUP_NEEDED="false" 52 | 53 | if [ "$CURRENT_VERSION" = "none" ]; then 54 | SETUP_NEEDED="true" 55 | echo "🆕 Status: Setup needed - no copilot instructions found" 56 | elif [ "$CURRENT_VERSION" = "unknown" ] || [ "$LATEST_VERSION" = "unknown" ]; then 57 | echo "❓ Status: Cannot determine versions - manual check required" 58 | else 59 | # Compare versions (simple string comparison for now) 60 | if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then 61 | UPDATE_NEEDED="true" 62 | COPILOT_INITIALIZED="true" 63 | echo "📈 Status: Update needed - $CURRENT_VERSION → $LATEST_VERSION" 64 | else 65 | COPILOT_INITIALIZED="true" 66 | echo "✅ Status: Up to date - version $CURRENT_VERSION" 67 | fi 68 | fi 69 | 70 | # Set outputs for later steps 71 | echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT 72 | echo "latest_version=$LATEST_VERSION" >> $GITHUB_OUTPUT 73 | echo "copilot_initialized=$COPILOT_INITIALIZED" >> $GITHUB_OUTPUT 74 | echo "update_needed=$UPDATE_NEEDED" >> $GITHUB_OUTPUT 75 | echo "setup_needed=$SETUP_NEEDED" >> $GITHUB_OUTPUT 76 | 77 | - name: Check for existing issues 78 | id: issue-check 79 | uses: actions/github-script@v7 80 | with: 81 | script: | 82 | const { data: issues } = await github.rest.issues.listForRepo({ 83 | owner: context.repo.owner, 84 | repo: context.repo.repo, 85 | state: 'open', 86 | labels: 'copilot-setup,automation' 87 | }); 88 | 89 | const setupIssues = issues.filter(issue => 90 | issue.title.includes('Setup ioBroker') || 91 | issue.title.includes('Update ioBroker') || 92 | issue.title.includes('Copilot') 93 | ); 94 | 95 | core.setOutput('has_existing_issue', setupIssues.length > 0); 96 | core.setOutput('existing_count', setupIssues.length); 97 | 98 | if (setupIssues.length > 0) { 99 | console.log(`Found ${setupIssues.length} existing copilot setup/update issue(s)`); 100 | } 101 | 102 | - name: Create initial setup issue 103 | if: steps.version-check.outputs.setup_needed == 'true' && steps.issue-check.outputs.has_existing_issue == 'false' 104 | uses: actions/github-script@v7 105 | with: 106 | script: | 107 | const title = '🤖 Setup ioBroker GitHub Copilot Instructions'; 108 | const body = `# Automated ioBroker Copilot Instructions Setup 109 | 110 | **GitHub Copilot**: Please help me set up GitHub Copilot instructions for my ioBroker adapter repository with full automation and validation. 111 | 112 | ## 🚨 Important Notice - Ignore GitHub Copilot's Own Setup Suggestions 113 | 114 | **GitHub Copilot**: If you see suggestions from GitHub Copilot about setting up copilot instructions (like creating \`.github/copilot-instructions.md\` through GitHub's interface), **IGNORE THEM**. This issue handles the complete setup process with ioBroker-specific optimizations. 115 | 116 | ## Setup Process 117 | 118 | This repository needs initial setup of GitHub Copilot instructions. Please: 119 | 120 | 1. **Initialize with Adapter-Specific Context**: Create \`.github/copilot-instructions.md\` with basic ioBroker adapter context 121 | 2. **Enhance with Full Template**: Merge with comprehensive template from https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/template.md 122 | 3. **Customize Sections**: Populate [CUSTOMIZE] sections with adapter-specific content only 123 | 124 | **Template Version**: ${{ steps.version-check.outputs.latest_version }} 125 | **Detected Status**: No copilot instructions found 126 | 127 | ## Reference Templates 128 | 129 | - **Initial Setup Action**: https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/templates/ghAction-InitialSetup.yml 130 | - **Full Template**: https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/template.md 131 | - **Version Check Action**: https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/templates/ghAction-AutomatedVersionCheckAndUpdate.yml`; 132 | 133 | await github.rest.issues.create({ 134 | owner: context.repo.owner, 135 | repo: context.repo.repo, 136 | title: title, 137 | body: body, 138 | labels: ['copilot-setup', 'automation'] 139 | }); 140 | 141 | - name: Create update issue 142 | if: steps.version-check.outputs.update_needed == 'true' && steps.issue-check.outputs.has_existing_issue == 'false' 143 | uses: actions/github-script@v7 144 | with: 145 | script: | 146 | const title = '🤖 Update ioBroker Copilot Instructions Template'; 147 | const body = `# Automated ioBroker Copilot Template Update Request 148 | 149 | **GitHub Copilot**: Please help me validate and update my ioBroker adapter's Copilot instructions template while preserving all custom sections. 150 | 151 | ## Update Required 152 | 153 | - **Current Version**: ${{ steps.version-check.outputs.current_version }} 154 | - **Latest Version**: ${{ steps.version-check.outputs.latest_version }} 155 | - **Status**: Update needed 156 | 157 | ## Update Process 158 | 159 | Please merge the latest ioBroker template while preserving all customizations: 160 | 161 | \`\`\` 162 | Merge the ioBroker template from https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/template.md with my existing .github/copilot-instructions.md. 163 | 164 | CRITICAL REQUIREMENTS: 165 | 1. Preserve ALL [CUSTOMIZE] sections and their content exactly as they are 166 | 2. Maintain any project-specific context not already covered in the template 167 | 3. Add the latest ioBroker best practices from the new template 168 | 4. Update the version number to ${{ steps.version-check.outputs.latest_version }} 169 | 5. Keep the Template Source reference up-to-date 170 | 6. Ensure no custom content is lost during the merge 171 | 7. REMOVE any duplicate content from [CUSTOMIZE] sections that already exists in the standard template 172 | \`\`\` 173 | 174 | ## Reference Templates 175 | 176 | - **Latest Template**: https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/template.md 177 | - **Update Template**: https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/templates/automated-template-update.md 178 | - **Version Check Action**: https://raw.githubusercontent.com/DrozmotiX/ioBroker-Copilot-Instructions/main/templates/ghAction-AutomatedVersionCheckAndUpdate.yml`; 179 | 180 | await github.rest.issues.create({ 181 | owner: context.repo.owner, 182 | repo: context.repo.repo, 183 | title: title, 184 | body: body, 185 | labels: ['template-update', 'automation'] 186 | }); 187 | 188 | - name: Summary 189 | run: | 190 | echo "🎯 Template Version Check Complete" 191 | echo " Current: ${{ steps.version-check.outputs.current_version }}" 192 | echo " Latest: ${{ steps.version-check.outputs.latest_version }}" 193 | echo " Setup needed: ${{ steps.version-check.outputs.setup_needed }}" 194 | echo " Update needed: ${{ steps.version-check.outputs.update_needed }}" 195 | echo " Existing issues: ${{ steps.issue-check.outputs.existing_count }}" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](admin/wolf-smartset.png) 2 | # ioBroker.wolf-smartset 3 | 4 | [![NPM version](http://img.shields.io/npm/v/iobroker.wolf-smartset.svg)](https://www.npmjs.com/package/iobroker.wolf-smartset) 5 | [![Downloads](https://img.shields.io/npm/dm/iobroker.wolf-smartset.svg)](https://www.npmjs.com/package/iobroker.wolf-smartset) 6 | ![Number of Installations (latest)](http://iobroker.live/badges/wolf-smartset-installed.svg) 7 | [![Dependency Status](https://img.shields.io/david/iobroker-community-adapters/iobroker.wolf-smartset.svg)](https://david-dm.org/iobroker-community-adapters/iobroker.wolf-smartset) 8 | [![Known Vulnerabilities](https://snyk.io/test/github/iobroker-community-adapters/ioBroker.wolf-smartset/badge.svg)](https://snyk.io/test/github/iobroker-community-adapters/ioBroker.wolf-smartset) 9 | ![Test and Release](https://github.com/iobroker-community-adapters/ioBroker.wolf-smartset/workflows/Test%20and%20Release/badge.svg) 10 | 11 | [![NPM](https://nodei.co/npm/iobroker.wolf-smartset.png?downloads=true)](https://nodei.co/npm/iobroker.wolf-smartset/) 12 | 13 | ## wolf-smartset adapter for ioBroker 14 | Connect your Wolf Heating to iobroker. 15 | 16 | This adapter connects to the the Wolf Smartset server (https://wolf-smartset.com) to monitor and manage your Wolf heating device. This is not a local connection. The benefit is that you can use the Wolf Smartset app or [Wolf Smartset Portal](https://wolf-smartset.com) and also receive or set param values in ioBroker in parallel. 17 | 18 | ## Requirements 19 | 20 | You need a Wolf heating/climate device equipped with a ISM7i WLAN/LAN module (aka Link Home) connected to the Wolf Smartset server and a Wolf Smartset account authorized for your device. 21 | 22 | ## Adapter Instance Settings 23 | 24 | ### Tab: Main Settings 25 | 26 | #### Wolf Smartset Account 27 | To connect to the Wolf Smartset server you need your 28 | - `Username` and 29 | - `Password` 30 | 31 | which you also use to log in to the Wolf Smartset app or the [Wolf Smartset Portal](https://wolf-smartset.com). 32 | 33 | #### Wolf Device 34 | 35 | Your Wolf account is associated with one ore more Wolf devices. Each device requires an individual ioBroker adapter instance. 36 | 37 | After first-time install you have to select a specific 38 | - `Device` for each instance. 39 | 40 | As soon as you entered a valid `Username` and `Password` the 41 | - `List of Wolf Devices` will be filled with the devices assigned to your account. 42 | 43 | After selecting the device from the list, click on 44 | - `USE THIS DEVICE` to confirm your selection. 45 | 46 | ### Tab: Advanced Settings 47 | 48 | Advanced settings allow you to adapt the adpater's operation to your needs. Typically, you can leave all advanced settings on their default. 49 | 50 | #### Poll Cycle Intervals and Parameter Lists 51 | 52 | The adapter will - after connecting to the Wolf Smartset server - periodically poll parameter values from the server. 53 | - `Poll all Parameters`: the adapter will always poll all parameters found on the server. This poll strategy is backward compatible with adapter version 1.x 54 | 55 | The adapter also supports a more sophisticated poll strategy based on two independent poll cycles with different cycle intervals. 56 | - `Short Poll Cycle Interval`: enter the interval in __seconds__. The Wolf Smartset server defines an absolute minimum poll interval (currently 60 sec) which you should not undercut. If you configure a value below this minimum interval the server will not respond in the expected way or may even disconnect your session. The adapter requests the current minimum poll interval from the server periodically. If the configured poll interval is below the minimum poll interval indicated by the server, you will get a warning log from the adapter and you should adjust your poll interval accordingly. 57 | - `Long Poll Cycle Interval`: enter the interval in __minutes__ for the second poll cycle. 58 | 59 | The Wolf Smartset server groups the various device parameters into different bundles, identified by a numeric BundleId. In the __ioBroker Admin__ UI you will find the BundleIds for the different parameter groups in the __Object__ view below the __wolf-smartset__ instance at the channel level. 60 | 61 | - `Parameters of Bundle`: Within this table you can define which parameter value group should be polled in which poll cycle. It is a good idea to: 62 | - `Include in Short Poll Cycle` all fast changing parameter values (e.g. operational states) and to 63 | - `Include in Long Poll Cycle` rarely changing parameter values (e.g. device configuration parameters). 64 | 65 | The Wolf Smartset API requires each poll request to include - besides a list of parameters to poll - also a BundleId. It's not quite clear how the BundleId relates to the actual parameter list, but in most cases 'Default' should be OK: it maps to the largest selected BundleId for the given poll cycle. Any other setting here is for experimental use. Configure the BundleId to be used as: 66 | - `BundleId for Short Poll Cycle` 67 | - `BundleId for Long Poll Cycle` 68 | 69 | If you configured `Poll all Parameters`, the BundleId used in the poll requests is set to 1000. This will likely exclude some Expert parameters (see below) from the result. So, if you intend to poll Expert parameters, you should probably not use `Poll all Parameters`. 70 | 71 | #### Expert Login 72 | 73 | Wolf Smartset API defines two access levels for device parameters: __User__ and __Expert__. Accordingly, you will find in the __Object__ view of the __ioBroker Admin__ UI the corresponding two subtrees: __Benutzer__ and __Fachmann__. Afer initial authentication the adapter is in User mode and will receive only once during initialization all available parameter values. After that, during periodic polls it will receive only updates for User level parameter values (i.e. value in the __Benutzer__ tree). 74 | 75 | If you check 76 | - `Do Expert Login` and enter the correct 77 | - `Expert Password`, 78 | 79 | the adapter will perform an Expert Login during initialization and receive also periodic updates of expert level parameter values (as shown in the __Fachmann__ tree) during the poll cylce they are associated with. 80 | 81 | __!!! Important Note on Expert Level: Start !!!__ 82 | 83 | Expert level seems to be behave like Pandora's box! Tests showed, that it was quite hard to leave the Expert level once you enabled it. Although the adapter will completely logout and remove all locally cached authentication data (openId tokens and session id) when disabling `Do Expert Login` setting and restarting the instance, it looks like this is not good enough for the Wolf Smartset server. 84 | 85 | ``` 86 | In fact, only a change of the adapter's public IP address in combination with an adapter instance reload might get the adapter back to User level. 87 | ``` 88 | 89 | While it doesn't look too problematic to stay in Expert mode at first sight, there is at least one side effect that might be a real issue for you: 90 | 91 | ``` 92 | In Expert mode some pre-period statistics might not be updated reliably from Wolf Smartset server! 93 | ``` 94 | 95 | This affects in particular the following ParameterIds and probably also other ones: 96 | ``` 97 | - wolf-smartset.0.Benutzer.Heizung.212_Statistik_Wärmeerzeuger 1.27017500001 98 | - wolf-smartset.0.Benutzer.Heizung.212_Statistik_Wärmeerzeuger 1.27017600001 99 | - wolf-smartset.0.Benutzer.Heizung.212_Statistik_Wärmeerzeuger 1.27017700001 100 | ``` 101 | 102 | So, if you rely on a constant and precise delivery of such pre-period statistic values, you should think twice whether to check `Do Expert Login`. Don't complain, if you are in trouble to get back to User level, you have been warned! 103 | 104 | __!!! Important Note on Expert Level: End !!!__ 105 | 106 | #### Check for Public IP Changes 107 | 108 | The Wolf Smartset server is Client IP address aware. This means, it associates some application state information with the public IP address of the client application. So, if you configured `Do Expert Login` and the adapter's public IP changes (e.g. after a router reload), the adapter will have to re-authenticate to the Wolf Smartset server in order to enable the Expert mode again. Since the adapter will do a re-authentication only every hour, it may take up to __one hour until the adapter is in Expert mode again__. 109 | 110 | If this is too long for you, you can check 111 | - `Enable Public IP Checking`: In this case, the adapter will check your public IP address via [ipify.org](https://ipify.org) __every 4th Short Poll Cycle__ and will trigger re-authentication on change. That way, the adapter will be back in Expert mode __the latest after 4 Short Poll Cycles__. 112 | 113 | #### API Profiling 114 | 115 | API Profiling allows you to track the Wolf Smartset API usage of the adapter. if you 116 | - `Enable API Profiling`, the adaptert will update the following objects in the __adapter instance object tree__ for each poll request: 117 | - info_api 118 | - poll_req_bundle_id: the BundleId used in the poll request 119 | - poll_req_num_params: the number of parameters requested by the adapter 120 | - poll_resp_num_params: the number of parameters returned from the server 121 | - poll_resp_num_params: the number of parameters values returned from the server (returned parameters may or may not have an associated value) 122 | 123 | ## Changelog 124 | 128 | ### **WORK IN PROGRESS** 129 | - (mcm1957) Dependencies have been updated. 130 | 131 | ### 2.1.2 (2025-08-14) 132 | - (mcm1957) Adapter requires admin 7.6.17 now. 133 | - (mcm1957) Dependencies have been updated. 134 | 135 | ### 2.1.1 (2025-08-05) 136 | - (mcm1957) Dependencies have been updated. 137 | 138 | ### 2.1.0 (2025-08-05) 139 | - (flingo64) Change: Log periodic message '_refreshAuthToken(): ERROR ...' with level info 140 | - (flingo64) Bugfix (#458): set instance state to connected only if initialization went fine 141 | - (flingo64) Bugfix: if configured BundleId for poll requests is not available on server, use default BundleId 142 | - (flingo64) Enhancement: option 'Poll all Parameters' implements backward compatible poll strategy 143 | - (flingo64) Enhancement(#459, #465): added more BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) for AdminUI as found on different Wolf device configurations 144 | 145 | ### 2.0.1 (2025-04-18) 146 | - (flingo64) Bugfix: fixed various typos in Readme and translations 147 | - (flingo64) Bugfix: Fixed an AdminUI issue (#450 - 'No device selected') when the device information contained line break (e.g. in ContactInformation, Description or Comment ) 148 | - (flingo64) Enhancement for AdminUI: support for more than one device in list of devices returned from Wolf Smartset server 149 | 150 | ### 2.0.0 (2025-04-02) 151 | - (flingo64) BREAKING CHANGE: Please reenter your login credentials. 152 | - (mcm1957) Adapter requires node.js 20, js-controller 6 and admin 7 now. 153 | - (flingo64) A general code cleanup and partial rewrite has been done. 154 | - (flingo64) Trigger re-initalization has been added, if api returns an error (server might be down temporarily). 155 | - (flingo64) Expert login and periodic re-login have been added (#242). 156 | - (flingo64) Support for level 3 objects `time programs` / `party mode` / `vacation mode` has been added. 157 | - (flingo64) Request UserInfo from Wolf server, check whether adapter instance's poll interval meets requirements (60 sec) added. 158 | - (flingo64) ParameterId lists for each Wolf BundleId created and show `BundleIds` for each channel added 159 | - (flingo64) Support for two sepearate poll cycles to avoid server abuse reactions has been added. 160 | - (flingo64) Switched AdminUI to `jsconConfig`. 161 | 162 | ## License 163 | MIT License 164 | 165 | Copyright (c) 2024-2025 iobroker-community-adapters 166 | Copyright (c) 2021-2023 MeisterTR 167 | 168 | Permission is hereby granted, free of charge, to any person obtaining a copy 169 | of this software and associated documentation files (the "Software"), to deal 170 | in the Software without restriction, including without limitation the rights 171 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 172 | copies of the Software, and to permit persons to whom the Software is 173 | furnished to do so, subject to the following conditions: 174 | 175 | The above copyright notice and this permission notice shall be included in all 176 | copies or substantial portions of the Software. 177 | 178 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 179 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 180 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 181 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 182 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 183 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 184 | SOFTWARE. 185 | -------------------------------------------------------------------------------- /admin/jsonConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "i18n": true, 3 | "type": "tabs", 4 | "tabsStyle": { 5 | "width": "calc(100% - 100px)" 6 | }, 7 | "items": { 8 | "tab_1": { 9 | "type": "panel", 10 | "label": "Main Settings", 11 | "items": { 12 | "accountHdr": { 13 | "newLine": true, 14 | "type": "header", 15 | "text": "Wolf Smartset Account", 16 | "size": 2 17 | }, 18 | "accountTxt": { 19 | "type": "staticText", 20 | "text": "Enter username and password of your Wolf Smartset account", 21 | "newLine": true, 22 | "xs": 12, 23 | "sm": 12, 24 | "md": 12, 25 | "lg": 4, 26 | "xl": 4 27 | }, 28 | "username": { 29 | "type": "text", 30 | "label": "Username", 31 | "newLine": true, 32 | "xs": 12, 33 | "sm": 6, 34 | "md": 4, 35 | "lg": 3, 36 | "xl": 2 37 | }, 38 | "password": { 39 | "type": "password", 40 | "label": "Password", 41 | "repeat": true, 42 | "visible": true, 43 | "newLine": true, 44 | "xs": 12, 45 | "sm": 12, 46 | "md": 8, 47 | "lg": 6, 48 | "xl": 4 49 | }, 50 | "deviceHeader": { 51 | "type": "header", 52 | "text": "Wolf Device", 53 | "size": 2 54 | }, 55 | "deviceName": { 56 | "type": "text", 57 | "label": "Device", 58 | "newLine": true, 59 | "readOnly": true, 60 | "xs": 12, 61 | "sm": 6, 62 | "md": 4, 63 | "lg": 3, 64 | "xl": 2 65 | }, 66 | "device": { 67 | "type": "text", 68 | "label": "Device Info", 69 | "newLine": false, 70 | "readOnly": true, 71 | "hidden": "true", 72 | "xs": 12, 73 | "sm": 6, 74 | "md": 4, 75 | "lg": 3, 76 | "xl": 2 77 | }, 78 | "deviceDivider": { 79 | "type": "divider", 80 | "newLine": true, 81 | "xs": 12, 82 | "sm": 12, 83 | "md": 6, 84 | "lg": 4, 85 | "xl": 4 86 | }, 87 | "deviceTxt": { 88 | "type": "staticText", 89 | "text": "Select a different Wolf device for this adapter instance", 90 | "newLine": true, 91 | "xs": 12, 92 | "sm": 6, 93 | "md": 4, 94 | "lg": 3, 95 | "xl": 2 96 | }, 97 | "deviceSelect": { 98 | "type": "selectSendTo", 99 | "label": "List of Wolf Devices", 100 | "command": "getDeviceList", 101 | "jsonData": "{ \"username\": \"${data.username}\", \"password\": \"${data.password}\" }", 102 | "newLine": true, 103 | "manual": false, 104 | "alsoDependsOn": [ 105 | "username", 106 | "password" 107 | ], 108 | "noTranslation": true, 109 | "xs": 12, 110 | "sm": 6, 111 | "md": 4, 112 | "lg": 3, 113 | "xl": 2 114 | }, 115 | "deviceConfirm": { 116 | "type": "sendTo", 117 | "label": "Use this Device", 118 | "jsonData": "{ \"deviceObject\": \"${data.deviceSelect}\" }", 119 | "command": "confirmDevice", 120 | "useNative": true, 121 | "variant": "outlined", 122 | "xs": 12, 123 | "sm": 6, 124 | "md": 4, 125 | "lg": 3, 126 | "xl": 2 127 | } 128 | } 129 | }, 130 | "tab_2": { 131 | "type": "panel", 132 | "label": "Advanced Settings", 133 | "items": { 134 | "pollHdr": { 135 | "newLine": true, 136 | "type": "header", 137 | "text": "Poll Cycle Intervals and Parameter Lists", 138 | "size": 2 139 | }, 140 | "doPollAllParams": { 141 | "type": "checkbox", 142 | "label": "Poll all Parameters", 143 | "tooltip": "This poll strategy is backward compatible with adpater version 1.x", 144 | "default": true, 145 | "newLine": true, 146 | "xs": 12, 147 | "sm": 6, 148 | "md": 4, 149 | "lg": 3, 150 | "xl": 2 151 | }, 152 | "pollTxt": { 153 | "type": "staticText", 154 | "text": "Configure poll intervals and the set of parameters to poll", 155 | "newLine": true, 156 | "xs": 12, 157 | "sm": 12, 158 | "md": 12, 159 | "lg": 6, 160 | "xl": 6 161 | }, 162 | "pollIntervalTxt": { 163 | "type": "staticText", 164 | "text": "Configure Short and Long Poll Cycle Interval", 165 | "newLine": true, 166 | "xs": 12, 167 | "sm": 12, 168 | "md": 4, 169 | "lg": 3, 170 | "xl": 2 171 | }, 172 | "pollIntervalShort": { 173 | "type": "number", 174 | "label": "Short Poll Cycle Interval (sec)", 175 | "min": 60, 176 | "step": 15, 177 | "xs": 12, 178 | "sm": 12, 179 | "md": 4, 180 | "lg": 3, 181 | "xl": 2 182 | }, 183 | "pollIntervalLong": { 184 | "hidden": "data.doPollAllParams === true", 185 | "type": "number", 186 | "label": "Long Poll Cycle Interval (min)", 187 | "min": 15, 188 | "step": 15, 189 | "xs": 12, 190 | "sm": 12, 191 | "md": 4, 192 | "lg": 3, 193 | "xl": 2 194 | }, 195 | "bundleIdTable": { 196 | "type": "table", 197 | "newLine": true, 198 | "xs": 12, 199 | "sm": 12, 200 | "md": 12, 201 | "lg": 9, 202 | "xl": 6, 203 | "label": "", 204 | "noDelete": true, 205 | "hidden": "data.doPollAllParams === true", 206 | "items": [ 207 | { 208 | "type": "text", 209 | "attr": "bundleIdName", 210 | "title": "Parameters of Bundle", 211 | "tooltip": "BundleId of Parameters from Wolf SmartSet API", 212 | "filter": false, 213 | "sort": false, 214 | "readOnly": true, 215 | "default": "" 216 | }, 217 | { 218 | "type": "checkbox", 219 | "attr": "bundleIdUseShort", 220 | "title": "Include in Short Poll Cycle", 221 | "tooltip": "Request Params with this BundleId in Short Poll Cylce", 222 | "filter": false, 223 | "sort": false, 224 | "default": false 225 | }, 226 | { 227 | "type": "checkbox", 228 | "attr": "bundleIdUseLong", 229 | "title": "Include in Long Poll Cycle", 230 | "tooltip": "Request Params with this BundleId in Long Poll Cycle", 231 | "filter": false, 232 | "sort": false, 233 | "default": false 234 | } 235 | ] 236 | }, 237 | "bundleIdRequestedText": { 238 | "hidden": "data.doPollAllParams === true", 239 | "type": "staticText", 240 | "text": "Set the BundleId to be used in poll requests", 241 | "newLine": true, 242 | "xs": 12, 243 | "sm": 12, 244 | "md": 4, 245 | "lg": 3, 246 | "xl": 2 247 | }, 248 | "bundleIdRequestedShort": { 249 | "hidden": "data.doPollAllParams === true", 250 | "type": "autocomplete", 251 | "label": "BundleId for Short Poll Cycle", 252 | "options": [ 253 | "Default", 254 | "1000", 255 | "3100", 256 | "3500", 257 | "4300", 258 | "6300", 259 | "6500", 260 | "7100", 261 | "7300", 262 | "7600", 263 | "8000", 264 | "8100", 265 | "8200", 266 | "10000", 267 | "10700", 268 | "14000", 269 | "14700", 270 | "15600", 271 | "15700", 272 | "15800" 273 | ], 274 | "freeSolo": true, 275 | "default": "Default", 276 | "xs": 12, 277 | "sm": 12, 278 | "md": 4, 279 | "lg": 3, 280 | "xl": 2 281 | }, 282 | "bundleIdRequestedLong": { 283 | "hidden": "data.doPollAllParams === true", 284 | "type": "autocomplete", 285 | "label": "BundleId for Long Poll Cycle", 286 | "options": [ 287 | "Default", 288 | "1000", 289 | "3100", 290 | "3500", 291 | "4300", 292 | "6300", 293 | "6500", 294 | "7100", 295 | "7300", 296 | "7600", 297 | "8000", 298 | "8100", 299 | "8200", 300 | "10000", 301 | "10700", 302 | "14000", 303 | "14700", 304 | "15600", 305 | "15700", 306 | "15800" 307 | ], 308 | "freeSolo": true, 309 | "default": "Default", 310 | "xs": 12, 311 | "sm": 12, 312 | "md": 4, 313 | "lg": 3, 314 | "xl": 2 315 | }, 316 | "expertHdr": { 317 | "newLine": true, 318 | "type": "header", 319 | "text": "Expert Login", 320 | "size": 2 321 | }, 322 | "expertTxt": { 323 | "type": "staticText", 324 | "text": "Expert Login is required to poll expert parameters", 325 | "newLine": true, 326 | "xs": 12, 327 | "sm": 12, 328 | "md": 12, 329 | "lg": 6, 330 | "xl": 6 331 | }, 332 | "doExpertLogin": { 333 | "type": "checkbox", 334 | "label": "Do Expert Login", 335 | "default": false, 336 | "newLine": true, 337 | "xs": 12, 338 | "sm": 6, 339 | "md": 4, 340 | "lg": 3, 341 | "xl": 2 342 | }, 343 | "expertPassword": { 344 | "type": "password", 345 | "label": "Expert Password", 346 | "visible": true, 347 | "disabled": "data.doExpertLogin === false", 348 | "xs": 12, 349 | "sm": 6, 350 | "md": 4, 351 | "lg": 3, 352 | "xl": 2 353 | }, 354 | "pubIpCheckHdr": { 355 | "newLine": true, 356 | "type": "header", 357 | "text": "Check for Public IP Changes", 358 | "size": 2 359 | }, 360 | "pubIpCheckTxt": { 361 | "type": "staticText", 362 | "text": "Enable checking your public IP via ipify.org for faster session recovery after changed public IP", 363 | "newLine": true, 364 | "xs": 12, 365 | "sm": 12, 366 | "md": 12, 367 | "lg": 6, 368 | "xl": 6 369 | }, 370 | "doPubIpCheck": { 371 | "type": "checkbox", 372 | "label": "Enable Public IP Checking", 373 | "default": false, 374 | "newLine": true, 375 | "xs": 12, 376 | "sm": 6, 377 | "md": 4, 378 | "lg": 3, 379 | "xl": 2 380 | }, 381 | "profilingHdr": { 382 | "newLine": true, 383 | "type": "header", 384 | "text": "API Profiling", 385 | "size": 2 386 | }, 387 | "profilingTxt": { 388 | "type": "staticText", 389 | "text": "Enable API profiling to track number of parameters requested and returned", 390 | "newLine": true, 391 | "xs": 12, 392 | "sm": 12, 393 | "md": 12, 394 | "lg": 6, 395 | "xl": 6 396 | }, 397 | "doApiProfile": { 398 | "type": "checkbox", 399 | "label": "Enable API Profiling", 400 | "default": false, 401 | "newLine": true, 402 | "xs": 12, 403 | "sm": 6, 404 | "md": 4, 405 | "lg": 3, 406 | "xl": 2 407 | } 408 | } 409 | } 410 | } 411 | } -------------------------------------------------------------------------------- /io-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "name": "wolf-smartset", 4 | "version": "2.1.2", 5 | "news": { 6 | "2.1.2": { 7 | "en": "Adapter requires admin 7.6.17 now.\nDependencies have been updated.", 8 | "de": "Adapter benötigt jetzt Admin 7.6.17.\nAbhängigkeiten wurden aktualisiert.", 9 | "ru": "Адаптер требует админ 7.6.17.\nЗависимости были обновлены.", 10 | "pt": "Adaptador requer admin 7.6.17 agora.\nAs dependências foram atualizadas.", 11 | "nl": "Adapter vereist admin 7.6.17 nu.\nAfhankelijkheden zijn bijgewerkt.", 12 | "fr": "Adaptateur nécessite admin 7.6.17 maintenant.\nLes dépendances ont été actualisées.", 13 | "it": "L'adattatore richiede ora admin 7.6.17.\nLe dipendenze sono state aggiornate.", 14 | "es": "Adaptador requiere admin 7.6.17 ahora.\nSe han actualizado las dependencias.", 15 | "pl": "Adapter wymaga admin 7.6.17 teraz.\nZaktualizowano zależność.", 16 | "uk": "Адаптер вимагає адміністратора 7.6.17 тепер.\nОновлено залежність.", 17 | "zh-cn": "适配器现在需要管理员7.6.17.\n附属关系已经更新." 18 | }, 19 | "2.1.1": { 20 | "en": "Dependencies have been updated.", 21 | "de": "Abhängigkeiten wurden aktualisiert.", 22 | "ru": "Зависимости были обновлены.", 23 | "pt": "As dependências foram atualizadas.", 24 | "nl": "Afhankelijkheden zijn bijgewerkt.", 25 | "fr": "Les dépendances ont été actualisées.", 26 | "it": "Le dipendenze sono state aggiornate.", 27 | "es": "Se han actualizado las dependencias.", 28 | "pl": "Zaktualizowano zależność.", 29 | "uk": "Оновлено залежність.", 30 | "zh-cn": "附属关系已经更新." 31 | }, 32 | "2.1.0": { 33 | "en": "Change: Log periodic message '_refreshAuthToken(): ERROR ...' with level info\nBugfix (#458): set instance state to connected only if initialization went fine\nBugfix: if configured BundleId for poll requests is not available on server, use default BundleId\nEnhancement: option 'Poll all Parameters' implements backward compatible poll strategy\nEnhancement(#459, #465): added more BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) for AdminUI as found on different Wolf device configurations", 34 | "de": "Änderung: Log Periodische Meldung '_refreshAuthToken(): ERROR ...' mit Level-Info\nBugfix (#458): Instanzzustand wird nur gesetzt, wenn die Initialisierung in Ordnung ging\nBugfix: Falls die konfigurierte Bundle Id für Umfrageanfragen nicht auf dem Server verfügbar ist, wird die Standard BundleId verwendet\nEnhancement: Option 'Poll all Parameters' implementiert für rückwärtskompatible Umfragestrategie\nEnhancement(#459, #465): mehr BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) für AdminUI implementiert die auf verschiedenen Wolf-Gerätekonfigurationen gefunden wurden", 35 | "ru": "Изменение: Лог периодического сообщения '_refreshAuthToken(): ERROR... с информацией об уровне\nBugfix (#458): установите состояние экземпляра на соединение, только если инициализация прошла нормально\nBugfix: если настроен Bundle Id для запросов на опрос недоступен на сервере, используйте BundleId по умолчанию\nОпция «Опросить все параметры» реализует обратно совместимую стратегию опроса\nУлучшение (#459, #465): добавлено больше BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) для AdminUI в различных конфигурациях устройств Wolf", 36 | "pt": "Alteração: Log period message '_refreshAuthToken(): ERRO ...' com informações de nível\nBugfix (# 458): defina o estado da instância para estar conectado apenas se a inicialização for boa\nCorrecção de Erros: se o pacote estiver configurado Id para solicitações de votação não está disponível no servidor, use o BundleId padrão\nMelhoramento: a opção 'Poll all Parâmetros' implementa uma estratégia de pesquisa para trás compatível\nEnhancement(#459, #465): adicionou mais BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) para AdminUI como encontrado em diferentes configurações de dispositivos Wolf", 37 | "nl": "Wijzigen: periodiek logbericht '_refreshAuthToken(): FOUT ...\" met niveau info\nBugfix (#458): de status van instantie alleen instellen als de initialisatie goed ging\nBugfix: indien ingesteld Id voor pollverzoeken is niet beschikbaar op de server, gebruik standaard BundleId\nVerbetering: optie 'Poll all Parameters' implementeert achterwaartse compatibele pollstrategie\nEnhancement(#459, #465): toegevoegd meer BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) voor AdminUI zoals gevonden op verschillende Wolf apparaat configuraties", 38 | "fr": "Changement: Loger le message périodique '_refreshAuthToken(): ERROR ...' avec informations de niveau\nBugfix (#458) : définir l'état de l'instance à connecter seulement si l'initialisation s'est bien passée\nBugfix : si configuré Bundle Id pour les requêtes de sondage n'est pas disponible sur le serveur, utiliser BundleId par défaut\nAmélioration: l'option 'Poll all Parameters' implémente une stratégie de sondage rétrocompatible\nAmélioration(#459, #465): ajouté plus BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) pour AdminUI comme sur différentes configurations d'appareils Wolf", 39 | "it": "Modifica: Log messaggio periodico '_refreshAuthToken(): ERROR...' con informazioni di livello\nBugfix (#458): imposta lo stato delle istanze da collegare solo se l'inizializzazione è andata bene\nBugfix: se configurato Bundle Id per richieste di sondaggio non è disponibile sul server, utilizzare default BundleId\nMiglioramento: opzione 'Poll all Parameters' implementa la strategia di sondaggio compatibile retro\nEnhancement(#459, #465): aggiunto più BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) per AdminUI come trovato su diverse configurazioni di dispositivi Wolf", 40 | "es": "Cambio: Log periodic message '_refreshAuthToken(): ERROR ...' con información de nivel\nBugfix (#458): estado de instancia establecido para conectar sólo si la inicialización fue bien\nBugfix: si está configurado Bundle Id para solicitudes de encuesta no está disponible en servidor, utilice BundleId predeterminado\nMejora: opción 'Poll all Parameters' implementa la estrategia de votación compatible atrasada\nMejora(#459, #465): añadido más BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) para AdminUI como se encuentra en diferentes configuraciones de dispositivos Wolf", 41 | "pl": "Zmiana: Zaloguj okresową wiadomość '_ reverse AuthToken (): ERROR \"... z informacjami o poziomie\nBugfix (# 458): ustaw stan instancji do połączenia tylko wtedy, gdy inicjalizacja poszła dobrze\nBugfix: jeśli skonfigurowany Id dla żądań ankietowych nie jest dostępny na serwerze, użyj domyślnego BundleId\nWzmocnienie: opcja \"Poll all Parametry\" implementuje wsteczną strategię ankietową\nWzmacnianie (# 459, # 465): dodano więcej BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) dla AdminUI w różnych konfiguracjach urządzeń Wolf", 42 | "uk": "Зміна: Журнал періодичне повідомлення '_refreshAuthToken(): ERROR ...' з інформацією про рівень\nВиправлення помилок (#458)\nВиправлення помилок: якщо налаштувати Bundle Id для запитів на опитування не доступний на сервері, використовуйте за замовчуванням BundleId\nПідвищення: опція «Полл всі параметри» реалізує стратегію зворотного зв'язку\nРозширення(#459, #465): додано більше BundleIds (4300, 10000, 10700, 14000, 14700, 15600, 15700, 15800) для AdminUI, як показано на різних налаштуваннях пристроїв Wolf", 43 | "zh-cn": "更改: 日志周期消息“ _ refreshAuthToken (): 错误...' 有级别信息\n错误fix (# 458): 仅在初始化完好的情况下, 才将实例状态设置为连接\n错误修正: 如果配置了套装 无法在服务器上获取民调请求的 Id, 使用默认的 BundleId\n增强: 选项“ 所有参数” 执行反向兼容的民调策略\n增强(# 459,# 465):在不同的Wolf设备配置上为AdminUI增加了更多的BundleIds(4300,1000,10700,14000,14700,14700,15600,15700,15800)" 44 | }, 45 | "2.0.1": { 46 | "en": "Bugfix: fixed various typos in Readme and translations\nBugfix: Fixed an AdminUI issue (#450 - 'No device selected') when the device information contained line break (e.g. in ContactInformation, Description or Comment )\nEnhancement for AdminUI: support for more than one device in list of devices returned from Wolf Smartset server", 47 | "de": "Bugfix: verschiedene Tippfehler in Readme und Übersetzungen behoben\nBugfix: AdminUI-Problem behoben (#450 - 'No device select') wenn die Geräteinformationen einen Zeilenumbruch enthalten (z.B. in ContactInformation, Beschreibung oder Kommentar)\nVerbesserung für AdminUI: Unterstützung für mehr als ein Gerät in der Liste der von Wolf Smartset Server zurückgegebenen Geräte", 48 | "ru": "Bugfix: исправлены различные опечатки в Readme и переводах\nBugfix: Исправлена проблема AdminUI (#450 - «Не выбрано устройство»), когда информация об устройстве содержала разрыв линии (например, в контактной информации, описании или комментарии)\nПоддержка более чем одного устройства в списке устройств, возвращенных с сервера Wolf Smartset", 49 | "pt": "Bugfix: corrigido vários typos em Readme e traduções\nBugfix: Corrigido um problema AdminUI (#450 - 'No device selected') quando as informações do dispositivo continham quebra de linha (por exemplo, em ContactInformation, Description or Comment )\nMelhoria para AdminUI: suporte para mais de um dispositivo na lista de dispositivos retornados do servidor Wolf Smartset", 50 | "nl": "Bugfix: verschillende typefouten opgelost in Readme en vertalingen\nBugfix: Vast een AdminUI probleem (#450 - 'Geen apparaat geselecteerd') wanneer het apparaat informatie bevatte lijn breken (bijvoorbeeld in Contactinformatie, beschrijving of opmerking)\nEnhancement for AdminUI: ondersteuning voor meer dan één apparaat in de lijst van apparaten die van Wolf Smartset server zijn teruggestuurd", 51 | "fr": "Bugfix : a corrigé différentes typos dans Readme et traductions\nCorrection d'un problème AdminUI (#450 - 'Aucun périphérique sélectionné') lorsque l'information de l'appareil contenait une rupture de ligne (par exemple dans ContactInformation, Description ou Commentaire )\nAmélioration pour AdminUI: prise en charge de plus d'un périphérique dans la liste des périphériques retournés depuis le serveur Wolf Smartset", 52 | "it": "Bugfix: diversi tipi fissi in Readme e traduzioni\nBugfix: Risolto un problema AdminUI (#450 - 'Nessun dispositivo selezionato') quando le informazioni del dispositivo contenevano l'interruzione della linea (ad esempio in ContactInformation, Descrizione o Commento)\nMiglioramento per AdminUI: supporto per più di un dispositivo nell'elenco dei dispositivi restituiti dal server Wolf Smartset", 53 | "es": "Bugfix: fijo varios tipos en Readme y traducciones\nBugfix: Se corrigió un número de AdminUI (#450 - 'No seleccionó ningún dispositivo') cuando la información del dispositivo contenía la interrupción de la línea (por ejemplo, en contactoInformación, descripción o comentario)\nMejora para AdminUI: soporte para más de un dispositivo en la lista de dispositivos devueltos desde el servidor Wolf Smartset", 54 | "pl": "Bugfix: stałe różne literówki w Readme i tłumaczenia\nBugfix: Naprawiono emisję administracyjną (# 450 - \"Nie wybrano urządzenia\"), gdy informacja o urządzeniu zawierała pęknięcie linii (np. w ContactInformation, Opis lub Komentarz)\nUlepszenie dla Administracji: obsługa więcej niż jednego urządzenia z listy urządzeń zwróconych z serwera Wolf Smartset", 55 | "uk": "Виправлено помилку: виправлено різні типограми в перекладі\nВиправлено помилку: Виправлено проблему AdminUI (#450 - 'Не вибрано пристрій') коли інформація про пристрій, що міститься в рядку (наприклад, в Контактній Інформації, опис або коментар )\nРозширення для AdminUI: підтримка більш ніж одного пристрою в списку пристроїв, що поставляються з сервера Wolf Smartset", 56 | "zh-cn": "Bugfix:在Readme和翻译中固定各种打字符\nBugfix: 当设备信息包含换行符( 如联系人信息、 描述或注释) 时, 固定一个 AdminUI 问题( # 450 - “ 没有选中设备 ” ) \n增强管理UI:支持从 Wolf Smartset 服务器返回的设备列表中的多个设备" 57 | }, 58 | "2.0.0": { 59 | "en": "BREAKING CHANGE: Please reenter your login credentials.\nAdapter requires node.js 20, js-controller 6 and admin 7 now.\nA general code cleanup and partial rewrite has been done.\nTrigger re-initalization has been added, if api returns an error (server might be down temporarily).\nExpert login and periodic re-login have been added (#242).\nSupport for level 3 objects `time programs` / `party mode` / `vacation mode` has been added.\nRequest UserInfo from Wolf server, check whether adapter instance's poll interval meets requirements (60 sec) added.\nParameterId lists for each Wolf BundleId created and show `BundleIds` for each channel added\nSupport for two sepearate poll cycles to avoid server abuse reactions has been added. \nSwitched AdminUI to `jsconConfig`.", 60 | "de": "BREAKING CHANGE: Bitte geben Sie Ihre Anmeldeinformationen neu ein.\nAdapter benötigt jetzt node.js 20, js-controller 6 und admin 7.\nEs wurde eine allgemeine Code-Reinigung und ein Teil-Rewrite durchgeführt.\nTrigger-Re-initalization wurde hinzugefügt, wenn api einen Fehler zurückgibt (Server kann vorübergehend herunter sein).\nExperten-Login und regelmäßiges Re-Login wurden hinzugefügt (#242).\nUnterstützung für Level 3 Objekte `time Programme` / `party mode` / `vacation mode` wurde hinzugefügt.\nFordern Sie UserInfo vom Wolf-Server an, ob das Umfrageintervall der Adapterinstanz den Anforderungen (60 sec) entspricht.\nParameter Id Listen für jeden Wolf Bundle Id erstellt und zeigen `BundleIds` für jeden Kanal hinzugefügt\nEs wurde eine Unterstützung für zwei sepearate Umfragezyklen zur Vermeidung von Server-Missbrauchreaktionen hinzugefügt.\nSchaltete AdminUI auf `jsconConfig`.", 61 | "ru": "Изменить дыхание: Пожалуйста, введите свои учетные данные для входа.\nАдаптер требует node.js 20, js-контроллер 6 и админ 7.\nПроведена генеральная очистка кода и частичная переписка.\nДобавлена реинитализация триггера, если api возвращает ошибку (сервер может временно упасть).\nДобавлен вход эксперта и периодический повторный вход (#242).\nДобавлена поддержка объектов уровня 3 «временные программы» / «партийный режим» / «режим отпуска».\nЗапросить UserInfo с сервера Wolf, проверить, соответствует ли интервал опроса адаптера требованиям (60 сек).\nПараметр Список для каждого волка Id создал и показал «BundleIds» для каждого добавленного канала\nДобавлена поддержка двух циклов опросов, чтобы избежать реакции злоупотребления сервером.\nПереключил AdminUI на jsconConfig.", 62 | "pt": "Muita coisa: Por favor, insira suas credenciais de login.\nAdaptador requer node.js 20, js-controller 6 e admin 7 agora.\nFoi feita uma limpeza geral e reescrita parcial.\nA reinicialização do gatilho foi adicionada, se o api retornar um erro (o servidor pode estar em baixo temporariamente).\nO login e o re-login periódico foram adicionados (#242).\nSuporte para o nível 3 objetos `programas de tempo` / 'modo de partido` / 'modo de educação` foi adicionado.\nSolicite UserInfo do servidor Wolf, verifique se o intervalo de pesquisa da instância do adaptador atende aos requisitos (60 sec) adicionados.\nParâmetro Listas de identificação para cada Wolf Bundle Id criado e mostrar `BundleIds` para cada canal adicionado\nO suporte para dois ciclos de pesquisa sepearate para evitar reações de abuso de servidor foi adicionado.\nAdminUI comutado para `jsconConfig`.", 63 | "nl": "Breaking VERANDERING: Voer uw aanmeldgegevens in.\nAdapter vereist node.js 20, js-controller 6 en admin 7.\nEen algemene code opruiming en gedeeltelijke herschrijven is gedaan.\nTrigger re-initalisatie is toegevoegd, als api een fout teruggeeft (server kan tijdelijk zijn uitgeschakeld).\nExpert login en periodieke re-login zijn toegevoegd (#242).\nOndersteuning voor niveau 3 objecten .\nVraag UserInfo van Wolf server, controleer of de poll interval van adapter instantie voldoet aan de eisen (60 sec) toegevoegd.\nParameter Id lijsten voor elke Wolf Bundle Id creëerde en show BundleIds voor elk kanaal toegevoegd\nEr is steun voor twee aparte pollcycli toegevoegd om servermisbruikreacties te voorkomen.\nSchakelde AdminUI naar .", 64 | "fr": "CHANGEMENT DE BREAING: Veuillez entrer de nouveau vos identifiants de connexion.\nAdaptateur nécessite node.js 20, js-controller 6 et admin 7 maintenant.\nUn nettoyage général du code et une réécriture partielle ont été effectués.\nLa ré-initalisation de déclenchement a été ajoutée, si api renvoie une erreur (le serveur peut être temporairement désactivé).\nUne connexion experte et une nouvelle connexion périodique ont été ajoutées (#242).\nLa prise en charge des objets de niveau 3 `time programs` / `part mode` / `vacation mode` a été ajoutée.\nDemander UserInfo au serveur Wolf, vérifier si l'intervalle de sondage de l'instance adaptateur répond aux exigences (60 sec) ajoutées.\nParamètre Id listes pour chaque Wolf Bundle Id créé et afficher `BundleIds` pour chaque canal ajouté\nOn a ajouté le support de deux cycles de sondage séparés pour éviter les réactions d'abus de serveur.\nA commuté AdminUI en `jsconConfig`.", 65 | "it": "CAMBIO DI BREAKING: Si prega di reinserire le credenziali di accesso.\nAdattatore richiede node.js 20, js-controller 6 e admin 7 ora.\nÈ stata fatta una pulizia generale del codice e una riscrittura parziale.\nTrigger ri-initalization è stato aggiunto, se api restituisce un errore (il server potrebbe essere giù temporaneamente).\nSono stati aggiunti login esperto e re-login periodico (#242).\nSupporto per livello 3 oggetti `programmi di tempo` / `modalità di partito` / `modalità di vacanza` è stato aggiunto.\nRichiedi informazioni sull'utente dal server Wolf, controlla se l'intervallo di sondaggio dell'istanza dell'adattatore soddisfa i requisiti (60 sec) aggiunti.\nParametro Elenchi Id per ogni Wolf Bundle Id ha creato e mostrare `BundleIds` per ogni canale aggiunto\nÈ stato aggiunto il supporto per due cicli di sondaggio sepearate per evitare reazioni di abuso del server.\nAdminUI commutato a `jsconConfig`.", 66 | "es": "Cambiando: Por favor vuelva a introducir sus credenciales de inicio de sesión.\nAdaptador requiere node.js 20, js-controller 6 y admin 7 ahora.\nSe ha hecho una limpieza general de códigos y una reescritura parcial.\nSe ha añadido la reinitalización del desencadenante, si api devuelve un error (el servidor podría bajar temporalmente).\nSe ha añadido el inicio de sesión de expertos y el reinicio periódico (#242).\nSe ha añadido soporte para el nivel 3 objetos `programas de tiempo` / `modo de partido` / `modo de vacunación'.\nSolicite a UserInfo del servidor Wolf, compruebe si el intervalo de encuesta de instancia de adaptador cumple con los requisitos (60 segundos) añadido.\nParámetro Listas de Id para cada Wolf Bundle Id creó y mostró `BundleIds` para cada canal añadido\nSe ha añadido soporte para dos ciclos de encuestas de secuencias para evitar reacciones de abuso del servidor.\nSwitched AdminUI to `jsconConfig`.", 67 | "pl": "ZMIANA ZBIORCZA: Proszę ponownie podać swoje dane logowania.\nAdapter wymaga node.js 20, js- kontroler 6 i admin 7 teraz.\nWykonano ogólne czyszczenie kodu i częściową korektę.\nDodano reinitalizację Trigger, jeśli api zwraca błąd (serwer może być tymczasowo w dół).\nDodano logowanie ekspertów i okresowe ponowne logowanie (# 242).\nDodano obsługę 'programów czasowych' / 'stron' / 'wakacji' poziomu 3.\nPoproś UserInfo z serwera Wolf, sprawdź, czy interwał ankiety instancji adaptera spełnia wymagania (60 sekund) dodane.\nParametr Lista Id dla każdego Wolf Bundle Id stworzył i pokazał 'BundleIds' dla każdego dodanego kanału\nDodano wsparcie dla dwóch cyklów ankietowych, aby uniknąć nadużyć serwera.\nPrzełączono administrację na 'jsconConfig'.", 68 | "uk": "БРЕАКІНГ ЗМІН: Будь ласка, перейменуйте свої облікові дані.\nАдаптер вимагає node.js 20, js-controller 6 і admin 7 тепер.\nЗдійснено очистку коду та частковий перезапис.\nДодана реінсталяція, якщо апі повертає помилку (сервер може бути тимчасовою).\nДодано експертний логін та періодичний релогін (#242).\nДодано підтримку рівня 3 об’єктів `time програм` / `party mode` / `vacation mode```.\nЗапитайте UserInfo від сервера Wolf, перевірте, чи відповідає інтервал опитування адаптера (60 сек).\nПараметр Списки Id для кожного Wolf Bundle Id створив і показати `BundleIds` для кожного каналу\nДодана підтримка двох циклів сепаратного опитування, щоб уникнути реакції зловживання сервером.\nУвімкнено AdminUI до `jsconConfig`.", 69 | "zh-cn": "打破变化: 请重新输入您的登录证书 .\n适配器需要节点.js 20,js控制器 6和现在的admin 7.\n一般代码清理和部分重写已经完成.\n已添加触发重命名, 如果 api 返回一个错误( 服务器可能暂时下调) .\n增加了专家登录和定期重新登录(# 242).\n增加了对 \" 时间程序 \" / \" 政党模式 \" / \" 退出模式 \" 3级对象的支持.\n从 Wolf 服务器请求用户Info,检查适配器实例的民调间隔是否符合要求(60秒)添加.\n参数 每个 Wolf 包的 Id 列表 Id 为每个频道创建并显示“ BundleIds ” \n增加了支持两个sepearate民意调查周期以避免服务器滥用反应.\n已切换为“jsconConfig”." 70 | }, 71 | "1.2.4": { 72 | "en": "Bugfix for issues #281, #329, #365, #406: ioBroker object limits min/max use Wolf Smartset Min/MaxValueCondition if available, otherwise use Min/MaxValue now.\nAdded some comments on Wolf Smartset ControlTypes\nModified misspelled variable name to 'SubMenuEntry'\nAdd NamePrefix, if exists, to object names (e.g. 'A1: ', 'WP001: ') for better parameter identification\nAdapter has been adapted to @iobroker/eslint-config and eslint 9.x.\nDependencies have been updated", 73 | "de": "Bugfix für Probleme #281, #329, #365, #406: ioBroker-Objektlimits min/max verwenden Wolf Smartset Min/MaxValueCondition, falls verfügbar, andernfalls Min/MaxValue jetzt verwenden.\nEinige Kommentare zu Wolf Smartset ControlTyps hinzugefügt\nModified misspelled variable name to 'SubMenuEntry'\nNamePrefix, falls vorhanden, zu Objektnamen hinzufügen (z.B. 'A1: ', 'WP001: ') zur besseren Parameteridentifikation\nAdapter wurde an @iobroker/eslint-config und eslint 9.x angepasst.\nAbhängigkeiten wurden aktualisiert", 74 | "ru": "Bugfix for issues #281, #329, #365, #406: ioBroker object limits min/max use Wolf Smartset Min/MaxValueCondition if available, otherwise use Min/MaxValue now.\nДобавлены некоторые комментарии на Wolf Smartset ControlTypes\nМодифицированное имя 'SubMenuEntry'\nДобавить NamePrefix, если он существует, для обозначения объектов (например, 'A1:', 'WP001:') для лучшей идентификации параметров\nАдаптер был адаптирован к @iobroker/eslint-config и eslint 9.x.\nЗависимость обновлена", 75 | "pt": "Bugfix para problemas #281, #329, #365, #406: ioBroker objeto limita min/max usar Wolf Smartset Min/MaxValueCondition se disponível, de outra forma usar Min/MaxValue agora.\nAdicionado alguns comentários em Wolf Smartset ControlTypes\nNome de variável desviado modificado para 'SubMenuEntry'\nAdicionar NamePrefix, se existir, para nomes de objetos (por exemplo, 'A1: ', 'WP001: ') para melhor identificação de parâmetros\nAdapter foi adaptado para @iobroker/eslint-config e eslint 9.x.\nAs dependências foram atualizadas", 76 | "nl": "Bugfix voor problemen #281, #329, #365, #406: ioBroker object limieten min/max gebruik Wolf Smartset Min/MaxValueCondition indien beschikbaar, anders gebruik Min/MaxValue nu.\nToegevoegd enkele opmerkingen over Wolf Smartset ControlTypes\nVeranderde fout gespelde variabelenaam naar 'SubMenuEntry'\nNaamVoorvoegsel toevoegen aan objectnamen (bijv. 'A1:', 'WP001:') voor een betere parameteridentificatie\nAdapter is aangepast aan @iobroker/eslint-config en eslint 9.x.\nAfhankelijkheden zijn bijgewerkt", 77 | "fr": "Bugfix pour les numéros #281, #329, #365, #406: ioBroker limite les objets min/max utiliser Wolf Smartset Min/MaxValueCondition si disponible, sinon utiliser Min/MaxValue maintenant.\nAjouté quelques commentaires sur Wolf Smartset ControlTypes\nNom de variable mal orthographiée modifié pour 'SubMenuEntry'\nAjouter NamePrefix, s'il existe, aux noms des objets (par exemple 'A1: ', 'WP001: ') pour une meilleure identification des paramètres\nAdaptateur a été adapté à @iobroker/eslint-config et eslint 9.x.\nLes dépendances ont été actualisées", 78 | "it": "Bugfix per i numeri #281, #329, #365, #406: l'oggetto ioBroker limita l'uso minimo/max Wolf Smartset Min/MaxValueCondition se disponibile, altrimenti usare Min/MaxValue ora.\nAggiunto alcuni commenti su Wolf Smartset ControlTypes\nModificato nome variabile misspelled a 'SubMenuEntry'\nAggiungi NamePrefix, se esiste, ai nomi degli oggetti (ad esempio 'A1: ', 'WP001: ') per una migliore identificazione dei parametri\nL'adattatore è stato adattato a @iobroker/eslint-config ed eslint 9.x.\nLe dipendenze sono state aggiornate", 79 | "es": "Bugfix para problemas #281, #329, #365, #406: ioBroker límite de objetos min/max uso Wolf Smartset Min/MaxValueCondición si está disponible, de lo contrario use Min/MaxValue ahora.\nAñadido algunos comentarios sobre Wolf Smartset ControlTypes\nNombre de variable desperdiciado modificado a 'SubMenuEntry'\nAñadir NombrePrefix, si existe, a los nombres de objetos (por ejemplo, 'A1: ', 'WP001: ') para una mejor identificación del parámetro\nAdaptador ha sido adaptado a @iobroker/eslint-config y eslint 9.x.\nSe han actualizado las dependencias", 80 | "pl": "Bugfix for issues # 281, # 329, # 365, # 406: joBroker object limits min / max use Wolf Smartset Min / MaxValueCondition if available, other use Min / MaxValue now.\nDodano kilka komentarzy do Wolf Smartset ControlTypes\nZmodyfikowana błędna nazwa zmiennej do 'SubMenuEntry'\nDodaj NamePrefix, jeśli istnieje, do nazw obiektów (np. 'A1:', 'WP001:') dla lepszej identyfikacji parametrów\nAdapter został dostosowany do @ iobroker / eslint- config i eslint 9.x.\nZaktualizowano zależności", 81 | "uk": "Виправлення помилок для питань #281, #329, #365, #406: ioBroker межі об'єктів min/max використання Wolf Smartset Min/MaxValueCondition, якщо це можливо, інакше використовуйте Min/MaxValue зараз.\nДодано деякі коментарі на Wolf Smartset ControlTypes\nМодифіковане ім'я пропущених змінних імен до 'SubMenuEntry'\nДодати ім'яПрефікс, якщо існує, для імен об'єктів (наприклад, 'A1: ', 'WP001: ') для визначення кращого параметра\nАдаптер був адаптований до @iobroker/eslint-config та eslint 9.x.\nЗалежність було оновлено", 82 | "zh-cn": "281,#329,#365,#406期的bugfix:ioBroker对象限制 min/max 如果可用的话使用Wolf Smartset Min/MaxValue Content,否则现在使用Min/MaxValue.\n添加一些关于 Wolf Smartset 控制模式的评论\n修改错误的可变名称为“ SubMenu Entry ”\n添加名称前缀, 如果存在的话, 用于对象名称( 例如“ A1: ” , “ WP001: ” ) , 以更好地识别参数\n适配器已改编为@iobroduc/eslint-config和eslint 9.x.\n依赖关系已更新" 83 | }, 84 | "1.2.3": { 85 | "en": "Dependencies have been updated", 86 | "de": "Abhängigkeiten wurden aktualisiert", 87 | "ru": "Зависимость обновлена", 88 | "pt": "As dependências foram atualizadas", 89 | "nl": "Afhankelijkheden zijn bijgewerkt", 90 | "fr": "Les dépendances ont été actualisées", 91 | "it": "Le dipendenze sono state aggiornate", 92 | "es": "Se han actualizado las dependencias", 93 | "pl": "Zaktualizowano zależności", 94 | "uk": "Залежність було оновлено", 95 | "zh-cn": "依赖关系已更新" 96 | } 97 | }, 98 | "titleLang": { 99 | "en": "Wolf SmartSet", 100 | "de": "Wolf SmartSet", 101 | "ru": "Wolf SmartSet", 102 | "pt": "Wolf SmartSet", 103 | "nl": "Wolf SmartSet", 104 | "fr": "Wolf SmartSet", 105 | "it": "Wolf SmartSet", 106 | "es": "Wolf SmartSet", 107 | "pl": "Wolf SmartSet", 108 | "zh-cn": "狼SmartSet", 109 | "uk": "Wolf SmartSet" 110 | }, 111 | "desc": { 112 | "en": "Connect Wolf cloud to IoBroker", 113 | "de": "Verbinden Sie die Wolf Cloud mit IoBroker", 114 | "ru": "Подключите облако Wolf к IoBroker", 115 | "pt": "Conecte a nuvem Wolf ao IoBroker", 116 | "nl": "Verbind Wolf-cloud met IoBroker", 117 | "fr": "Connectez Wolf Cloud à IoBroker", 118 | "it": "Connetti il ​​cloud Wolf a IoBroker", 119 | "es": "Conecte la nube Wolf a IoBroker", 120 | "pl": "Połącz chmurę Wolfa z IoBrokerem", 121 | "zh-cn": "将Wolf云连接到IoBroker", 122 | "uk": "Підключіть Wolf cloud до IoBroker" 123 | }, 124 | "authors": [ 125 | "MeisterTR ", 126 | "flingo64 " 127 | ], 128 | "keywords": [ 129 | "wolf", 130 | "heating", 131 | "cloud" 132 | ], 133 | "licenseInformation": { 134 | "license": "MIT", 135 | "type": "free" 136 | }, 137 | "platform": "Javascript/Node.js", 138 | "icon": "wolf-smartset.png", 139 | "enabled": true, 140 | "messagebox": true, 141 | "extIcon": "https://raw.githubusercontent.com/iobroker-community-adapters/ioBroker.wolf-smartset/master/admin/wolf-smartset.png", 142 | "readme": "https://github.com/iobroker-community-adapters/ioBroker.wolf-smartset/blob/master/README.md", 143 | "loglevel": "info", 144 | "tier": 2, 145 | "mode": "daemon", 146 | "type": "climate-control", 147 | "compact": true, 148 | "connectionType": "cloud", 149 | "dataSource": "poll", 150 | "adminUI": { 151 | "config": "json" 152 | }, 153 | "plugins": { 154 | "sentry": { 155 | "dsn": "https://a9218dbd5539461fae687736e804c590@sentry.iobroker.net/129" 156 | } 157 | }, 158 | "dependencies": [ 159 | { 160 | "js-controller": ">=6.0.11" 161 | } 162 | ], 163 | "globalDependencies": [ 164 | { 165 | "admin": ">=7.6.17" 166 | } 167 | ] 168 | }, 169 | "encryptedNative": [ 170 | "password", 171 | "expertPassword" 172 | ], 173 | "protectedNative": [ 174 | "password", 175 | "expertPassword" 176 | ], 177 | "native": { 178 | "username": "", 179 | "password": "", 180 | "device": "", 181 | "deviceName": "No device selected", 182 | "pollIntervalShort": 120, 183 | "pollIntervalLong": 60, 184 | "doExpertLogin": false, 185 | "expertPassword": "", 186 | "doPubIpCheck": true, 187 | "doApiProfile": false, 188 | "doPollAllParams": true, 189 | "bundleIdRequestedShort": "Default", 190 | "bundleIdRequestedLong": "Default", 191 | "bundleIdTable": [ 192 | { 193 | "bundleIdName": "1000", 194 | "bundleIdUseShort": true, 195 | "bundleIdUseLong": false 196 | }, 197 | { 198 | "bundleIdName": "3100", 199 | "bundleIdUseShort": true, 200 | "bundleIdUseLong": false 201 | }, 202 | { 203 | "bundleIdName": "3500", 204 | "bundleIdUseShort": false, 205 | "bundleIdUseLong": true 206 | }, 207 | { 208 | "bundleIdName": "4300", 209 | "bundleIdUseShort": false, 210 | "bundleIdUseLong": false 211 | }, 212 | { 213 | "bundleIdName": "6300", 214 | "bundleIdUseShort": false, 215 | "bundleIdUseLong": true 216 | }, 217 | { 218 | "bundleIdName": "6500", 219 | "bundleIdUseShort": false, 220 | "bundleIdUseLong": false 221 | }, 222 | { 223 | "bundleIdName": "7100", 224 | "bundleIdUseShort": false, 225 | "bundleIdUseLong": true 226 | }, 227 | { 228 | "bundleIdName": "7300", 229 | "bundleIdUseShort": false, 230 | "bundleIdUseLong": false 231 | }, 232 | { 233 | "bundleIdName": "7600", 234 | "bundleIdUseShort": false, 235 | "bundleIdUseLong": false 236 | }, 237 | { 238 | "bundleIdName": "8000", 239 | "bundleIdUseShort": false, 240 | "bundleIdUseLong": false 241 | }, 242 | { 243 | "bundleIdName": "8100", 244 | "bundleIdUseShort": false, 245 | "bundleIdUseLong": false 246 | }, 247 | { 248 | "bundleIdName": "8200", 249 | "bundleIdUseShort": false, 250 | "bundleIdUseLong": false 251 | }, 252 | { 253 | "bundleIdName": "10000", 254 | "bundleIdUseShort": false, 255 | "bundleIdUseLong": false 256 | }, 257 | { 258 | "bundleIdName": "10700", 259 | "bundleIdUseShort": false, 260 | "bundleIdUseLong": false 261 | }, 262 | { 263 | "bundleIdName": "14000", 264 | "bundleIdUseShort": false, 265 | "bundleIdUseLong": false 266 | }, 267 | { 268 | "bundleIdName": "14700", 269 | "bundleIdUseShort": false, 270 | "bundleIdUseLong": false 271 | }, 272 | { 273 | "bundleIdName": "15600", 274 | "bundleIdUseShort": false, 275 | "bundleIdUseLong": false 276 | }, 277 | { 278 | "bundleIdName": "15700", 279 | "bundleIdUseShort": false, 280 | "bundleIdUseLong": false 281 | }, 282 | { 283 | "bundleIdName": "15800", 284 | "bundleIdUseShort": false, 285 | "bundleIdUseLong": false 286 | } 287 | ] 288 | }, 289 | "objects": [], 290 | "instanceObjects": [ 291 | { 292 | "_id": "info", 293 | "type": "channel", 294 | "common": { 295 | "name": "Information" 296 | }, 297 | "native": {} 298 | }, 299 | { 300 | "_id": "info.connection", 301 | "type": "state", 302 | "common": { 303 | "role": "indicator.connected", 304 | "name": "If connected to device or service", 305 | "type": "boolean", 306 | "read": true, 307 | "write": false, 308 | "def": false 309 | }, 310 | "native": {} 311 | }, 312 | { 313 | "_id": "info_api", 314 | "type": "channel", 315 | "common": { 316 | "name": "API Profile Info" 317 | }, 318 | "native": {} 319 | }, 320 | { 321 | "_id": "info_api.poll_req_bundle_id", 322 | "type": "state", 323 | "common": { 324 | "role": "value", 325 | "name": "Polled BundleId", 326 | "type": "number", 327 | "read": true, 328 | "write": false, 329 | "def": 0 330 | }, 331 | "native": {} 332 | }, 333 | { 334 | "_id": "info_api.poll_req_num_params", 335 | "type": "state", 336 | "common": { 337 | "role": "value", 338 | "name": "Number of requested parameter values", 339 | "type": "number", 340 | "read": true, 341 | "write": false, 342 | "def": 0 343 | }, 344 | "native": {} 345 | }, 346 | { 347 | "_id": "info_api.poll_resp_num_params", 348 | "type": "state", 349 | "common": { 350 | "role": "value", 351 | "name": "Number of returned parameters", 352 | "type": "number", 353 | "read": true, 354 | "write": false, 355 | "def": 0 356 | }, 357 | "native": {} 358 | }, 359 | { 360 | "_id": "info_api.poll_resp_num_values", 361 | "type": "state", 362 | "common": { 363 | "role": "value", 364 | "name": "Number of returned parameters having a value", 365 | "type": "number", 366 | "read": true, 367 | "write": false, 368 | "def": 0 369 | }, 370 | "native": {} 371 | } 372 | ] 373 | } 374 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('@iobroker/adapter-core'); 4 | const wolfsmartset = require('./lib/wss'); 5 | 6 | // ipify.org REST API: get your public IP 7 | const axios = require('axios').default; 8 | const _GET_MY_PUBLIC_IP_URL = 'https://api.ipify.org?format=json'; 9 | 10 | const timeoutHandler = []; 11 | 12 | const BUNDLEID_FULL = 0; 13 | const BUNDLEID_DEFAULT = 1000; 14 | 15 | let ParamObjList = []; 16 | //const objects = {}; 17 | 18 | class WolfSmartsetAdapter extends utils.Adapter { 19 | wss; 20 | wss_user; 21 | wss_password; 22 | myPublicIp; 23 | device; 24 | onlinePoll; 25 | emptyCount; 26 | BundleIdShortCycle; 27 | ValueIdListShortCycle; 28 | BundleIdLongCycle; 29 | ValueIdListLongCycle; 30 | /** 31 | * @param [options] - adapter options 32 | */ 33 | constructor(options) { 34 | super({ 35 | ...options, 36 | name: 'wolf-smartset', 37 | }); 38 | this.on('ready', this.onReady.bind(this)); 39 | this.on('stateChange', this.onStateChange.bind(this)); 40 | // this.on('objectChange', this.onObjectChange.bind(this)); 41 | this.on('message', this.onMessage.bind(this)); 42 | this.on('unload', this.onUnload.bind(this)); 43 | } 44 | 45 | /** 46 | * Get our public IP from ipify.org 47 | * 48 | */ 49 | async _getMyPublicIp() { 50 | if (this.config.doPubIpCheck) { 51 | try { 52 | const myIpDataResponse = await axios.get(_GET_MY_PUBLIC_IP_URL); 53 | 54 | if (myIpDataResponse.status == 200 && myIpDataResponse.data && myIpDataResponse.data.ip) { 55 | return myIpDataResponse.data.ip; 56 | } 57 | } catch (error) { 58 | this.log.warn(`_getMyPublicIp() failed: ${error.message}`); 59 | } 60 | } 61 | return null; 62 | } 63 | 64 | async _getParamsWebGui(guiData) { 65 | if (guiData == null) { 66 | return; 67 | } 68 | const param = []; 69 | 70 | guiData.MenuItems.forEach(MenuItem => { 71 | const tabName = MenuItem.Name; 72 | 73 | // Benutzerebene: iterate over MenuItems 74 | MenuItem.TabViews.forEach(TabView => { 75 | const tabName2 = `${tabName}.${TabView.TabName}`; 76 | // BundleId and GuiId of TabView are required in each param below thie TabView 77 | const TabViewBundleId = TabView.BundleId; 78 | const TabViewGuiId = TabView.GuiId; 79 | 80 | TabView.ParameterDescriptors.forEach(ParameterDescriptor => { 81 | var tabName3; 82 | if (typeof ParameterDescriptor.Group !== 'undefined') { 83 | tabName3 = `${tabName2}.${ParameterDescriptor.Group.replace(' ', '_')}`; 84 | } else { 85 | tabName3 = tabName2; 86 | } 87 | 88 | // ignore pseudo or intermediate/complex parameters (e.g list of time programs) 89 | if (ParameterDescriptor.ParameterId > 0) { 90 | const paramInfo = ParameterDescriptor; 91 | paramInfo.BundleId = TabViewBundleId; 92 | paramInfo.GuiId = TabViewGuiId; 93 | 94 | //search duplicate 95 | const find = param.find(element => element.ParameterId === paramInfo.ParameterId); 96 | 97 | if (!find) { 98 | paramInfo.TabName = tabName3; 99 | // remove subtree if exists 100 | // delete paramInfo.ChildParameterDescriptors; 101 | param.push(paramInfo); 102 | } 103 | } 104 | 105 | // Check for ChildParameterDescriptors (e.g. time program) 106 | if (typeof ParameterDescriptor.ChildParameterDescriptors !== 'undefined') { 107 | ParameterDescriptor.ChildParameterDescriptors.forEach(ChildParameterDescriptor => { 108 | var tabName4 = `${tabName3}.${ParameterDescriptor.Name}`; 109 | 110 | // ignore pseudo or intermediate/complex parameters (e.g time program) 111 | if ( 112 | ChildParameterDescriptor.NoDataPoint == false && 113 | ChildParameterDescriptor.ParameterId > 0 114 | ) { 115 | const paramInfo = ChildParameterDescriptor; 116 | paramInfo.BundleId = TabViewBundleId; 117 | paramInfo.GuiId = TabViewGuiId; 118 | 119 | //search duplicate 120 | const find = param.find(element => element.ParameterId === paramInfo.ParameterId); 121 | 122 | if (!find) { 123 | paramInfo.TabName = tabName4.replace(' ', '_'); 124 | param.push(paramInfo); 125 | } 126 | } 127 | 128 | if (typeof ChildParameterDescriptor.ChildParameterDescriptors !== 'undefined') { 129 | ChildParameterDescriptor.ChildParameterDescriptors.forEach( 130 | ChildChildParameterDescriptor => { 131 | const tabName5 = `${tabName4}.${ChildParameterDescriptor.Name}`; 132 | 133 | if (ChildChildParameterDescriptor.ParameterId > 0) { 134 | const paramInfo = ChildChildParameterDescriptor; 135 | paramInfo.BundleId = TabViewBundleId; 136 | paramInfo.GuiId = TabViewGuiId; 137 | 138 | //search duplicate 139 | const find = param.find( 140 | element => element.ParameterId === paramInfo.ParameterId, 141 | ); 142 | 143 | if (!find) { 144 | paramInfo.TabName = tabName5.replace(' ', '_'); 145 | param.push(paramInfo); 146 | } 147 | } 148 | }, 149 | ); 150 | } 151 | }); 152 | } 153 | }); 154 | }); 155 | 156 | // Fachmannebene: interate over SubMenuEntries 157 | MenuItem.SubMenuEntries.forEach(SubMenuEntry => { 158 | const tabName2 = `${tabName}.${SubMenuEntry.Name}`; 159 | SubMenuEntry.TabViews.forEach(TabView => { 160 | const tabName3 = `${tabName2}.${TabView.TabName}`.replace(' ', '_'); 161 | // BundleId and GuiId of TabView are required in each param below thie TabView 162 | const TabViewBundleId = TabView.BundleId; 163 | const TabViewGuiId = TabView.GuiId; 164 | 165 | TabView.ParameterDescriptors.forEach(ParameterDescriptor => { 166 | var tabName4; 167 | if (typeof ParameterDescriptor.Group !== 'undefined') { 168 | tabName4 = `${tabName3}.${ParameterDescriptor.Group.replace(' ', '_')}`; 169 | } else { 170 | tabName4 = tabName3; 171 | } 172 | 173 | // ignore pseudo or intermediate/complex parameters (e.g list of time programs) 174 | if (ParameterDescriptor.ParameterId > 0) { 175 | const paramInfo = ParameterDescriptor; 176 | paramInfo.BundleId = TabViewBundleId; 177 | paramInfo.GuiId = TabViewGuiId; 178 | 179 | //search duplicate 180 | const find = param.find(element => element.ParameterId === paramInfo.ParameterId); 181 | 182 | if (!find) { 183 | paramInfo.TabName = tabName4; 184 | param.push(paramInfo); 185 | } 186 | } 187 | }); 188 | }); 189 | }); 190 | }); 191 | 192 | return param; 193 | } 194 | 195 | /** 196 | * Generates folders, channels and adapter object states for each param in WolfParamDescriptions. 197 | * 198 | * @param WolfParamDescriptions - flat list of ParamDescriptions for each state returned by getParamsWebGui() 199 | */ 200 | async _CreateObjects(WolfParamDescriptions) { 201 | // get list of instance objects before fetching new list of params from Wolf server 202 | const oldInstanceObjects = await this.getForeignObjectsAsync(`${this.namespace}.*`); 203 | const collectedChannels = {}; 204 | 205 | // 1.: Create states 206 | for (const WolfParamDescription of WolfParamDescriptions) { 207 | // export BundleId of object to associated channel 208 | collectedChannels[`${WolfParamDescription.TabName}`] = WolfParamDescription.BundleId; 209 | const id = `${WolfParamDescription.TabName}.${WolfParamDescription.ParameterId.toString()}`; 210 | 211 | const common = { 212 | name: 213 | typeof WolfParamDescription.NamePrefix !== 'undefined' 214 | ? `${WolfParamDescription.NamePrefix}: ${WolfParamDescription.Name}` 215 | : WolfParamDescription.Name, 216 | // do not declare type and role here to avoid typecheck errors in setObjectNotExists() 217 | // type: 'number', 218 | // role: 'value', 219 | read: true, 220 | write: !WolfParamDescription.IsReadOnly, 221 | }; 222 | common.type = 'number'; 223 | common.role = 'value'; 224 | 225 | // Wolf ControlTypes: 226 | // 0: Unknown 227 | // 1: Enum w/ ListItems (simple) 228 | // 5: Bool 229 | // 6: Number; 'Decimals' = decimal places (accuracy) 230 | // 9: Date 231 | // 10: Time 232 | // 13: list of time programs (1, 2 or 3) (not a Value) 233 | // 14: list of time ranges 234 | // 19: time program (Mon - Sun) (not a value) 235 | // 20: Name, SerialNo, MacAddr, SW-Version, HW-Version 236 | // 21: IPv4 addr or netmask 237 | // 31: Number of any kind 238 | // 35: Enum w/ ListItems (w/ Image, Decription, ...) 239 | 240 | if (WolfParamDescription.ControlType === 5) { 241 | //Boolean text 242 | common.type = 'boolean'; 243 | common.role = WolfParamDescription.IsReadOnly ? 'indicator' : 'switch'; 244 | } else if ( 245 | WolfParamDescription.ControlType === 9 || 246 | WolfParamDescription.ControlType === 10 || 247 | WolfParamDescription.ControlType === 14 || 248 | WolfParamDescription.ControlType === 20 || 249 | WolfParamDescription.ControlType === 21 250 | ) { 251 | common.type = 'string'; 252 | common.role = 'text'; 253 | } else { 254 | if (typeof WolfParamDescription.Unit !== 'undefined') { 255 | common.unit = WolfParamDescription.Unit; 256 | } 257 | 258 | // thresholds min/max : use Min/MaxValueCondition if available, otherwise use MinValue/MaxValue 259 | // Min/MaxValue might be wrong in case of floats, whereas Min/MaxValueCondition seem to be always correct 260 | if (typeof WolfParamDescription.MinValue !== 'undefined') { 261 | common.min = WolfParamDescription.MinValue; 262 | } 263 | if (typeof WolfParamDescription.MinValueCondition !== 'undefined') { 264 | common.min = parseFloat(WolfParamDescription.MinValueCondition); 265 | } 266 | if (typeof WolfParamDescription.MaxValue !== 'undefined') { 267 | common.max = WolfParamDescription.MaxValue; 268 | } 269 | if (typeof WolfParamDescription.MaxValueCondition !== 'undefined') { 270 | common.max = parseFloat(WolfParamDescription.MaxValueCondition); 271 | } 272 | 273 | if (typeof WolfParamDescription.StepWidth !== 'undefined') { 274 | common.step = WolfParamDescription.StepWidth; 275 | } 276 | if (typeof WolfParamDescription.ListItems !== 'undefined') { 277 | const states = {}; 278 | WolfParamDescription.ListItems.forEach(ListItems => { 279 | states[ListItems.Value] = ListItems.DisplayText; 280 | }); 281 | common.states = states; 282 | } 283 | } 284 | 285 | this.log.debug( 286 | `WolfParamDescription ${JSON.stringify(WolfParamDescription)} --> ioBrokerObj.common ${JSON.stringify(common)}`, 287 | ); 288 | 289 | // if this is a new object, create it first 290 | const fullId = `${this.namespace}.${id}`; 291 | if (typeof oldInstanceObjects[`${fullId}`] == 'undefined') { 292 | // create object w/ minimum set of attributes 293 | this.setObjectNotExists(id, { 294 | type: 'state', 295 | common: { 296 | name: common.name, 297 | type: common.type, 298 | role: common.role, 299 | read: common.read, 300 | write: common.write, 301 | }, 302 | native: {}, 303 | }); 304 | } else { 305 | oldInstanceObjects[fullId].common.desc = 'active'; 306 | } 307 | 308 | // set all attributes for object 309 | this.extendObject(id, { 310 | type: 'state', 311 | common: common, 312 | native: { 313 | ValueId: WolfParamDescription.ValueId, 314 | ParameterId: WolfParamDescription.ParameterId, 315 | ControlType: WolfParamDescription.ControlType, 316 | }, 317 | }); 318 | 319 | // 2.: Update object states 320 | await this._setStatesWithDiffTypes(WolfParamDescription.ControlType, id, WolfParamDescription.Value); 321 | } 322 | 323 | // 3.: mark obsoleted objects 324 | for (const fullId in oldInstanceObjects) { 325 | let re = new RegExp(String.raw`^${this.namespace}.info`, 'g'); 326 | if ( 327 | !fullId.match(re) && 328 | typeof oldInstanceObjects[fullId].common != 'undefined' && 329 | typeof oldInstanceObjects[fullId].common.desc == 'undefined' 330 | ) { 331 | oldInstanceObjects[fullId].common.desc = 'obsoleted'; 332 | this.extendObject(fullId, oldInstanceObjects[fullId]); 333 | } 334 | } 335 | 336 | // 4.: Create folders and channels 337 | const createdObjects = {}; 338 | for (const [channel, bundleId] of Object.entries(collectedChannels)) { 339 | const name = `${channel.split('.').pop()} (Bundle: ${bundleId})`; 340 | this.extendObject(channel, { 341 | type: 'channel', 342 | common: { 343 | name: name, 344 | }, 345 | native: {}, 346 | }); 347 | createdObjects[channel] = true; 348 | this.log.debug(`Create channel ${channel}`); 349 | const channelParts = channel.split('.'); 350 | let id = channelParts.shift() || ''; 351 | let folderName = id; 352 | while (id && channelParts.length > 0) { 353 | if (!createdObjects[id]) { 354 | await this.extendObject(id, { 355 | type: 'folder', 356 | common: { 357 | name: folderName, 358 | }, 359 | native: {}, 360 | }); 361 | this.log.debug(`Create folder ${id}`); 362 | createdObjects[id] = true; 363 | } 364 | folderName = channelParts.shift() || ''; 365 | if (!folderName) { 366 | break; 367 | } 368 | id += `.${folderName}`; 369 | } 370 | } 371 | 372 | this.log.debug('createParams DONE'); 373 | } 374 | 375 | /** 376 | * Creates a list of ParameterId for each BundleId defined in WolfParamDescriptions and 377 | * From that create ValueIdListShortCycle and ValueIdListLongCycle 378 | * 379 | * @param WolfParamDescriptions - list of extended WolfParamDescriptions returned by getParamsWebGui() 380 | */ 381 | async _CreateBundleValuesLists(WolfParamDescriptions) { 382 | let BundleValuesList = {}; 383 | let DefaultBundleIdShortCycle = BUNDLEID_DEFAULT; 384 | let ValueIdListShortCycle = []; 385 | let DefaultBundleIdLongCycle = BUNDLEID_DEFAULT; 386 | let ValueIdListLongCycle = []; 387 | 388 | // Bundle BUNDLEID_FULL (0) --> full parameter list 389 | BundleValuesList[BUNDLEID_FULL] = []; 390 | 391 | for (const WolfParamDescription of WolfParamDescriptions) { 392 | const bundleId = WolfParamDescription.BundleId; 393 | if (typeof BundleValuesList[bundleId] == 'undefined') { 394 | BundleValuesList[bundleId] = []; 395 | } 396 | 397 | // De-duplicate ParamterIds for bundleId: they might be at multiple locations in the tree 398 | if (typeof BundleValuesList[BUNDLEID_FULL][WolfParamDescription.ParameterId] == 'undefined') { 399 | BundleValuesList[BUNDLEID_FULL].push(WolfParamDescription.ParameterId); 400 | } 401 | if (typeof BundleValuesList[bundleId][WolfParamDescription.ParameterId] == 'undefined') { 402 | BundleValuesList[bundleId].push(WolfParamDescription.ParameterId); 403 | } 404 | } 405 | 406 | if (this.config.doPollAllParams) { 407 | // doPollAllParams: poll full params list with BundleId set to BUNDLEID_DEFAULT (1000) 408 | this.ValueIdListShortCycle = BundleValuesList[BUNDLEID_FULL]; 409 | this.BundleIdShortCycle = BUNDLEID_DEFAULT; 410 | 411 | this.ValueIdListLongCycle = []; 412 | this.BundleIdLongCycle = BUNDLEID_DEFAULT; 413 | } else { 414 | for (const bundleId of this.config.bundleIdTable) { 415 | if (bundleId.bundleIdUseShort && typeof BundleValuesList[bundleId.bundleIdName] != 'undefined') { 416 | ValueIdListShortCycle = ValueIdListShortCycle.concat(BundleValuesList[bundleId.bundleIdName]); 417 | // BundleId - Default is the greatest BundleId 418 | DefaultBundleIdShortCycle = 419 | Number(bundleId.bundleIdName) > DefaultBundleIdShortCycle 420 | ? Number(bundleId.bundleIdName) 421 | : DefaultBundleIdShortCycle; 422 | } 423 | if (bundleId.bundleIdUseLong && typeof BundleValuesList[bundleId.bundleIdName] != 'undefined') { 424 | ValueIdListLongCycle = ValueIdListLongCycle.concat(BundleValuesList[bundleId.bundleIdName]); 425 | // BundleId - Default is the greatest BundleId 426 | DefaultBundleIdLongCycle = 427 | Number(bundleId.bundleIdName) > DefaultBundleIdLongCycle 428 | ? Number(bundleId.bundleIdName) 429 | : DefaultBundleIdLongCycle; 430 | } 431 | } 432 | 433 | this.ValueIdListShortCycle = ValueIdListShortCycle; 434 | this.ValueIdListLongCycle = ValueIdListLongCycle; 435 | 436 | // Determine BundleId to use in GET PARAMS API: 437 | // if configured BunddleId is 'Default' 438 | // or configured BunddleId not available on server 439 | // then 440 | // use largest BundleId found in configured list 441 | // else 442 | // use configured BundleId 443 | this.BundleIdShortCycle = 444 | this.config.bundleIdRequestedShort == 'Default' || 445 | !(this.config.bundleIdRequestedShort in BundleValuesList) 446 | ? DefaultBundleIdShortCycle 447 | : this.config.bundleIdRequestedShort; 448 | 449 | this.BundleIdLongCycle = 450 | this.config.bundleIdRequestedLong == 'Default' || 451 | !(this.config.bundleIdRequestedLong in BundleValuesList) 452 | ? DefaultBundleIdLongCycle 453 | : this.config.bundleIdRequestedLong; 454 | } 455 | } 456 | 457 | async _setStatesWithDiffTypes(type, id, value) { 458 | if (type == null || id == null || value == null) { 459 | return; 460 | } 461 | 462 | // Wolf ControlTypes: 463 | // 0: Unknown 464 | // 1: Enum w/ ListItems (simple) 465 | // 5: Bool 466 | // 6: Number; 'Decimals' = decimal places (accuracy) 467 | // 9: Date 468 | // 10: Time 469 | // 13: list of time programs (1, 2 or 3) (not a Value) 470 | // 14: list of time ranges 471 | // 19: time program (Mon - Sun) (not a value) 472 | // 20: Name, SerialNo, MacAddr, SW-Version, HW-Version 473 | // 21: IPv4 addr or netmask 474 | // 31: Number of any kind 475 | // 35: Enum w/ ListItems (w/ Image, Decription, ...) 476 | switch (type) { 477 | case 5: 478 | this.setState(id, { 479 | val: value === 'True' ? true : false, 480 | ack: true, 481 | }); 482 | break; 483 | case 9: 484 | case 10: 485 | case 14: 486 | case 20: 487 | case 21: 488 | this.setState(id, { 489 | val: value.toString(), 490 | ack: true, 491 | }); 492 | break; 493 | 494 | default: 495 | this.setState(id, { 496 | val: parseFloat(value), 497 | ack: true, 498 | }); 499 | break; 500 | } 501 | } 502 | 503 | /** 504 | * Poll parameter values from Wolf server as configured for the given poll cycle 505 | * 506 | * @param pollCycle - 'short or 'long' 507 | */ 508 | async _PollValueList(pollCycle) { 509 | try { 510 | const recValList = await this.wss.getValList( 511 | this.device.GatewayId, 512 | this.device.SystemId, 513 | pollCycle == 'short' ? this.BundleIdShortCycle : this.BundleIdLongCycle, 514 | pollCycle == 'short' ? this.ValueIdListShortCycle : this.ValueIdListLongCycle, 515 | pollCycle, 516 | ); 517 | if (recValList) { 518 | await this._SetStatesArray(recValList); 519 | } 520 | } catch (error) { 521 | this.log.warn(error); 522 | } 523 | } 524 | 525 | /** 526 | * Handler for Short Poll Cycle: poll parameter values from Wolf server and 527 | * additionally, every 4th poll cycle: getSystemState and check for PublicIP changes 528 | * 529 | */ 530 | async _ShortPollValueList() { 531 | timeoutHandler['shortPollTimeout'] && clearTimeout(timeoutHandler['shortPollTimeout']); 532 | 533 | await this._PollValueList('short'); 534 | 535 | this.onlinePoll++; 536 | if (this.onlinePoll > 4) { 537 | this.onlinePoll = 0; 538 | try { 539 | const myPublicIp = await this._getMyPublicIp(); 540 | if (myPublicIp && this.myPublicIp && myPublicIp != this.myPublicIp) { 541 | this.log.warn( 542 | `_ShortPollValueList(); PubIP changed from ${this.myPublicIp} to ${myPublicIp}: triggering reload...`, 543 | ); 544 | await this._mainloop(myPublicIp); 545 | return; 546 | } 547 | 548 | const systemStatus = await this.wss.getSystemState(parseInt(this.device.SystemId)); 549 | if (systemStatus && typeof systemStatus.IsOnline !== 'undefined') { 550 | this.setState('info.connection', { 551 | val: systemStatus.IsOnline, 552 | ack: true, 553 | }); 554 | } else { 555 | this.setState('info.connection', { 556 | val: false, 557 | ack: true, 558 | }); 559 | } 560 | } catch (error) { 561 | this.log.warn(error); 562 | } 563 | } 564 | 565 | timeoutHandler['shortPollTimeout'] = setTimeout(() => { 566 | this._ShortPollValueList(); 567 | }, this.config.pollIntervalShort * 1000); 568 | } 569 | 570 | /** 571 | * Handler for Lhort Poll Cycle: poll parameter values from Wolf server 572 | * 573 | */ 574 | async _LongPollValueList() { 575 | timeoutHandler['longPollTimeout'] && clearTimeout(timeoutHandler['longPollTimeout']); 576 | 577 | await this._PollValueList('long'); 578 | 579 | timeoutHandler['longPollTimeout'] = setTimeout( 580 | () => { 581 | this._LongPollValueList(); 582 | }, 583 | // pollIntervalLong is given in minutes; add 5 seconds to avoid parallel execution w/ _ShortPollValueList() 584 | this.config.pollIntervalLong * 60 * 1000 + 5000, 585 | ); 586 | } 587 | 588 | async _SetStatesArray(array) { 589 | if (array.Values.length === 0) { 590 | this.emptyCount++; 591 | } else { 592 | this.emptyCount = 0; 593 | } 594 | 595 | if (this.emptyCount >= 10) { 596 | // no data for long time try a restart 597 | this.emptyCount = 0; 598 | await this._mainloop(null); 599 | return; 600 | } 601 | 602 | array.Values.forEach(recVal => { 603 | //this.log.debug("search:" + JSON.stringify(recVal)); 604 | 605 | //find ParamId for ValueId 606 | const findParamObj = ParamObjList.find(element => element.ValueId === recVal.ValueId); 607 | 608 | if (findParamObj) { 609 | for (const key in this.objects) { 610 | if (this.objects[key].native && this.objects[key].native.ParameterId === findParamObj.ParameterId) { 611 | this._setStatesWithDiffTypes(this.objects[key].native.ControlType, key, recVal.Value); 612 | } 613 | } 614 | } 615 | }); 616 | } 617 | 618 | /** 619 | * main loop is called from onReady(), and in case of an error by _ShortPollValueList(), _SetStatesArray() and itself 620 | * 621 | * @param myPublicIp - my current public IP or null if unknown 622 | */ 623 | async _mainloop(myPublicIp) { 624 | timeoutHandler['restartTimeout'] && clearTimeout(timeoutHandler['restartTimeout']); 625 | timeoutHandler['shortPollTimeout'] && clearTimeout(timeoutHandler['shortPollTimeout']); 626 | timeoutHandler['longPollTimeout'] && clearTimeout(timeoutHandler['longPollTimeout']); 627 | 628 | try { 629 | // Note: Wolf Smartset is IP address aware: if we changed or IP, we have to re-init 630 | // if we have a wss matching our configured u/p and our current public IP then use it ... 631 | if (!myPublicIp) { 632 | myPublicIp = await this._getMyPublicIp(); 633 | } 634 | if ( 635 | !this.wss || 636 | this.wss_user != this.config.username || 637 | this.wss_password != this.config.password || 638 | (myPublicIp && this.myPublicIp && this.myPublicIp != myPublicIp) 639 | ) { 640 | // ... otherwise kill old wss object and create a new one 641 | this.wss && (await this.wss.stop()); 642 | this.wss = new wolfsmartset(this.config.username, this.config.password, this); 643 | this.wss_user = this.config.username; 644 | this.wss_password = this.config.password; 645 | if (myPublicIp) { 646 | this.myPublicIp = myPublicIp; 647 | } 648 | } 649 | 650 | const wssInitialized = await this.wss.init(); 651 | if (!wssInitialized) { 652 | throw new Error('Could not initialized WSS session'); 653 | } 654 | 655 | const GUIDesc = await this.wss.getGUIDescription(this.device.GatewayId, this.device.SystemId); 656 | if (GUIDesc) { 657 | ParamObjList = (await this._getParamsWebGui(GUIDesc)) || []; 658 | } else { 659 | throw new Error('Could not get GUIDescription (device might be down)'); 660 | } 661 | 662 | // set adapter to 'connected' 663 | this.setState('info.connection', { val: true, ack: true }); 664 | 665 | if (ParamObjList) { 666 | await this._CreateObjects(ParamObjList); 667 | // create a list of params for each BundleId as defined in the GUI Desc 668 | await this._CreateBundleValuesLists(ParamObjList); 669 | } 670 | 671 | this.objects = await this.getForeignObjectsAsync(`${this.namespace}.*`); 672 | this.log.debug(JSON.stringify(this.objects)); 673 | 674 | if (this.ValueIdListLongCycle.length > 0) { 675 | await this._LongPollValueList(); 676 | } 677 | await this._ShortPollValueList(); 678 | } catch (error) { 679 | this.setState('info.connection', { val: false, ack: true }); 680 | this.log.warn(error); 681 | this.log.warn('Trying again in 60 sec...'); 682 | timeoutHandler['restartTimeout'] = setTimeout(async () => { 683 | this._mainloop(null); 684 | }, 60000); 685 | } 686 | 687 | this.subscribeStates('*'); 688 | } 689 | 690 | // /** 691 | // * Some message was sent to this instance over message box. Used by email, pushover, text2speech, adminUI... 692 | // * Using this method requires "common.messagebox" property to be set to true in io-package.json 693 | // * The main purpose for this handler is to handle Device Listing and Device Confirm from the adapter instance settings UI. 694 | // 695 | // * @param {ioBroker.Message} obj 696 | // */ 697 | async onMessage(obj) { 698 | if (typeof obj === 'object' && obj.message) { 699 | if (obj.command === 'send') { 700 | // e.g. send email or pushover or whatever 701 | this.log.info('send command'); 702 | 703 | // Send response in callback if required 704 | if (obj.callback) { 705 | this.sendTo(obj.from, obj.command, 'Message received', obj.callback); 706 | } 707 | } 708 | 709 | // getDeviceList: triggered by adapter instance settings UI object 'deviceSelect' 710 | if (obj.command === 'getDeviceList') { 711 | this.log.debug('getDeviceList ...'); 712 | let myPublicIp; 713 | let devicelist; 714 | let getDeviceListResponse; 715 | let adminWss; 716 | 717 | try { 718 | if (obj.message.username == '' || obj.message.password == '') { 719 | throw new Error('Please set username and password'); 720 | } 721 | 722 | // check if we can use an already existing wss object from running adapter instance, otherwise create one 723 | // Note: Wolf Smartset is IP address aware: if we changed or IP, we have to re-init 724 | myPublicIp = await this._getMyPublicIp(); 725 | if ( 726 | !this.wss || 727 | this.wss_user != obj.message.username || 728 | this.wss_password != obj.message.password || 729 | (myPublicIp && this.myPublicIp && this.myPublicIp != myPublicIp) 730 | ) { 731 | adminWss = new wolfsmartset(obj.message.username, obj.message.password, this); 732 | } else { 733 | adminWss = this.wss; 734 | } 735 | 736 | devicelist = await adminWss.adminGetDevicelist(); 737 | if (typeof devicelist !== 'undefined') { 738 | function convertToSelectEntry(value) { 739 | return { label: value.Name, value: JSON.stringify(value) }; 740 | } 741 | getDeviceListResponse = devicelist.map(convertToSelectEntry); 742 | this.log.debug(`getDeviceList: returning '${JSON.stringify(getDeviceListResponse)}`); 743 | } else { 744 | getDeviceListResponse = [{ label: 'No devices found', value: '' }]; 745 | this.log.debug(`getDeviceList: got no devicelist`); 746 | } 747 | } catch (error) { 748 | getDeviceListResponse = [{ label: error.message, value: '' }]; 749 | this.wss = null; 750 | this.log.debug(`getDeviceList: got error '${error.message}'`); 751 | } 752 | this.sendTo(obj.from, obj.command, getDeviceListResponse, obj.callback); 753 | // if getDeviceList was successful and this adapter instance has currently no wss object 754 | // then store our wss instance for use by adapter instance 755 | if (typeof devicelist !== 'undefined' && devicelist.length > 0 && !this.wss) { 756 | this.wss = adminWss; 757 | this.wss_user = obj.message.username; 758 | this.wss_password = obj.message.password; 759 | if (myPublicIp) { 760 | this.myPublicIp = myPublicIp; 761 | } 762 | } 763 | } 764 | 765 | // confirmDevice: triggered by adapter instance settings UI object 'deviceConfirm' 766 | if (obj.command === 'confirmDevice') { 767 | this.log.info('confirmDevice'); 768 | let myDevice; 769 | let confirmDeviceResponse; 770 | 771 | try { 772 | let jsonStringNoCrNl = obj.message.deviceObject.replace(/[\r\n]/g, ' '); 773 | myDevice = JSON.parse(jsonStringNoCrNl); 774 | 775 | if ( 776 | typeof myDevice.Name !== 'undefined' && 777 | typeof myDevice.Id !== 'undefined' && 778 | typeof myDevice.GatewayId !== 'undefined' 779 | ) { 780 | confirmDeviceResponse = { 781 | native: { 782 | deviceName: `${myDevice.Name}`, 783 | device: jsonStringNoCrNl, 784 | }, 785 | }; 786 | } else { 787 | confirmDeviceResponse = { 788 | error: `No valid device selected: got '${obj.message.deviceObject}'`, 789 | }; 790 | } 791 | } catch (error) { 792 | confirmDeviceResponse = { 793 | error: `No device selected: got '${obj.message.deviceObject}', error: ${error.message}`, 794 | }; 795 | } 796 | this.sendTo(obj.from, obj.command, confirmDeviceResponse, obj.callback); 797 | } 798 | } 799 | } 800 | 801 | /** 802 | * Is called when databases are connected and adapter received configuration. 803 | */ 804 | async onReady() { 805 | this.onlinePoll = 4; 806 | this.emptyCount = 0; 807 | 808 | try { 809 | this.device = JSON.parse(this.config.device); 810 | 811 | if (typeof this.device.Id !== 'undefined') { 812 | this.device.SystemId = this.device.Id; 813 | } 814 | 815 | if ( 816 | this.config.username !== '' && 817 | this.config.password !== '' && 818 | this.config.deviceName !== '' && 819 | typeof this.device.GatewayId !== 'undefined' && 820 | typeof this.device.SystemId !== 'undefined' 821 | ) { 822 | const myPublicIp = await this._getMyPublicIp(); 823 | await this._mainloop(myPublicIp); 824 | } else { 825 | this.log.warn('Please configure username, password and device in adapter instance settings'); 826 | this.setState('info.connection', { val: false, ack: true }); 827 | } 828 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 829 | } catch (error) { 830 | this.log.warn('Please configure username, password and device in adapter instance settings'); 831 | this.setState('info.connection', { val: false, ack: true }); 832 | } 833 | } 834 | 835 | /** 836 | * Is called when adapter shuts down - callback has to be called under any circumstances! 837 | * 838 | * @param callback - callback function 839 | */ 840 | onUnload(callback) { 841 | try { 842 | timeoutHandler['shortPollTimeout'] && clearTimeout(timeoutHandler['shortPollTimeout']); 843 | timeoutHandler['longPollTimeout'] && clearTimeout(timeoutHandler['longPollTimeout']); 844 | 845 | timeoutHandler['restartTimeout'] && clearTimeout(timeoutHandler['restartTimeout']); 846 | 847 | this.wss.stop(); 848 | this.wss = null; 849 | 850 | callback(); 851 | } catch { 852 | callback(); 853 | } 854 | } 855 | 856 | /** 857 | * Is called if a subscribed state changes 858 | * 859 | * @param id - value id 860 | * @param state - value state 861 | */ 862 | async onStateChange(id, state) { 863 | if (state && !state.ack) { 864 | //const ParamId = id.split('.').pop(); 865 | const obj = await this.getObjectAsync(id); 866 | if (obj) { 867 | const findParamObj = ParamObjList.find(element => element.ParameterId === obj.native.ParameterId); 868 | 869 | this.log.info(`Change value for: ${obj.common.name}: ${JSON.stringify(state)}`); 870 | 871 | try { 872 | const answer = await this.wss.setValList(this.device.GatewayId, this.device.SystemId, [ 873 | { 874 | ValueId: findParamObj.ValueId, 875 | ParameterId: obj.native.ParameterId, 876 | Value: String(state.val), 877 | ParameterName: obj.common.name, 878 | }, 879 | ]); 880 | if (typeof answer.Values !== 'undefined') { 881 | this.setState(id, { 882 | val: state.val, 883 | ack: true, 884 | }); 885 | await this._SetStatesArray(answer); 886 | } 887 | } catch (err) { 888 | this.log.error(err); 889 | } 890 | } 891 | } 892 | } 893 | } 894 | 895 | // @ts-expect-error parent is a valid property on module 896 | if (module.parent) { 897 | // Export the constructor in compact mode 898 | module.exports = options => new WolfSmartsetAdapter(options); 899 | } else { 900 | // otherwise start the instance directly 901 | new WolfSmartsetAdapter(); 902 | } 903 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # ioBroker Adapter Development with GitHub Copilot 2 | 3 | **Version:** 0.4.2 4 | **Template Source:** https://github.com/DrozmotiX/ioBroker-Copilot-Instructions 5 | 6 | This file contains instructions and best practices for GitHub Copilot when working on ioBroker adapter development. 7 | 8 | ## Project Context 9 | 10 | You are working on an ioBroker adapter. ioBroker is an integration platform for the Internet of Things, focused on building smart home and industrial IoT solutions. Adapters are plugins that connect ioBroker to external systems, devices, or services. 11 | 12 | ## Adapter-Specific Context 13 | - **Adapter Name**: iobroker.wolf-smartset 14 | - **Primary Function**: Connect Wolf cloud to IoBroker - integrates Wolf heating systems via Wolf Smartset cloud API 15 | - **Target System**: Wolf heating/climate devices equipped with ISM7i WLAN/LAN module (Link Home) 16 | - **Connection Type**: Cloud-based API integration to Wolf Smartset server (https://wolf-smartset.com) 17 | - **Authentication**: OAuth2-based authentication with username/password and expert mode support 18 | - **Key Features**: 19 | - Dual poll cycles (short/long intervals) to optimize server load 20 | - Expert login capabilities for advanced parameter access 21 | - BundleId-based parameter grouping and polling 22 | - Public IP change detection for re-authentication 23 | - API profiling and usage tracking 24 | - **External Dependencies**: 25 | - axios for HTTP requests 26 | - openid-client for OAuth2 authentication 27 | - Wolf Smartset cloud API 28 | - **Configuration**: JSON-based admin interface with device selection, poll intervals, and expert settings 29 | 30 | ## Development Patterns 31 | 32 | ### Error Handling Patterns 33 | - Implement proper Wolf API error handling with automatic re-authentication 34 | - Handle server-side minimum poll interval enforcement (60 seconds) 35 | - Implement graceful degradation when Expert mode access fails 36 | - Log periodic messages with appropriate levels (info for routine, warn for issues) 37 | 38 | ### Authentication & Session Management 39 | ```javascript 40 | // Example authentication patterns for Wolf Smartset 41 | async refreshAuthToken() { 42 | try { 43 | // Implement OpenID client token refresh 44 | // Handle expert vs user mode authentication 45 | // Update session tokens and handle failures 46 | } catch (error) { 47 | this.log.info('_refreshAuthToken(): ERROR - authentication failed, will retry'); 48 | // Trigger re-initialization if needed 49 | } 50 | } 51 | ``` 52 | 53 | ### API Request Patterns 54 | ```javascript 55 | // Wolf API polling with BundleId support 56 | async pollParameterValues(bundleId, parameterIds) { 57 | try { 58 | // Respect server minimum poll intervals 59 | // Handle bundle ID validation 60 | // Process parameter value updates 61 | // Update API profiling metrics 62 | } catch (error) { 63 | this.log.warn(`Poll request failed for bundle ${bundleId}: ${error.message}`); 64 | } 65 | } 66 | ``` 67 | 68 | ### State Management 69 | - Use proper ioBroker object creation for Wolf parameter hierarchies (Benutzer/Fachmann) 70 | - Handle Min/MaxValueCondition constraints from Wolf API 71 | - Implement proper state updates with value validation 72 | - Support time programs, party mode, and vacation mode objects 73 | 74 | ## Testing 75 | 76 | ### Unit Testing 77 | - Use Jest as the primary testing framework for ioBroker adapters 78 | - Create tests for all adapter main functions and helper methods 79 | - Test error handling scenarios and edge cases 80 | - Mock external API calls and hardware dependencies 81 | - For adapters connecting to APIs/devices not reachable by internet, provide example data files to allow testing of functionality without live connections 82 | - Example test structure: 83 | ```javascript 84 | describe('AdapterName', () => { 85 | let adapter; 86 | 87 | beforeEach(() => { 88 | // Setup test adapter instance 89 | }); 90 | 91 | test('should initialize correctly', () => { 92 | // Test adapter initialization 93 | }); 94 | }); 95 | ``` 96 | 97 | ### Integration Testing 98 | 99 | **IMPORTANT**: Use the official `@iobroker/testing` framework for all integration tests. This is the ONLY correct way to test ioBroker adapters. 100 | 101 | **Official Documentation**: https://github.com/ioBroker/testing 102 | 103 | #### Framework Structure 104 | Integration tests MUST follow this exact pattern: 105 | 106 | ```javascript 107 | const path = require('path'); 108 | const { tests } = require('@iobroker/testing'); 109 | 110 | // Define test coordinates or configuration 111 | const TEST_COORDINATES = '52.520008,13.404954'; // Berlin 112 | const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); 113 | 114 | // Use tests.integration() with defineAdditionalTests 115 | tests.integration(path.join(__dirname, '..'), { 116 | defineAdditionalTests({ suite }) { 117 | suite('Test adapter with specific configuration', (getHarness) => { 118 | let harness; 119 | 120 | before(() => { 121 | harness = getHarness(); 122 | }); 123 | 124 | it('should configure and start adapter', function () { 125 | return new Promise(async (resolve, reject) => { 126 | try { 127 | harness = getHarness(); 128 | 129 | // Get adapter object using promisified pattern 130 | const obj = await new Promise((res, rej) => { 131 | harness.objects.getObject('system.adapter.your-adapter.0', (err, o) => { 132 | if (err) return rej(err); 133 | res(o); 134 | }); 135 | }); 136 | 137 | if (!obj) { 138 | return reject(new Error('Adapter object not found')); 139 | } 140 | 141 | // Configure adapter properties 142 | Object.assign(obj.native, { 143 | position: TEST_COORDINATES, 144 | createCurrently: true, 145 | createHourly: true, 146 | createDaily: true, 147 | // Add other configuration as needed 148 | }); 149 | 150 | // Set the updated configuration 151 | harness.objects.setObject(obj._id, obj); 152 | 153 | console.log('✅ Step 1: Configuration written, starting adapter...'); 154 | 155 | // Start adapter and wait 156 | await harness.startAdapterAndWait(); 157 | 158 | console.log('✅ Step 2: Adapter started'); 159 | 160 | // Wait for adapter to process data 161 | const waitMs = 15000; 162 | await wait(waitMs); 163 | 164 | console.log('🔍 Step 3: Checking states after adapter run...'); 165 | 166 | // Validate expected states exist 167 | const states = await harness.states.getStatesAsync('your-adapter.0.*'); 168 | 169 | // Assert expected behavior 170 | expect(states).toBeDefined(); 171 | expect(Object.keys(states).length).toBeGreaterThan(0); 172 | 173 | resolve(); 174 | } catch (error) { 175 | reject(error); 176 | } 177 | }); 178 | }).timeout(60000); 179 | }); 180 | } 181 | }); 182 | ``` 183 | 184 | #### Testing Both Success AND Failure Scenarios 185 | 186 | **IMPORTANT**: For every "it works" test, implement corresponding "it doesn't work and fails" tests. This ensures proper error handling and validates that your adapter fails gracefully when expected. 187 | 188 | ```javascript 189 | // Example: Testing successful configuration 190 | it('should configure and start adapter with valid configuration', function () { 191 | return new Promise(async (resolve, reject) => { 192 | // ... successful configuration test as shown above 193 | }); 194 | }).timeout(40000); 195 | 196 | // Example: Testing failure scenarios 197 | it('should NOT create daily states when daily is disabled', function () { 198 | return new Promise(async (resolve, reject) => { 199 | try { 200 | harness = getHarness(); 201 | 202 | console.log('🔍 Step 1: Fetching adapter object...'); 203 | const obj = await new Promise((res, rej) => { 204 | harness.objects.getObject('system.adapter.your-adapter.0', (err, o) => { 205 | if (err) return rej(err); 206 | res(o); 207 | }); 208 | }); 209 | 210 | if (!obj) return reject(new Error('Adapter object not found')); 211 | console.log('✅ Step 1.5: Adapter object loaded'); 212 | 213 | console.log('🔍 Step 2: Updating adapter config...'); 214 | Object.assign(obj.native, { 215 | position: TEST_COORDINATES, 216 | createCurrently: false, 217 | createHourly: true, 218 | createDaily: false, // Daily disabled for this test 219 | }); 220 | 221 | await new Promise((res, rej) => { 222 | harness.objects.setObject(obj._id, obj, (err) => { 223 | if (err) return rej(err); 224 | console.log('✅ Step 2.5: Adapter object updated'); 225 | res(undefined); 226 | }); 227 | }); 228 | 229 | console.log('🔍 Step 3: Starting adapter...'); 230 | await harness.startAdapterAndWait(); 231 | console.log('✅ Step 4: Adapter started'); 232 | 233 | console.log('⏳ Step 5: Waiting 20 seconds for states...'); 234 | await new Promise((res) => setTimeout(res, 20000)); 235 | 236 | console.log('🔍 Step 6: Fetching state IDs...'); 237 | const stateIds = await harness.dbConnection.getStateIDs('your-adapter.0.*'); 238 | 239 | console.log(`📊 Step 7: Found ${stateIds.length} total states`); 240 | 241 | const hourlyStates = stateIds.filter((key) => key.includes('hourly')); 242 | if (hourlyStates.length > 0) { 243 | console.log(`✅ Step 8: Correctly ${hourlyStates.length} hourly weather states created`); 244 | } else { 245 | console.log('❌ Step 8: No hourly states created (test failed)'); 246 | return reject(new Error('Expected hourly states but found none')); 247 | } 248 | 249 | // Check daily states should NOT be present 250 | const dailyStates = stateIds.filter((key) => key.includes('daily')); 251 | if (dailyStates.length === 0) { 252 | console.log(`✅ Step 9: No daily states found as expected`); 253 | } else { 254 | console.log(`❌ Step 9: Daily states present (${dailyStates.length}) (test failed)`); 255 | return reject(new Error('Expected no daily states but found some')); 256 | } 257 | 258 | await harness.stopAdapter(); 259 | console.log('🛑 Step 10: Adapter stopped'); 260 | 261 | resolve(true); 262 | } catch (error) { 263 | reject(error); 264 | } 265 | }); 266 | }).timeout(40000); 267 | 268 | // Example: Testing missing required configuration 269 | it('should handle missing required configuration properly', function () { 270 | return new Promise(async (resolve, reject) => { 271 | try { 272 | harness = getHarness(); 273 | 274 | console.log('🔍 Step 1: Fetching adapter object...'); 275 | const obj = await new Promise((res, rej) => { 276 | harness.objects.getObject('system.adapter.your-adapter.0', (err, o) => { 277 | if (err) return rej(err); 278 | res(o); 279 | }); 280 | }); 281 | 282 | if (!obj) return reject(new Error('Adapter object not found')); 283 | 284 | console.log('🔍 Step 2: Removing required configuration...'); 285 | // Remove required configuration to test failure handling 286 | delete obj.native.position; // This should cause failure or graceful handling 287 | 288 | await new Promise((res, rej) => { 289 | harness.objects.setObject(obj._id, obj, (err) => { 290 | if (err) return rej(err); 291 | res(undefined); 292 | }); 293 | }); 294 | 295 | console.log('🔍 Step 3: Starting adapter...'); 296 | await harness.startAdapterAndWait(); 297 | 298 | console.log('⏳ Step 4: Waiting for adapter to process...'); 299 | await new Promise((res) => setTimeout(res, 10000)); 300 | 301 | console.log('🔍 Step 5: Checking adapter behavior...'); 302 | const stateIds = await harness.dbConnection.getStateIDs('your-adapter.0.*'); 303 | 304 | // Check if adapter handled missing configuration gracefully 305 | if (stateIds.length === 0) { 306 | console.log('✅ Adapter properly handled missing configuration - no invalid states created'); 307 | resolve(true); 308 | } else { 309 | // If states were created, check if they're in error state 310 | const connectionState = await new Promise((res, rej) => { 311 | harness.states.getState('your-adapter.0.info.connection', (err, state) => { 312 | if (err) return rej(err); 313 | res(state); 314 | }); 315 | }); 316 | 317 | if (!connectionState || connectionState.val === false) { 318 | console.log('✅ Adapter properly failed with missing configuration'); 319 | resolve(true); 320 | } else { 321 | console.log('❌ Adapter should have failed or handled missing config gracefully'); 322 | reject(new Error('Adapter should have handled missing configuration')); 323 | } 324 | } 325 | 326 | await harness.stopAdapter(); 327 | } catch (error) { 328 | console.log('✅ Adapter correctly threw error with missing configuration:', error.message); 329 | resolve(true); 330 | } 331 | }); 332 | }).timeout(40000); 333 | ``` 334 | 335 | #### Advanced State Access Patterns 336 | 337 | For testing adapters that create multiple states, use bulk state access methods to efficiently verify large numbers of states: 338 | 339 | ```javascript 340 | it('should create and verify multiple states', () => new Promise(async (resolve, reject) => { 341 | // Configure and start adapter first... 342 | harness.objects.getObject('system.adapter.tagesschau.0', async (err, obj) => { 343 | if (err) { 344 | console.error('Error getting adapter object:', err); 345 | reject(err); 346 | return; 347 | } 348 | 349 | // Configure adapter as needed 350 | obj.native.someConfig = 'test-value'; 351 | harness.objects.setObject(obj._id, obj); 352 | 353 | await harness.startAdapterAndWait(); 354 | 355 | // Wait for adapter to create states 356 | setTimeout(() => { 357 | // Access bulk states using pattern matching 358 | harness.dbConnection.getStateIDs('tagesschau.0.*').then(stateIds => { 359 | if (stateIds && stateIds.length > 0) { 360 | harness.states.getStates(stateIds, (err, allStates) => { 361 | if (err) { 362 | console.error('❌ Error getting states:', err); 363 | reject(err); // Properly fail the test instead of just resolving 364 | return; 365 | } 366 | 367 | // Verify states were created and have expected values 368 | const expectedStates = ['tagesschau.0.info.connection', 'tagesschau.0.articles.0.title']; 369 | let foundStates = 0; 370 | 371 | for (const stateId of expectedStates) { 372 | if (allStates[stateId]) { 373 | foundStates++; 374 | console.log(`✅ Found expected state: ${stateId}`); 375 | } else { 376 | console.log(`❌ Missing expected state: ${stateId}`); 377 | } 378 | } 379 | 380 | if (foundStates === expectedStates.length) { 381 | console.log('✅ All expected states were created successfully'); 382 | resolve(); 383 | } else { 384 | reject(new Error(`Only ${foundStates}/${expectedStates.length} expected states were found`)); 385 | } 386 | }); 387 | } else { 388 | reject(new Error('No states found matching pattern tagesschau.0.*')); 389 | } 390 | }).catch(reject); 391 | }, 20000); // Allow more time for multiple state creation 392 | }); 393 | })).timeout(45000); 394 | ``` 395 | 396 | #### Key Integration Testing Rules 397 | 398 | 1. **NEVER test API URLs directly** - Let the adapter handle API calls 399 | 2. **ALWAYS use the harness** - `getHarness()` provides the testing environment 400 | 3. **Configure via objects** - Use `harness.objects.setObject()` to set adapter configuration 401 | 4. **Start properly** - Use `harness.startAdapterAndWait()` to start the adapter 402 | 5. **Check states** - Use `harness.states.getState()` to verify results 403 | 6. **Use timeouts** - Allow time for async operations with appropriate timeouts 404 | 7. **Test real workflow** - Initialize → Configure → Start → Verify States 405 | 406 | #### Workflow Dependencies 407 | Integration tests should run ONLY after lint and adapter tests pass: 408 | 409 | ```yaml 410 | integration-tests: 411 | needs: [check-and-lint, adapter-tests] 412 | runs-on: ubuntu-latest 413 | steps: 414 | - name: Run integration tests 415 | run: npx mocha test/integration-*.js --exit 416 | ``` 417 | 418 | #### What NOT to Do 419 | ❌ Direct API testing: `axios.get('https://api.example.com')` 420 | ❌ Mock adapters: `new MockAdapter()` 421 | ❌ Direct internet calls in tests 422 | ❌ Bypassing the harness system 423 | 424 | #### What TO Do 425 | ✅ Use `@iobroker/testing` framework 426 | ✅ Configure via `harness.objects.setObject()` 427 | ✅ Start via `harness.startAdapterAndWait()` 428 | ✅ Test complete adapter lifecycle 429 | ✅ Verify states via `harness.states.getState()` 430 | ✅ Allow proper timeouts for async operations 431 | 432 | ### API Testing with Credentials 433 | For adapters that connect to external APIs requiring authentication, implement comprehensive credential testing: 434 | 435 | #### Password Encryption for Integration Tests 436 | When creating integration tests that need encrypted passwords (like those marked as `encryptedNative` in io-package.json): 437 | 438 | 1. **Read system secret**: Use `harness.objects.getObjectAsync("system.config")` to get `obj.native.secret` 439 | 2. **Apply XOR encryption**: Implement the encryption algorithm: 440 | ```javascript 441 | async function encryptPassword(harness, password) { 442 | const systemConfig = await harness.objects.getObjectAsync("system.config"); 443 | if (!systemConfig || !systemConfig.native || !systemConfig.native.secret) { 444 | throw new Error("Could not retrieve system secret for password encryption"); 445 | } 446 | 447 | const secret = systemConfig.native.secret; 448 | let result = ''; 449 | for (let i = 0; i < password.length; ++i) { 450 | result += String.fromCharCode(secret[i % secret.length].charCodeAt(0) ^ password.charCodeAt(i)); 451 | } 452 | return result; 453 | } 454 | ``` 455 | 3. **Store encrypted password**: Set the encrypted result in adapter config, not the plain text 456 | 4. **Result**: Adapter will properly decrypt and use credentials, enabling full API connectivity testing 457 | 458 | #### Demo Credentials Testing Pattern 459 | - Use provider demo credentials when available (e.g., `demo@api-provider.com` / `demo`) 460 | - Create separate test file (e.g., `test/integration-demo.js`) for credential-based tests 461 | - Add npm script: `"test:integration-demo": "mocha test/integration-demo --exit"` 462 | - Implement clear success/failure criteria with recognizable log messages 463 | - Expected success pattern: Look for specific adapter initialization messages 464 | - Test should fail clearly with actionable error messages for debugging 465 | 466 | #### Enhanced Test Failure Handling 467 | ```javascript 468 | it("Should connect to API with demo credentials", async () => { 469 | // ... setup and encryption logic ... 470 | 471 | const connectionState = await harness.states.getStateAsync("adapter.0.info.connection"); 472 | 473 | if (connectionState && connectionState.val === true) { 474 | console.log("✅ SUCCESS: API connection established"); 475 | return true; 476 | } else { 477 | throw new Error("API Test Failed: Expected API connection to be established with demo credentials. " + 478 | "Check logs above for specific API errors (DNS resolution, 401 Unauthorized, network issues, etc.)"); 479 | } 480 | }).timeout(120000); // Extended timeout for API calls 481 | ``` 482 | 483 | #### Critical Testing Requirements 484 | 485 | 1. **Always use `tests.integration()`** - Never try to manually instantiate adapters 486 | 2. **Use `defineAdditionalTests({ suite })`** - This is the correct way to add custom tests 487 | 3. **Get harness with `getHarness()`** - Don't try to create it manually 488 | 4. **Use promisified patterns** - Wrap callback-based operations in Promises 489 | 5. **Set adequate timeouts** - Integration tests often need 30-60 seconds 490 | 6. **Validate state creation** - Always check that expected states are created 491 | 492 | #### Common Mistakes to Avoid 493 | 494 | ❌ **Wrong**: Trying to manually create adapter instances 495 | ```javascript 496 | // DON'T DO THIS 497 | const MyAdapter = require('../main'); 498 | const adapter = new MyAdapter({...}); 499 | ``` 500 | 501 | ❌ **Wrong**: Using synchronous patterns with harness 502 | ```javascript 503 | // DON'T DO THIS 504 | const obj = harness.objects.getObject('system.adapter.my-adapter.0'); 505 | ``` 506 | 507 | ✅ **Correct**: Use the official testing framework 508 | ```javascript 509 | // DO THIS 510 | tests.integration(path.join(__dirname, '..'), { 511 | defineAdditionalTests({ suite }) { 512 | suite('My Tests', (getHarness) => { 513 | // Your tests here 514 | }); 515 | } 516 | }); 517 | ``` 518 | 519 | ### Testing Authentication & API Integration 520 | For the Wolf SmartSet adapter specifically: 521 | - Mock Wolf Smartset API responses for unit tests 522 | - Create integration tests with demo credentials when available 523 | - Test both User and Expert authentication modes 524 | - Validate parameter value parsing and state creation 525 | - Test poll cycle management and BundleId handling 526 | 527 | ## README Updates 528 | 529 | ### Required Sections 530 | When updating README.md files, ensure these sections are present and well-documented: 531 | 532 | 1. **Installation** - Clear npm/ioBroker admin installation steps 533 | 2. **Configuration** - Detailed configuration options with examples 534 | 3. **Usage** - Practical examples and use cases 535 | 4. **Changelog** - Version history and changes (use "## **WORK IN PROGRESS**" section for ongoing changes following AlCalzone release-script standard) 536 | 5. **License** - License information (typically MIT for ioBroker adapters) 537 | 6. **Support** - Links to issues, discussions, and community support 538 | 539 | ### Documentation Standards 540 | - Use clear, concise language 541 | - Include code examples for configuration 542 | - Add screenshots for admin interface when applicable 543 | - Maintain multilingual support (at minimum English and German) 544 | - When creating PRs, add entries to README under "## **WORK IN PROGRESS**" section following ioBroker release script standard 545 | - Always reference related issues in commits and PR descriptions (e.g., "solves #xx" or "fixes #xx") 546 | 547 | ### Mandatory README Updates for PRs 548 | For **every PR or new feature**, always add a user-friendly entry to README.md: 549 | 550 | - Add entries under `## **WORK IN PROGRESS**` section before committing 551 | - Use format: `* (author) **TYPE**: Description of user-visible change` 552 | - Types: **NEW** (features), **FIXED** (bugs), **ENHANCED** (improvements), **TESTING** (test additions), **CI/CD** (automation) 553 | - Focus on user impact, not technical implementation details 554 | - Example: `* (DutchmanNL) **FIXED**: Adapter now properly validates login credentials instead of always showing "credentials missing"` 555 | 556 | ### Documentation Workflow Standards 557 | - **Mandatory README updates**: Establish requirement to update README.md for every PR/feature 558 | - **Standardized documentation**: Create consistent format and categories for changelog entries 559 | - **Enhanced development workflow**: Integrate documentation requirements into standard development process 560 | 561 | ### Changelog Management with AlCalzone Release-Script 562 | Follow the [AlCalzone release-script](https://github.com/AlCalzone/release-script) standard for changelog management: 563 | 564 | #### Format Requirements 565 | - Always use `## **WORK IN PROGRESS**` as the placeholder for new changes 566 | - Add all PR/commit changes under this section until ready for release 567 | - Never modify version numbers manually - only when merging to main branch 568 | - Maintain this format in README.md or CHANGELOG.md: 569 | 570 | ```markdown 571 | # Changelog 572 | 573 | 577 | 578 | ## **WORK IN PROGRESS** 579 | 580 | - Did some changes 581 | - Did some more changes 582 | 583 | ## v0.1.0 (2023-01-01) 584 | Initial release 585 | ``` 586 | 587 | #### Workflow Process 588 | - **During Development**: All changes go under `## **WORK IN PROGRESS**` 589 | - **For Every PR**: Add user-facing changes to the WORK IN PROGRESS section 590 | - **Before Merge**: Version number and date are only added when merging to main 591 | - **Release Process**: The release-script automatically converts the placeholder to the actual version 592 | 593 | #### Change Entry Format 594 | Use this consistent format for changelog entries: 595 | - `- (author) **TYPE**: User-friendly description of the change` 596 | - Types: **NEW** (features), **FIXED** (bugs), **ENHANCED** (improvements) 597 | - Focus on user impact, not technical implementation details 598 | - Reference related issues: "fixes #XX" or "solves #XX" 599 | 600 | #### Example Entry 601 | ```markdown 602 | ## **WORK IN PROGRESS** 603 | 604 | - (DutchmanNL) **FIXED**: Adapter now properly validates login credentials instead of always showing "credentials missing" (fixes #25) 605 | - (DutchmanNL) **NEW**: Added support for device discovery to simplify initial setup 606 | ``` 607 | 608 | ## Dependency Updates 609 | 610 | ### Package Management 611 | - Always use `npm` for dependency management in ioBroker adapters 612 | - When working on new features in a repository with an existing package-lock.json file, use `npm ci` to install dependencies. Use `npm install` only when adding or updating dependencies. 613 | - Keep dependencies minimal and focused 614 | - Only update dependencies to latest stable versions when necessary or in separate Pull Requests. Avoid updating dependencies when adding features that don't require these updates. 615 | - When you modify `package.json`: 616 | 1. Run `npm install` to update and sync `package-lock.json`. 617 | 2. If `package-lock.json` was updated, commit both `package.json` and `package-lock.json`. 618 | 619 | ### Dependency Best Practices 620 | - Prefer built-in Node.js modules when possible 621 | - Use `@iobroker/adapter-core` for adapter base functionality 622 | - Avoid deprecated packages 623 | - Document any specific version requirements 624 | 625 | ## JSON-Config Admin Instructions 626 | 627 | ### Configuration Schema 628 | When creating admin configuration interfaces: 629 | 630 | - Use JSON-Config format for modern ioBroker admin interfaces 631 | - Provide clear labels and help text for all configuration options 632 | - Include input validation and error messages 633 | - Group related settings logically 634 | - Example structure: 635 | ```json 636 | { 637 | "type": "panel", 638 | "items": { 639 | "host": { 640 | "type": "text", 641 | "label": "Host address", 642 | "help": "IP address or hostname of the device" 643 | } 644 | } 645 | } 646 | ``` 647 | 648 | ### Admin Interface Guidelines 649 | - Use consistent naming conventions 650 | - Provide sensible default values 651 | - Include validation for required fields 652 | - Add tooltips for complex configuration options 653 | - Ensure translations are available for all supported languages (minimum English and German) 654 | - Write end-user friendly labels and descriptions, avoiding technical jargon where possible 655 | 656 | ## Best Practices for Dependencies 657 | 658 | ### HTTP Client Libraries 659 | - **Preferred:** Use native `fetch` API (Node.js 20+ required for adapters; built-in since Node.js 18) 660 | - **Avoid:** `axios` unless specific features are required (reduces bundle size) 661 | 662 | ### Example with fetch: 663 | ```javascript 664 | try { 665 | const response = await fetch('https://api.example.com/data'); 666 | if (!response.ok) { 667 | throw new Error(`HTTP ${response.status}: ${response.statusText}`); 668 | } 669 | const data = await response.json(); 670 | } catch (error) { 671 | this.log.error(`API request failed: ${error.message}`); 672 | } 673 | ``` 674 | 675 | ### Other Dependency Recommendations 676 | - **Logging:** Use adapter built-in logging (`this.log.*`) 677 | - **Scheduling:** Use adapter built-in timers and intervals 678 | - **File operations:** Use Node.js `fs/promises` for async file operations 679 | - **Configuration:** Use adapter config system rather than external config libraries 680 | 681 | ## ioBroker-Specific Development 682 | 683 | ### Admin Interface (JSON Config) 684 | - Use jsonConfig.json for configuration UI 685 | - Implement device selection dropdown with Wolf Smartset API integration 686 | - Support for parameter bundle configuration tables 687 | - Validation of poll intervals against server requirements 688 | 689 | ### State and Object Management 690 | ```javascript 691 | // Wolf-specific object creation patterns 692 | await this.setObjectNotExistsAsync(id, { 693 | type: 'state', 694 | common: { 695 | name: parameterName, 696 | type: getIoBrokerType(wolfType), 697 | role: getIoBrokerRole(wolfControlType), 698 | read: true, 699 | write: isWritable, 700 | min: wolfParam.MinValue || wolfParam.MinValueCondition, 701 | max: wolfParam.MaxValue || wolfParam.MaxValueCondition, 702 | unit: wolfParam.Unit 703 | }, 704 | native: { 705 | parameterId: wolfParam.ParameterId, 706 | bundleId: wolfParam.BundleId 707 | } 708 | }); 709 | ``` 710 | 711 | ### Connection and Lifecycle Management 712 | ```javascript 713 | // ioBroker adapter lifecycle for cloud services 714 | class WolfSmartsetAdapter extends utils.Adapter { 715 | constructor(options = {}) { 716 | super({ ...options, name: 'wolf-smartset' }); 717 | this.on('ready', this.onReady.bind(this)); 718 | this.on('stateChange', this.onStateChange.bind(this)); 719 | this.on('unload', this.onUnload.bind(this)); 720 | } 721 | 722 | async onReady() { 723 | // Initialize Wolf API connection 724 | // Set up polling intervals 725 | // Create device objects 726 | this.setState('info.connection', true, true); 727 | } 728 | 729 | onUnload(callback) { 730 | try { 731 | // Clear polling timers 732 | if (this.shortPollTimer) clearInterval(this.shortPollTimer); 733 | if (this.longPollTimer) clearInterval(this.longPollTimer); 734 | // Cleanup Wolf API connections 735 | this.setState('info.connection', false, true); 736 | } catch (e) { 737 | callback(); 738 | } 739 | } 740 | } 741 | ``` 742 | 743 | ## Error Handling and Logging 744 | 745 | ### Adapter Error Patterns 746 | - Always catch and log errors appropriately 747 | - Use adapter log levels (error, warn, info, debug) 748 | - Provide meaningful, user-friendly error messages that help users understand what went wrong 749 | - Handle network failures gracefully 750 | - Implement retry mechanisms where appropriate 751 | - Always clean up timers, intervals, and other resources in the `unload()` method 752 | 753 | ### Example Error Handling: 754 | ```javascript 755 | try { 756 | await this.connectToDevice(); 757 | } catch (error) { 758 | this.log.error(`Failed to connect to device: ${error.message}`); 759 | this.setState('info.connection', false, true); 760 | // Implement retry logic if needed 761 | } 762 | ``` 763 | 764 | ### Timer and Resource Cleanup: 765 | ```javascript 766 | // In your adapter class 767 | private connectionTimer?: NodeJS.Timeout; 768 | 769 | async onReady() { 770 | this.connectionTimer = setInterval(() => { 771 | this.checkConnection(); 772 | }, 30000); 773 | } 774 | 775 | onUnload(callback) { 776 | try { 777 | // Clean up timers and intervals 778 | if (this.connectionTimer) { 779 | clearInterval(this.connectionTimer); 780 | this.connectionTimer = undefined; 781 | } 782 | // Close connections, clean up resources 783 | callback(); 784 | } catch (e) { 785 | callback(); 786 | } 787 | } 788 | ``` 789 | 790 | ### ioBroker Logging Patterns 791 | ```javascript 792 | // Use appropriate log levels 793 | this.log.error('Critical error that prevents adapter operation'); 794 | this.log.warn('Warning that might affect functionality'); 795 | this.log.info('Important information for users'); 796 | this.log.debug('Detailed information for debugging'); 797 | ``` 798 | 799 | ### Wolf SmartSet Specific Error Handling 800 | - Handle authentication failures with automatic retry 801 | - Manage API rate limiting and minimum poll intervals 802 | - Process Expert mode access issues gracefully 803 | - Handle device offline/online state changes 804 | 805 | ## Code Style and Standards 806 | 807 | - Follow JavaScript/TypeScript best practices 808 | - Use async/await for asynchronous operations 809 | - Implement proper resource cleanup in `unload()` method 810 | - Use semantic versioning for adapter releases 811 | - Include proper JSDoc comments for public methods 812 | 813 | ## CI/CD and Testing Integration 814 | 815 | ### GitHub Actions for API Testing 816 | For adapters with external API dependencies, implement separate CI/CD jobs: 817 | 818 | ```yaml 819 | # Tests API connectivity with demo credentials (runs separately) 820 | demo-api-tests: 821 | if: contains(github.event.head_commit.message, '[skip ci]') == false 822 | 823 | runs-on: ubuntu-22.04 824 | 825 | steps: 826 | - name: Checkout code 827 | uses: actions/checkout@v4 828 | 829 | - name: Use Node.js 20.x 830 | uses: actions/setup-node@v4 831 | with: 832 | node-version: 20.x 833 | cache: 'npm' 834 | 835 | - name: Install dependencies 836 | run: npm ci 837 | 838 | - name: Run demo API tests 839 | run: npm run test:integration-demo 840 | ``` 841 | 842 | ### CI/CD Best Practices 843 | - Run credential tests separately from main test suite 844 | - Use ubuntu-22.04 for consistency 845 | - Don't make credential tests required for deployment 846 | - Provide clear failure messages for API connectivity issues 847 | - Use appropriate timeouts for external API calls (120+ seconds) 848 | 849 | ### Package.json Script Integration 850 | Add dedicated script for credential testing: 851 | ```json 852 | { 853 | "scripts": { 854 | "test:integration-demo": "mocha test/integration-demo --exit" 855 | } 856 | } 857 | ``` 858 | 859 | ### Practical Example: Complete API Testing Implementation 860 | Here's a complete example based on lessons learned from the Discovergy adapter: 861 | 862 | #### test/integration-demo.js 863 | ```javascript 864 | const path = require("path"); 865 | const { tests } = require("@iobroker/testing"); 866 | 867 | // Helper function to encrypt password using ioBroker's encryption method 868 | async function encryptPassword(harness, password) { 869 | const systemConfig = await harness.objects.getObjectAsync("system.config"); 870 | 871 | if (!systemConfig || !systemConfig.native || !systemConfig.native.secret) { 872 | throw new Error("Could not retrieve system secret for password encryption"); 873 | } 874 | 875 | const secret = systemConfig.native.secret; 876 | let result = ''; 877 | for (let i = 0; i < password.length; ++i) { 878 | result += String.fromCharCode(secret[i % secret.length].charCodeAt(0) ^ password.charCodeAt(i)); 879 | } 880 | 881 | return result; 882 | } 883 | 884 | // Run integration tests with demo credentials 885 | tests.integration(path.join(__dirname, ".."), { 886 | defineAdditionalTests({ suite }) { 887 | suite("API Testing with Demo Credentials", (getHarness) => { 888 | let harness; 889 | 890 | before(() => { 891 | harness = getHarness(); 892 | }); 893 | 894 | it("Should connect to API and initialize with demo credentials", async () => { 895 | console.log("Setting up demo credentials..."); 896 | 897 | if (harness.isAdapterRunning()) { 898 | await harness.stopAdapter(); 899 | } 900 | 901 | const encryptedPassword = await encryptPassword(harness, "demo_password"); 902 | 903 | await harness.changeAdapterConfig("your-adapter", { 904 | native: { 905 | username: "demo@provider.com", 906 | password: encryptedPassword, 907 | // other config options 908 | } 909 | }); 910 | 911 | console.log("Starting adapter with demo credentials..."); 912 | await harness.startAdapter(); 913 | 914 | // Wait for API calls and initialization 915 | await new Promise(resolve => setTimeout(resolve, 60000)); 916 | 917 | const connectionState = await harness.states.getStateAsync("your-adapter.0.info.connection"); 918 | 919 | if (connectionState && connectionState.val === true) { 920 | console.log("✅ SUCCESS: API connection established"); 921 | return true; 922 | } else { 923 | throw new Error("API Test Failed: Expected API connection to be established with demo credentials. " + 924 | "Check logs above for specific API errors (DNS resolution, 401 Unauthorized, network issues, etc.)"); 925 | } 926 | }).timeout(120000); 927 | }); 928 | } 929 | }); 930 | ``` 931 | 932 | ## Wolf SmartSet Specific Implementation Details 933 | 934 | ### API Integration Patterns 935 | ```javascript 936 | // Wolf Smartset authentication with OpenID Client 937 | const { Issuer, generators } = require('openid-client'); 938 | 939 | async initializeWolfAPI() { 940 | const issuer = await Issuer.discover('https://wolf-smartset.com/.well-known/openid_configuration'); 941 | this.client = new issuer.Client({ 942 | client_id: 'your-client-id', 943 | client_secret: 'your-client-secret' 944 | }); 945 | } 946 | 947 | // Parameter polling with BundleId management 948 | async pollParameters(bundleId, parameterList) { 949 | const response = await this.wolfAPI.post('/parameters/poll', { 950 | bundleId: bundleId, 951 | parameters: parameterList 952 | }); 953 | 954 | // Process parameter values and update ioBroker states 955 | for (const param of response.data.parameters) { 956 | await this.setStateAsync(`${param.path}.${param.id}`, { 957 | val: param.value, 958 | ack: true 959 | }); 960 | } 961 | } 962 | ``` 963 | 964 | ### Configuration Management 965 | - Support device selection from Wolf Smartset account 966 | - Manage dual poll cycles (short/long intervals) 967 | - Handle Expert mode authentication and parameter access 968 | - Implement BundleId configuration for parameter groups 969 | 970 | ### State Structure 971 | Follow Wolf's parameter hierarchy: 972 | - `Benutzer` (User level parameters) 973 | - `Fachmann` (Expert level parameters) 974 | - Support for time programs, party mode, vacation mode 975 | - API profiling data in `info_api` channel --------------------------------------------------------------------------------