[
  {
    "path": ".eslintrc.json",
    "content": "{\n    \"root\": true,\n    \"env\": {\n        \"es6\": true,\n        \"node\": true,\n        \"mocha\": true\n    },\n    \"extends\": [\n        \"eslint:recommended\"\n    ],\n    \"plugins\": [],\n    \"rules\": {\n        \"indent\": [\n            \"error\",\n            4,\n            {\n                \"SwitchCase\": 1\n            }\n        ],\n        \"no-console\": \"off\",\n        \"no-var\": \"error\",\n        \"no-trailing-spaces\": \"error\",\n        \"prefer-const\": \"error\",\n        \"quotes\": [\n            \"error\",\n            \"single\",\n            {\n                \"avoidEscape\": true,\n                \"allowTemplateLiterals\": true\n            }\n        ],\n        \"semi\": [\n            \"error\",\n            \"always\"\n        ]\n    },\n    \"parserOptions\": {\n        \"ecmaVersion\": 2018\n    }\n}"
  },
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs     diff=csharp\n\n# Standard to msysgit\n*.doc\t diff=astextplain\n*.DOC\t diff=astextplain\n*.docx diff=astextplain\n*.DOCX diff=astextplain\n*.dot  diff=astextplain\n*.DOT  diff=astextplain\n*.pdf  diff=astextplain\n*.PDF\t diff=astextplain\n*.rtf\t diff=astextplain\n*.RTF\t diff=astextplain\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: Apollon77\npatreon: Apollon77\ncustom: ['https://paypal.me/Apollon77']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Something is not working as it should\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n**Check if the Bug maybe is with Amazon**  \nThe adapter is using the same API as the Alexa mobile App. This means:\n* If you can not do this what you want with the App then the Adapter also can not do it!\n* Also if the App can do it please test if the same effect then the bug also happens with the App (e.g. a speak command do not work on a certain device ... can you do a speak via Alexa App? If not - Amazon problem!)\n\n**Describe the bug**  \nA clear and concise description of what the bug is. Especially describe what you verified using the Alexa App (see above).\n\n**To Reproduce**  \nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '...'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**  \nA clear and concise description of what you expected to happen.\n\n**Screenshots & Logfiles**  \nIf applicable, add screenshots and logfiles to help explain your problem.\n\n**Versions:**  \n - Adapter version: <adapter-version>\n - JS-Controller version: <js-controller-version> <!-- determine this with `iobroker -v` on the console -->\n - Node version: <node-version> <!-- determine this with `node -v` on the console -->\n - Operating system: <os-name>\n\n**Additional context**  \nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n**Consider the Reality**  \nThe adapter is using the same API as the Alexa mobile App. This means:\n\nIf you can not do this what you want with the App then the Adapter also can not do it! Also a feature rewuest most likely will not be possible.\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/auto-merge.yml",
    "content": "# Configure here which dependency updates should be merged automatically.\n# The recommended configuration is the following:\n- match:\n      # Only merge patches for production dependencies\n      dependency_type: production\n      update_type: \"semver:patch\"\n- match:\n      # Except for security fixes, here we allow minor patches\n      dependency_type: production\n      update_type: \"security:minor\"\n- match:\n      # and development dependencies can have a minor update, too\n      dependency_type: development\n      update_type: \"semver:minor\"\n\n# The syntax is based on the legacy dependabot v1 automerged_updates syntax, see:\n# https://dependabot.com/docs/config-file/#automerged_updates"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "# ioBroker.alexa2 Adapter\nioBroker.alexa2 is a Node.js adapter for ioBroker that provides remote control for Amazon Alexa devices (Amazon Echo). The adapter integrates with the ioBroker home automation platform and allows control of Alexa devices, smart home devices, routines, and more.\n\nAlways reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.\n\n## Working Effectively\n\n### Bootstrap, Build, and Test the Repository:\n- `npm install` -- Install dependencies. Takes ~40 seconds. NEVER CANCEL.\n- `npm test` -- Run complete test suite. Takes ~55 seconds. NEVER CANCEL. Set timeout to 90+ seconds.\n- `npx eslint main.js lib/ test/ --ext .js` -- Lint main code files. Admin files have known translation issues.\n\n### Timing Expectations:\n- **NEVER CANCEL**: npm install takes 40 seconds. Wait for completion.\n- **NEVER CANCEL**: npm test takes 55 seconds total. Set timeout to 90+ seconds minimum.\n- **NEVER CANCEL**: The test includes full ioBroker js-controller setup which can take 30+ seconds just for initialization.\n\n### Manual Validation After Changes:\n- ALWAYS run `npm test` after making any code changes to ensure adapter functionality\n- ALWAYS run ESLint on modified files: `npx eslint <modified-files> --ext .js`\n- The adapter cannot be run standalone - it requires ioBroker infrastructure\n- Tests automatically validate adapter startup, configuration, and shutdown sequences\n- DO NOT attempt to run main.js directly - it requires ioBroker adapter-core framework\n\n## Project Structure and Navigation\n\n### Key Directories and Files:\n```\n/home/runner/work/ioBroker.alexa2/ioBroker.alexa2/\n├── main.js                 # Main adapter entry point (352KB, core logic)\n├── package.json           # Dependencies and npm scripts\n├── io-package.json        # ioBroker adapter configuration\n├── README.md              # Comprehensive adapter documentation\n├── lib/\n│   ├── smarthomedevices.js # Smart home device definitions and controllers\n│   └── tools.js           # Utility functions\n├── admin/                 # Web UI configuration files\n│   ├── index_m.html       # Material Design admin interface\n│   ├── words.js           # i18n translations (has known ESLint issues)\n│   └── icons/             # Device type icons\n├── test/\n│   ├── testAdapter.js     # Main adapter functionality tests\n│   ├── testPackageFiles.js # Package validation tests\n│   └── lib/setup.js       # Test infrastructure and js-controller setup\n└── .github/workflows/     # CI/CD pipeline configuration\n```\n\n### Important Code Areas:\n- **main.js**: Core adapter logic, Alexa API integration, device management\n- **lib/smarthomedevices.js**: Smart home device capability definitions for various device types\n- **admin/index_m.html**: Admin configuration interface for credentials and settings  \n- **test/lib/setup.js**: Complex test infrastructure that sets up js-controller environment\n\n## Validation Scenarios\n\n### Always Run These Validation Steps:\n1. **Install and Test**: `npm install && npm test` - Ensures all dependencies work and adapter can start properly\n2. **Lint Check**: `npx eslint main.js lib/ test/ --ext .js` - Check code quality (ignore admin/words.js issues)\n3. **Package Validation**: Tests automatically validate package.json and io-package.json consistency\n\n### Manual Testing Scenarios:\n- The adapter requires Amazon Alexa account credentials which cannot be tested in CI\n- Tests validate adapter initialization, configuration loading, and proper shutdown\n- Real functionality requires proxy-based authentication flow with Amazon\n- Tests use mock/stub behavior for core adapter lifecycle validation\n\n## Common Tasks\n\n### Dependencies and Requirements:\n- **Node.js**: 16+ required (package.json engines), tested with 20.x\n- **npm**: Standard package manager, uses package-lock.json\n- **@iobroker/adapter-core**: ^3.2.3 - Core ioBroker adapter framework\n- **alexa-remote2**: ^8.0.2 - Amazon Alexa API integration library\n\n### npm Scripts Available:\n```bash\nnpm test          # Run mocha test suite (takes ~55s)\nnpm run release   # Release automation (requires tokens)\nnpm run translate # Translation management\n```\n\n### Linting and Code Quality:\n- ESLint configured in .eslintrc.json with strict rules\n- Use single quotes, 4-space indentation, prefer const over let/var\n- Main code files should lint cleanly\n- admin/words.js has known translation format issues - ignore ESLint errors in this file\n\n### GitHub Actions Workflow:\n- Runs on Node.js 18.x, 20.x, 22.x, 24.x across Ubuntu, Windows, macOS\n- check-and-lint job: `npm ci` only (linting commented out in workflow)\n- adapter-tests job: `npm ci && npm test` \n- Deployment triggers on tagged releases\n\n## Architecture Notes\n\n### ioBroker Adapter Pattern:\n- Extends @iobroker/adapter-core.Adapter class\n- Uses state-based communication with ioBroker objects/states databases\n- Implements standard adapter lifecycle: ready, unload, stateChange, objectChange\n- Creates device/state hierarchies under adapter namespace (alexa2.0.*)\n\n### External Dependencies:\n- alexa-remote2: Amazon Alexa API integration\n- https, nearest-color, rrule: Utility libraries\n- Sentry integration for error reporting (optional)\n\n### Test Infrastructure:\n- Uses complex test setup that installs and configures js-controller\n- Creates temporary ioBroker instance with Redis backends\n- Tests adapter lifecycle, not actual Amazon Alexa functionality\n- Mock/stub approach for external API dependencies\n\n## Troubleshooting\n\n### Common Issues:\n- **Test timeouts**: Always set 90+ second timeouts for npm test\n- **ESLint errors in admin/words.js**: These are translation files with specific format requirements - ignore\n- **Cannot run main.js directly**: Requires ioBroker adapter-core framework and configuration\n- **Missing dependencies**: Always run `npm install` before any other commands\n\n### Development Environment:\n- Use any modern IDE with Node.js support\n- ESLint integration recommended for code quality\n- Git hooks not configured - manually run linting before commits\n- No TypeScript - pure JavaScript codebase\n\n### CI/CD Pipeline:\n- Tests run automatically on pushes and PRs\n- Linting step commented out in workflow due to admin file issues\n- Releases automated via @alcalzone/release-script\n- Sentry integration for production error tracking"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# Dependabot will run on day 19 of each month at 02:38 (Europe/Berlin timezone)\nversion: 2\nupdates:\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"cron\"\n      timezone: \"Europe/Berlin\"\n      cronjob: \"38 2 19 * *\"\n    open-pull-requests-limit: 15\n\n  - package-ecosystem: \"npm\"\n    directory: \"/\"\n    schedule:\n      interval: \"cron\"\n      timezone: \"Europe/Berlin\"\n      cronjob: \"38 2 19 * *\"\n    open-pull-requests-limit: 20\n    versioning-strategy: \"increase\"\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "# Configuration for probot-stale - https://github.com/probot/stale\n\n# Number of days of inactivity before an Issue or Pull Request becomes stale\ndaysUntilStale: 90\n\n# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.\n# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.\ndaysUntilClose: 7\n\n# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)\nonlyLabels: []\n\n# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable\nexemptLabels:\n  - enhancement\n  - security\n  - bug\n\n# Set to true to ignore issues in a project (defaults to false)\nexemptProjects: true\n\n# Set to true to ignore issues in a milestone (defaults to false)\nexemptMilestones: true\n\n# Set to true to ignore issues with an assignee (defaults to false)\nexemptAssignees: false\n\n# Label to use when marking as stale\nstaleLabel: wontfix\n\n# Comment to post when marking as stale. Set to `false` to disable\nmarkComment: >\n  This issue has been automatically marked as stale because it has not had\n  recent activity. It will be closed if no further activity occurs within the next 7 days.\n  Please check if the issue is still relevant in the most current version of the adapter\n  and tell us. Also check that all relevant details, logs and reproduction steps\n  are included and update them if needed.\n  Thank you for your contributions.\n\n  Dieses Problem wurde automatisch als veraltet markiert, da es in letzter Zeit keine Aktivitäten gab.\n  Es wird geschlossen, wenn nicht innerhalb der nächsten 7 Tage weitere Aktivitäten stattfinden.\n  Bitte überprüft, ob das Problem auch in der aktuellsten Version des Adapters noch relevant ist,\n  und teilt uns dies mit. Überprüft auch, ob alle relevanten Details, Logs und Reproduktionsschritte\n  enthalten sind bzw. aktualisiert diese.\n  Vielen Dank für Eure Unterstützung.\n\n# Comment to post when removing the stale label.\n# unmarkComment: >\n#   Your comment here.\n\n# Comment to post when closing a stale Issue or Pull Request.\ncloseComment: >\n  This issue has been automatically closed because of inactivity. Please open a new\n  issue if still relevant and make sure to include all relevant details, logs and\n  reproduction steps.\n  Thank you for your contributions.\n\n  Dieses Problem wurde aufgrund von Inaktivität automatisch geschlossen. Bitte öffnet ein\n  neues Issue, falls dies noch relevant ist und stellt sicher das alle relevanten Details,\n  Logs und Reproduktionsschritte enthalten sind.\n  Vielen Dank für Eure Unterstützung.\n\n# Limit the number of actions per hour, from 1-30. Default is 30\nlimitPerRun: 30\n\n# Limit to only `issues` or `pulls`\nonly: issues\n\n# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':\n# pulls:\n#   daysUntilStale: 30\n#   markComment: >\n#     This pull request has been automatically marked as stale because it has not had\n#     recent activity. It will be closed if no further activity occurs. Thank you\n#     for your contributions.\n\n# issues:\n#   exemptLabels:\n#     - confirmed\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ \"master\" ]\n  schedule:\n    - cron: '31 17 * * 1'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'javascript' ]\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v5\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: ${{ matrix.language }}\n\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v4\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n      with:\n        category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/dependabot-automerge.yml",
    "content": "# Automatically merge Dependabot PRs when version comparison is within the range\n# that is configured in .github/auto-merge.yml\n\nname: Auto-Merge Dependabot PRs\n\non:\n  pull_request_target:\n\njobs:\n  auto-merge:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v5\n\n      - name: Check if PR should be auto-merged\n        uses: ahmadnassri/action-dependabot-auto-merge@v2\n        with:\n          # This must be a personal access token with push access\n          github-token: ${{ secrets.AUTO_MERGE_TOKEN }}\n          # By default, squash and merge, so Github chooses nice commit messages\n          command: squash and merge"
  },
  {
    "path": ".github/workflows/test-and-release.yml",
    "content": "# This is a composition of lint and test scripts\n# Make sure to update this file along with the others\n\nname: Test and Release\n\n# Run this job on all pushes and pull requests\n# as well as tags with a semantic version\non:\n  push:\n    branches:\n      - '*'\n    tags:\n      # normal versions\n      - \"v?[0-9]+.[0-9]+.[0-9]+\"\n      # pre-releases\n      - \"v?[0-9]+.[0-9]+.[0-9]+-**\"\n  pull_request: {}\n\n# Cancel previous PR/branch runs when a new commit is pushed\nconcurrency:\n  group: ${{ github.ref }}\n  cancel-in-progress: true\n\npermissions:\n  id-token: write  # Required for OIDC\n  contents: write\n  pull-requests: read\n\njobs:\n  # Performs quick checks before the expensive test runs\n  check-and-lint:\n    if: contains(github.event.head_commit.message, '[skip ci]') == false\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [22.x]\n\n    steps:\n      - uses: actions/checkout@v5\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node-version }}\n\n\n      - name: Install Dependencies\n        run: npm ci\n\n#      - name: Perform a type check\n#        run: npm run check:ts\n#        env:\n#          CI: true\n      # - name: Lint TypeScript code\n      #   run: npm run lint\n#      - name: Test package files\n#        run: npm run test:package\n\n  # Runs adapter tests on all supported node versions and OSes\n  adapter-tests:\n    if: contains(github.event.head_commit.message, '[skip ci]') == false\n\n    needs: [check-and-lint]\n\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        node-version: [18.x, 20.x, 22.x, 24.x]\n        os: [ubuntu-latest, windows-latest, macos-latest]\n\n    steps:\n      - uses: actions/checkout@v5\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install Dependencies\n        run: npm ci\n\n      - name: Run local tests\n        run: npm test\n#      - name: Run unit tests\n#        run: npm run test:unit\n#      - name: Run integration tests # (linux/osx)\n#        if: startsWith(runner.OS, 'windows') == false\n#        run: DEBUG=testing:* npm run test:integration\n#      - name: Run integration tests # (windows)\n#        if: startsWith(runner.OS, 'windows')\n#        run: set DEBUG=testing:* & npm run test:integration\n\n  # Deploys the final package to NPM\n  deploy:\n    needs: [adapter-tests]\n\n    # Trigger this step only when a commit on master is tagged with a version number\n    if: |\n      contains(github.event.head_commit.message, '[skip ci]') == false &&\n      github.event_name == 'push' &&\n      startsWith(github.ref, 'refs/tags/')\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version: [22.x]\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v5\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Extract the version and commit body from the tag\n        shell: bash\n        id: extract_release\n        # The body may be multiline, therefore newlines and % need to be escaped\n        run: |\n          VERSION=\"${{ github.ref }}\"\n          VERSION=${VERSION##*/v}\n          echo \"VERSION=$VERSION\" >> $GITHUB_OUTPUT\n          EOF=$(od -An -N6 -x /dev/urandom | tr -d ' ')\n          BODY=$(git show -s --format=%b)\n          echo \"BODY<<$EOF\" >> $GITHUB_OUTPUT\n          echo \"$BODY\" >> $GITHUB_OUTPUT\n          echo \"$EOF\" >> $GITHUB_OUTPUT\n          if [[ $VERSION == *\"-\"* ]] ; then\n            echo \"TAG=--tag next\" >> $GITHUB_OUTPUT\n          fi\n\n      - name: Install Dependencies\n        run: npm ci\n\n#      - name: Create a clean build\n#        run: npm run build\n      - name: Publish package to npm\n        run: |\n          npm install -g npm@latest\n          npm publish ${{ steps.extract_release.outputs.TAG }}\n\n      - name: Create Github Release\n        uses: softprops/action-gh-release@v2\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ github.ref }}\n          name: Release v${{ steps.extract_release.outputs.VERSION }}\n          draft: false\n          # Prerelease versions create prereleases on Github\n          prerelease: ${{ contains(steps.extract_release.outputs.VERSION, '-') }}\n          body: ${{ steps.extract_release.outputs.BODY }}\n\n      - name: Notify Sentry.io about the release\n        run: |\n          npm i -g @sentry/cli  \n          export SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}\n          export SENTRY_URL=https://sentry.iobroker.net\n          export SENTRY_ORG=iobroker\n          export SENTRY_PROJECT=iobroker-alexa2\n          export SENTRY_VERSION=iobroker.alexa2@${{ steps.extract_release.outputs.VERSION }}\n          sentry-cli releases new $SENTRY_VERSION\n          sentry-cli releases set-commits $SENTRY_VERSION --auto\n          sentry-cli releases finalize $SENTRY_VERSION\n\n        # Add the following line BEFORE finalize if sourcemap uploads are needed\n        # sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/\n"
  },
  {
    "path": ".gitignore",
    "content": "# Created by .ignore support plugin (hsz.mobi)\n### Node template\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\nadmin/i18n/flat.txt\nadmin/i18n/*/flat.txt\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n\n.idea\n\n# ioBroker dev-server\n.dev-server/\n\n# Test temporary files\ntmp/\n\n#ignore .commitinfo created by ioBroker release script\n.commitinfo\n"
  },
  {
    "path": ".mocharc.json",
    "content": "{\n    \"require\": [\n        \"./test/mocha.setup.js\"\n    ]\n}\n"
  },
  {
    "path": ".npmignore",
    "content": "# exclude all dot-files and directories\n.*\n\nadmin/i18n\ntasks\nnode_modules\n.idea\n.git\n/node_modules\ntest\n.travis.yml\nappveyor.yml\n.github\npackage-lock.json\n.gitattributes\n"
  },
  {
    "path": ".releaseconfig.json",
    "content": "{\n    \"plugins\": [\"iobroker\", \"license\"]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2018-2025 Ingo Fischer <iobroker@fischer-ka.de>, 2017-2018 soef <soef@gmx.net>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![Logo](admin/alexa.png)\n# ioBroker.alexa2\n\n![Number of Installations](http://iobroker.live/badges/alexa2-installed.svg)\n![Number of Installations](http://iobroker.live/badges/alexa2-stable.svg)\n[![NPM version](http://img.shields.io/npm/v/iobroker.alexa2.svg)](https://www.npmjs.com/package/iobroker.alexa2)\n\n![Test and Release](https://github.com/Apollon77/iobroker.alexa2/workflows/Test%20and%20Release/badge.svg)\n[![Translation status](https://weblate.iobroker.net/widgets/adapters/-/alexa2/svg-badge.svg)](https://weblate.iobroker.net/engage/adapters/?utm_source=widget)\n[![Downloads](https://img.shields.io/npm/dm/iobroker.alexa2.svg)](https://www.npmjs.com/package/iobroker.alexa2)\n\n**This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and for information how to disable the error reporting see [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Sentry reporting is used starting with js-controller 3.0.\n\nThis adapter allows you to remotely control your Alexa (Amazon Echo) devices.\n\nBig thanks go to soef for version 1 of the adapter and to Hauke and ruhr70 for ideas in their scripts from ioBroker-Forum (especially the media progress updates)!\nAlso, big thanks to meicker for support in documenting all of this and numerous users from ioBroker Forum for their testing support!\n\n## Disclaimer\n**All product and company names or logos are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them or any associated subsidiaries! This personal project is maintained in spare time and has no business goal.**\n**ALEXA is a trademark of AMAZON TECHNOLOGIES, INC.**\n\n## States and their meanings:\n\nIn the adapter namespace (e.g., alexa2.0) some channels are created\n\n### alexa2.0\n\n| State name           | meaning                                                |\n|----------------------|--------------------------------------------------------|\n| Echo-Devices.*       | States per Echo device, see below                      |\n| History.*            | Infos for command history, see below                   |\n| Smart-Home-Devices.* | States per smart home device and in general, see below |\n| info.*               | General information about the adapter status           |\n| requestResult        | Error info for TuneIn and smart-home device requests   |\n\n### alexa2.0.Contacts.ContactId.*\nAll Alexa-Contacts that can be used to send Text Messages to, including himself. The own contact gets a special \"(Self)\" after his name.\n\n| State name        | meaning                                                                                                                                        |\n|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------|\n| #clearOwnMessages | Only exists in own contact and a trigger deletes all messages that are send to himself (also includes messages to himself via App or devices!) |\n| textMessage       | Sends this text as message to the user. It is shown on all devices of this user with a \"yellow ring\"                                           |\n\n### alexa2.0.Echo-Devices.CommandsAll.*\nCommands to be sent to all devices in the account.\n\n| State name         | meaning                                         | Comments                                                                                                     |\n|--------------------|-------------------------------------------------|--------------------------------------------------------------------------------------------------------------|\n| deviceStop         | Stop all actions on device                      | Button                                                                                                       |\n| deviceDoNotDisturb | Switch on/off \"Do not Disturb\" for all devices. | true/false, or number in seconds to enable (max 12h) or string in form \"HH:MM\" until this time it is enabled |\n\n### alexa2.0.Echo-Devices.Serialnumber.*\nUnder \"echo-devices\" every amazon echo device is listed with its serial number. Not every device shows all the states. Every device has its own states as described below:\n\n### alexa2.0.Echo-Devices.Serialnumber.Alarm.*\nAlarm (Wecker) settings for each device, if available.\n\n| State name            | meaning                                                                                                                                                                                                                                                                                                                                                                               | value                                                                                                                                                                                                                                                                                                                              |\n|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| <id>.customVolume     | Set a custom Volume for this Reminder. The volume is set 2s before the reminder triggers and re-set to the value before as soon as the timer is (or adapter thinks!) stopped - latest after 120s! When custom volumes and trigger times overlap it will be restored at the end once!                                                                                                  | Number 0..100                                                                                                                                                                                                                                                                                                                      |\n| <id>.date             | Overwrite the date for existing alarm to set a new date for this alarm. In case you have an existing alarm you can change the date here by simply overwrite the time in format YYYY-MM-DD. Might have no effect when multiple-times-per-day recurring settings were used!                                                                                                             | Date Output                                                                                                                                                                                                                                                                                                                        |\n| <id>.delete           | Button to delete the Alarm                                                                                                                                                                                                                                                                                                                                                            | delete with true                                                                                                                                                                                                                                                                                                                   |\n| <id>.enabled          | Shows status of alarm and allows to change it: Activate alarm with true - Deactivate alarm with false                                                                                                                                                                                                                                                                                 | true / false                                                                                                                                                                                                                                                                                                                       |\n| <id>.musicEntity      | Shows the track info if this alarm is a music alarm                                                                                                                                                                                                                                                                                                                                   | String or null                                                                                                                                                                                                                                                                                                                     |\n| <id>.musicProvider    | Shows the provider of the music  if this alarm is a music alarm                                                                                                                                                                                                                                                                                                                       | String or null                                                                                                                                                                                                                                                                                                                     |\n| <id>.nextTriggerDate  | Contains the timepoint of the next expected triggering as unix epoch in ms                                                                                                                                                                                                                                                                                                            | Number                                                                                                                                                                                                                                                                                                                             |\n| <id>.recurringDays    | Shows the list of days configured if the Alarm has recurring settings                                                                                                                                                                                                                                                                                                                 | US notation of weekdays (e.g. MO,TU,WE,TH,FR,SA,SU)                                                                                                                                                                                                                                                                                |\n| <id>.recurringPattern | Shows the recurring pattern of alarm                                                                                                                                                                                                                                                                                                                                                  | 0 = one time, no recurring <br> P1D = daily <br> XXXX-WD = on weekdays <br> XXXX-WE = on weekends <br> XXXX-WXX-1 = every monday <br> XXXX-WXX-2 = every tuesday <br> XXXX-WXX-3 = every wednesday <br> XXXX-WXX-4 = every thursday <br> XXXX-WXX-5 = every friday <br> XXXX-WXX-6 = every saturday <br> XXXX-WXX-7 = every sunday |\n| <id>.snoozed          | true if the Alarm is snoozed at the moment                                                                                                                                                                                                                                                                                                                                            | true/false                                                                                                                                                                                                                                                                                                                         |\n| <id>.sound            | Contains the set sound for this alarm. Can be changed. Also changing between music sound entry and \"build in sounds\" is possible.                                                                                                                                                                                                                                                     | ID from list                                                                                                                                                                                                                                                                                                                       |\n| <id>.time             | Time for alarm. Overwrite the time for existing alarm to set a new time for this alarm. In case you have an existing alarm you can change the time here by simply overwrite the time in format hh:mm:ss, seconds are not needed to set. Might have no effect when multiple-times-per-day recurring settings were used!                                                                | Time Input                                                                                                                                                                                                                                                                                                                         |\n| <id>.triggered        | true if alarm is reached and triggered. Clock must be in sync with Amazon and iobroker, Use this to trigger other action as soon as the alarm time is reached                                                                                                                                                                                                                         | true / false                                                                                                                                                                                                                                                                                                                       |\n| New                   | Data to create a new Reminder as String in following format separated by ; as \"timestamp;[label];[sound];[recurring]. timestamp as unix timestamp in ms, label as Text, sound as sound ID, recurring either empty for once, \"DAILY\" for daily or \"WEEKLY=MO,TU,WE,TH,FR,SA,SU\" with comma separated weekly day list. Fields in example above in brackets mean that they are optional! | String                                                                                                                                                                                                                                                                                                                             |\n| triggered             | ID of the Alarm that triggered last on this device                                                                                                                                                                                                                                                                                                                                    | ID                                                                                                                                                                                                                                                                                                                                 |\n\nWhen changing an Alarm does not work, please make sure that the Alarm timepoint is in the future - so changing e.g. \"sound\" on an Alarm in the past will _not_ work!\n\n### alexa2.0.Echo-Devices.Serialnumber.Bluetooth.*\nHere you find all connected or known bluetooth device(s) with MAC address(es). The states of each device:\n\n\n| State name | meaning                                                                                            |\n|------------|----------------------------------------------------------------------------------------------------|\n| connected  | Shows current connection status and allow connection (set to true) or disconnection (set to false) |\n| unpair     | Button to unpair this device from the echo device                                                  |\n\n### alexa2.0.Echo-Devices.Serialnumber.Commands.*\nWith Commands, you can trigger some actions on your Alexa-Device. If you use these on a multiroom device, then they are executed independently and *will not* run in sync on the single devices!\n\n| State name    | meaning                                                                                                                                | value                                                                                                        |\n|---------------|----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|\n| doNotDisturb  | Switch on/off \"Do not Disturb\" for this device or group. Value is updated with Device Configuration updates from Cloud too             | true/false, or number in seconds to enable (max 12h) or string in form \"HH:MM\" until this time it is enabled |\n| flashbriefing | Briefing in 100 seconds - news etc.pp                                                                                                  | Button                                                                                                       |\n| goodmorning   | Good morning from Alexa ...                                                                                                            | Button                                                                                                       |\n| funfact       | Fun fact from Alexa ... (Only USA at the moment)                                                                                       | Button                                                                                                       |\n| joke          | Joke from Alexa ...                                                                                                                    | Button                                                                                                       |\n| cleanup       | Plays a \"gong\" tone like for start/end of listening mode ...                                                                           | Button                                                                                                       |\n| curatedtts    | Random sentence from the chosen area from Alexa ...                                                                                   | Text (allowed: \"goodbye\", \"confirmations\", \"goodmorning\", \"compliments\", \"birthday\", \"goodnight\", \"iamhome\") |\n| singasong     | Alexa sings a song ...                                                                                                                 | Button                                                                                                       |\n| speak         | Alexa says what you type in here ...                                                                                                   | Text Input                                                                                                   |\n| speakvolume   | Adjust the speak volume of Alexa, this volume is set before the speak and reset afterwards                                             | 0-100                                                                                                        |\n| skill         | Launch a defined Skill                                                                                                                 | Skill-ID as String                                                                                           |\n| skillYours    | launch a defined Skill - is prefilled with \"Your Skills\" as displayed in Alexa App too                                                 | Skill-ID as String                                                                                           |\n| tellstory     | Alexa tells a story                                                                                                                    | Button                                                                                                       |\n| traffic       | Traffic news                                                                                                                           | Button                                                                                                       |\n| weather       | Weather news                                                                                                                           | Button                                                                                                       |\n| deviceStop    | Stop all actions on device                                                                                                             | Button                                                                                                       |\n| notification  | Send text notification to customer of the device                                                                                       | Text, optionally specify title \"title;text\"                                                                  |\n| announcement  | Play announcement (like speak but with Bing before text) <br>*Note: will only work if announcements (for this device) are activated and the device is not in \"do not disturb\"-mode*                                                                              | Text                                                                                                         |\n| ssml          | Speak SSML XML string <br>*Note: will only work if announcements (for this device) are activated and the device is not in \"do not disturb\"-mode*                                                                                                                 | Text                                                                                                         |\n| textcommand   | Send a Text command to Alexa,. Make sure to only use text (e.g. also 3 -> three and such, else Alexa might not correctly react to it!) | Text                                                                                                         |\n| sound         | Play a sound on the device.                                                                                                            | Text                                                                                                         |\n\nDetailed information Speak and Announcement: Type in here what you want Alexa to say. You can also adjust the volume of Alexa by giving a percentage before your text.\nExample: 10;Alexa is saying Alexa with 10% volume, while 100;Alexa is 100% volume.\nNormally, you only can send 250 characters per speak command. By using the semicolon, it is possible to write as much as you want, as long as you separate 250 characters with a semicolon.\nAlexa will then speak the text after each other with a small break. You also can use the volume together with more 255 blocks by writing #Volume;#Block1;#Block2, a.s.o A volume set here will be used over a defined speak-volume.\n\nPartially also sounds from https://developer.amazon.com/en-US/docs/alexa/custom-skills/ask-soundlibrary.html work. Specify in speak or ssml as `<audio src=\"soundbank://soundlibrary/animals/amzn_sfx_bear_groan_roar_01\"/>`. Details and discussion please at https://forum.iobroker.net/topic/27509/ssml-audio\n\n### alexa2.0.Echo-Devices.Serialnumber.FireTVCommands.*\nIf a device is an Amazon FireTV then you can use the following commands:\n\n| State name   | meaning                  | value  |\n|--------------|--------------------------|--------|\n| turnOn       | Turn FireTV and TV on    | Button |\n| turnOff      | Turn FireTV and TV off   | Button |\n| videoPause   | Pause the running video  | Button |\n| videoResume  | Resume the current video | Button |\n| navigateHome | Navigate to Home         | Button |\n\n### alexa2.0.Echo-Devices.Serialnumber.Info.*\nInformation about the Alexa device\n\n| State name        | meaning                                                                                     | value                     |\n|-------------------|---------------------------------------------------------------------------------------------|---------------------------|\n| capabilities      | capabilities if the alexa device                                                            | Information               |\n| deviceType        | device type from Amazon                                                                     | Information               |\n| deviceTypeString  | Device Type as string                                                                       | Information               |\n| isMultiroomDevice | Is multiroom device - Multiroom is a virtual device group                                   | Information, true / false |\n| isMultiroomMember | Is Multiroom member - If true the device is part of a multiroom device group                | Information, true / false |\n| MultiroomParents  | If this device is part of a multiroom device group this state shows the parent group device | Information               |\n| name              | Name of Alexa Device                                                                        | Information               |\n| SerialNumber      | Serial number of Alexa device                                                               |\n\n### alexa2.0.Echo-Devices.Serialnumber.Music-Provider.*\nDirectly tell Alexa to play Music or a playlist from supported music providers. Actually supported are: My Library, Amazon Music, Tune In. You can also include a multiroom device group name in the phrase to play it on this group (e.g. \"SWR3 auf Erdgeschoss\")\n\n| State name            | meaning                            | value      |\n|-----------------------|------------------------------------|------------|\n| Amazon-Music          | Phrase to play with Amazon Music   | Text input |\n| Amazon-Music-Playlist | Playlist to play with Amazon Music | Text input |\n| My-Library            | Phrase to play with My Library     | Text input |\n| My-Library-Playlist   | Playlist to play with My Library   | Text input |\n| Tune-In               | Phrase to play with Tune In        | Text input |\n| Tune-In-Playlist      | Playlist to play with Tune In      | Text input |\n\n### alexa2.0.Echo-Devices.Serialnumber.Player.*\nStates to control the Playback of the device and to see the current status and media information\n\n| State name          | meaning                                                                                                                                                                                                | value                                                        |\n|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|\n| allowNext           | Is the Next/Forward action allowed?                                                                                                                                                                    | Information                                                  |\n| allowPlayPause      | Is the Play/Pause action allowed?                                                                                                                                                                      | Information                                                  |\n| allowPrevious       | Is the Previous action allowed?                                                                                                                                                                        | Information                                                  |\n| allowRepeat         | Can Repeat function be used?                                                                                                                                                                           | Information                                                  |\n| allowShuffle        | Can Shuffle function be used?                                                                                                                                                                          | Information                                                  |\n| ContentType         | text field to put in desired content to play on this device                                                                                                                                            | Information                                                  |\n| controlForward      | Button to trigger player \"forward\" command (30s)                                                                                                                                                       | Button                                                       |\n| controlNext         | Button to trigger player \"next\" command                                                                                                                                                                | Button                                                       |\n| controlPause        | Button to trigger player \"pause\" command                                                                                                                                                               | Button                                                       |\n| controlPlay         | Button to trigger player \"play\" command                                                                                                                                                                | Button                                                       |\n| controlPrevious     | Button to trigger player \"previous\" command                                                                                                                                                            | Button                                                       |\n| controlRepeat       | Button to trigger player \"repeat\" command                                                                                                                                                              | true / false                                                 |\n| controlRewind       | Button to trigger player \"rewind\" command (30s)                                                                                                                                                        | Button                                                       |\n| controlShuffle      | Switch to enable or disable Shuffle mode for player                                                                                                                                                    | true / false                                                 |\n| currentAlbum        | Current album actually playing                                                                                                                                                                         | Information                                                  |\n| currentArtist       | Current artist actually playing                                                                                                                                                                        | Information                                                  |\n| currentState        | If playing -> true , else false                                                                                                                                                                        | true / false                                                 |\n| currentTitle        | Current title actually playing                                                                                                                                                                         | Information                                                  |\n| imageURL            | URL to the image of the album                                                                                                                                                                          | Information                                                  |\n| mainArtURL          | URL to current main art                                                                                                                                                                                | Information                                                  |\n| mediaId             | media ID of the current played media (usually queueID:<number>                                                                                                                                         | String, can be set to jump to the provided media ID          |\n| mediaLength         | Length of the current title                                                                                                                                                                            | Information                                                  |\n| mediaLengthStr      | active media length as (HH:)MM:SS                                                                                                                                                                      | Information                                                  |\n| mainProgress        | active media elapsed time                                                                                                                                                                              | Information                                                  |\n| mainProgressPercent | active media elapsed time in percent                                                                                                                                                                   | Information                                                  |\n| mediaProgressStr    | active media progress as (HH:)MM:SS                                                                                                                                                                    | Information                                                  |\n| miniArtUrl          | URL to the art (mini)                                                                                                                                                                                  | Information                                                  |\n| muted               | state of 'MUTE'                                                                                                                                                                                        | Information, true / false, volume = 0 is considered as muted |\n| playingInGroup      | Is the medium played in a group?                                                                                                                                                                       | Information                                                  |\n| playingInGroupId    | ID of the playing group                                                                                                                                                                                | Information                                                  |\n| providerID          | ID of the current music provider                                                                                                                                                                       | Information                                                  |\n| providerName        | Name of the current music provider                                                                                                                                                                     | Information                                                  |\n| quality             | quality name of the current medium (might be empty)                                                                                                                                                    | Information                                                  |\n| qualityCodec        | Codec of the current medium (might be empty)                                                                                                                                                           | Information                                                  |\n| qualityDataRate     | data rate (kbps) of the current medium (might be empty)                                                                                                                                                | Information                                                  |\n| qualitySampleRate   | sample rate (Hz) of the current medium (might be empty)                                                                                                                                                | Information                                                  |\n| queueId             | queue ID of the current playlist                                                                                                                                                                       | Information                                                  |\n| radioStationId      | ID of the TuneIn radio station                                                                                                                                                                         | Information                                                  |\n| service             | name of the current music service                                                                                                                                                                      | Information                                                  |\n| TuneIn-Station      | text field to put in a Station name to play this station on this device. Also it is possible to type in the station number (s123456...), a show/podcast id (p1234567...) or a topic id (t123456789...) | Text input                                                   |\n| volume              | Volume of playback. You can enter a value between 0-100%                                                                                                                                               | INPUT Volume                                                 |\n\n### alexa2.0.Echo-Devices.Serialnumber.Preferences.*\nHere you find some device preferences.\n\n| State name                       | meaning                                                                                                                                                                    | value                                                                           |\n|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|\n| ringNotificationsEnabled         | Shows if the ring notifications are enabled or not and allows to edit it (true/false). The status is updated from cloud with a device configuration interval               |\n| notificationVolume               | The notification volume set for the device. The value is loaded once on adapter start and then not synced with Cloud services, but changeable                              | number 0..100                                                                   |\n| ascendingAlarmState              | The ascending alarm state set for the device. The value is loaded once on adapter start and then not synced with Cloud services, but changeable                            | Boolean                                                                         |\n| auxPort-*-Direction              | The direction of the AuxPort (when supported). The value is loaded once on adapter start and then not synced with Cloud services, but changeable                           | \"INPUT\" or \"OUTPUT\"                                                             |\n| connectedSpeaker                 | The speaker with is used for the Device output. The value is loaded once on adapter start and then not synced with Cloud services, but changeable                          | \"InternalSpeaker\", \"Bluetooth\" or \"Aux\" (if supported by Device! check the App) |\n| defaultAlarmNotificationSound    | The default alarm sound set for the device. The value is loaded once on adapter start and then not synced with Cloud services, but changeable                              | ID from a list                                                                  |\n| defaultTimerNotificationSound    | The default timer sound set for the device. The value is loaded once on adapter start and then not synced with Cloud services, but changeable                              | ID from a list                                                                  |\n| displayAdaptiveBrightnessEnabled | Is the adaptive brightness for the display of the device enabled or not. The value is loaded once on adapter start and then not synced with Cloud services, but changeable | true/false                                                                      |\n| displayEnabled                   | Is the display of the device enabled or not. The value is loaded once on adapter start and then not synced with Cloud services, but changeable                             | true/false                                                                      |\n| displayBrightness                | Brightness of the display. The value is loaded once on adapter start and then not synced with Cloud services, but changeable                                               | 0..100%                                                                         |\n| equalizerBass                    | Equalizer Bass setting. Value is updated when changed if push connection is enabled                                                                                        | Number                                                                          |\n| equalizerMidRange                | Equalizer Midrange  setting. Value is updated when changed if push connection is enabled                                                                                   | Number                                                                          |\n| equalizerTreble                  | Equalizer Treble  setting. Value is updated when changed if push connection is enabled                                                                                     | Number                                                                          |\n\n\n### alexa2.0.Echo-Devices.Serialnumber.Reminder.*\nReminder (Erinnerungen) settings for each device, if available.\n\n| State name            | meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | value                                                                                                                                                                                                                                                                                                                              |\n|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| <id>.customVolume     | Set a custom Volume for this Reminder. The volume is set 2s before the reminder triggers and re-set to the value before as soon as the timer is (or adapter thinks!) stopped - latest after 120s! When custom volumes and trigger times overlap it will be restored at the end once!                                                                                                                                                                                                                                     | Number 0..100                                                                                                                                                                                                                                                                                                                      |\n| <id>.date             | Overwrite the date for existing alarm to set a new date for this alarm. In case you have an existing alarm you can change the date here by simply overwrite the time in format YYYY-MM-DD. Might have no effect when multiple-times-per-day recurring settings were used!                                                                                                                                                                                                                                                | Date Output                                                                                                                                                                                                                                                                                                                        |\n| <id>.delete           | Button to delete the Alarm                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | delete with true                                                                                                                                                                                                                                                                                                                   |\n| <id>.enabled          | Shows status of alarm and allows to change it: Activate alarm with true - Deactivate alarm with false                                                                                                                                                                                                                                                                                                                                                                                                                    | true / false                                                                                                                                                                                                                                                                                                                       |\n| <id>.nextTriggerDate  | Contains the timepoint of the next expected triggering as unix epoch in ms                                                                                                                                                                                                                                                                                                                                                                                                                                               | Number                                                                                                                                                                                                                                                                                                                             |\n| <id>.recurringDays    | Shows the list of days configured if the Alarm has recurring settings                                                                                                                                                                                                                                                                                                                                                                                                                                                    | US notation of weekdays (e.g. MO,TU,WE,TH,FR,SA,SU)                                                                                                                                                                                                                                                                                |\n| <id>.recurringPattern | Shows the recurring pattern of alarm                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | 0 = one time, no recurring <br> P1D = daily <br> XXXX-WD = on weekdays <br> XXXX-WE = on weekends <br> XXXX-WXX-1 = every monday <br> XXXX-WXX-2 = every tuesday <br> XXXX-WXX-3 = every wednesday <br> XXXX-WXX-4 = every thursday <br> XXXX-WXX-5 = every friday <br> XXXX-WXX-6 = every saturday <br> XXXX-WXX-7 = every sunday |\n| <id>.snoozed          | true if the Alarm is snoozed at the moment                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | true/false                                                                                                                                                                                                                                                                                                                         |\n| <id>.sound            | Contains the set sound for this alarm. Can be adjusted                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | ID from list                                                                                                                                                                                                                                                                                                                       |\n| <id>.time             | Time for alarm. Overwrite the time for existing alarm to set a new time for this alarm. In case you have an existing alarm you can change the time here by simply overwrite the time in format hh:mm:ss, seconds are not needed to set. Might have no effect when multiple-times-per-day recurring settings were used!                                                                                                                                                                                                   | Time Input                                                                                                                                                                                                                                                                                                                         |\n| <id>.triggered        | true if alarm is reached and triggered. Clock must be in sync with Amazon and iobroker, Use this to trigger other action as soon as the alarm time is reached                                                                                                                                                                                                                                                                                                                                                            | true / false                                                                                                                                                                                                                                                                                                                       |\n| New                   | Data to create a new Reminder as String in following format separated by ; as \"timestamp;label;[sound];[recurring]. timestamp as unix timestamp in ms or text like \"HH:MM\", label as Text (required), sound as sound ID, recurring either empty for once, \"DAILY\" for daily or \"WEEKLY=MO,TU,WE,TH,FR,SA,SU\" with comma separated weekly day list. For full flexibility recurring can also be a JSONified object with all data which is passed through. Fields in example above in brackets mean that they are optional! | String                                                                                                                                                                                                                                                                                                                             |\n| triggered             | ID of the Alarm that triggered last on this device                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | ID                                                                                                                                                                                                                                                                                                                                 |\n\nWhen changing a Reminder does not work, please make sure that the Reminder timepoint is in the future - so changing e.g. \"sound\" on a Reminder in the past will _not_ work!\n\n### alexa2.0.Echo-Devices.Serialnumber.Routines.*\nOverview of routines set up in Alexa App. Self created routines have a serial number, Amazon shows as 'preconfigured:...' Each routine can be triggered with a button to run once.\n\n| State name                         | meaning         | value  |\n|------------------------------------|-----------------|--------|\n| Serial or internal name of routine | name of routine | Button |\n\n### alexa2.0.Echo-Devices.Serialnumber.Timer.*\nYou can have one or more timers running on each Alexa device. Because of the very dynamic nature of timers, there will be no further objects created like with Alarm or Reminders, but a way to get triggered info exists.\n\n| State name      | meaning                                                                                                      | value      |\n|-----------------|--------------------------------------------------------------------------------------------------------------|------------|\n| activeTimerList | JSON array with the list of active timers containing ID, label and trigger timepoint as unix timestamp in ms | JSON array |\n| nextTimeDate    | Contains the timepoint of the next expected triggering as unix epoch in ms                                   | Number     | Number\n| nextTimerId     | ID of the next timer to trigger                                                                              | String     |\n| stopTimerId     | Control with a timer ID to stop the timer (also stops if the timer is currently ringing!)                    | String     |\n| triggered       | A timer got triggered - in fact it is the \"nextTimerId\" one                                                  | true/false |\n\n**Please note that it is important that the timezone of the iobroker host is set to match your local timezone, else the triggered time detection might be wrong!**\n\n### alexa2.0.Echo-Devices.Serialnumber.online\nIs this Alexa device online and connected to the Amazon cloud?\n\n| State name | meaning                | value        |\n|------------|------------------------|--------------|\n| online     | Is the device online ? | True / False |\n\n### alexa2.0.History\n\n| State name            | meaning                                                                                                                                                            | value                                                                                                                                                                                |\n|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| #trigger              | Button to get new History (more current then timestamp in creationTime), only needed when not using the push connection or when the automatic querying is disabled | Button                                                                                                                                                                               |\n| cardContent           | Additional information as shown in Alexa-App/Echo Show                                                                                                             | Information                                                                                                                                                                          |\n| cardJson              | Additional information as shown in Alexa-App/Echo Show in JSON format                                                                                              | Information                                                                                                                                                                          |\n| creationTime          | date of this history entry, new history entries are only considered when later as this timestamp                                                                   | Information                                                                                                                                                                          |\n| domainApplicationId   | Additional information like Skill-ID or such, optional                                                                                                             | Information                                                                                                                                                                          |\n| domainApplicationName | Additional information like Skill name or such, optional                                                                                                           | Information                                                                                                                                                                          |\n| json                  | Json of last command data to be able to process all infos e.g. in own JavaScripts                                                                                  | JSON                                                                                                                                                                                 |\n| name                  | Name of the device that got the last request                                                                                                                       | Information                                                                                                                                                                          |\n| serialNumber          | Serial number of the device that got the last request                                                                                                              | Information                                                                                                                                                                          |\n| status                | Status of last command to Alexa                                                                                                                                    | SUCCESS / FAULT / DISCARDED_NON_DEVICE_DIRECTED_INTENT; last one is generated when activating the device by saying the wake word, or when the device discarded input as \"not for me\" |\n| summary               | text/summary/action received by the device                                                                                                                         | Information                                                                                                                                                                          |\n\n### alexa.0.Smart-Home-Devices\nIncludes all smart home devices Alexa knows from your skills. States as follows, for all known devices:\n\n| State name      | meaning                                                                                    | value  |\n|-----------------|--------------------------------------------------------------------------------------------|--------|\n| deleteAll       | deletes all smart home devices from Alexa, same as the button in the Alexa App             | Button |\n| discoverDevices | finds new smart home devices, same as the button in the Alexa App                          | Button |\n| queryAll        | queries all devices, only visible when at least one device is able to retrieve information | Button | \n\n### alexa.0.Smart-Home-Devices.SerialNumber.*\n| State name         | meaning                                                                                                                                                       | value                         |\n|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|\n| #delete            | delete smart home device from Alexa                                                                                                                           | Button                        |\n| #enabled           | Is the smart home device active? Status and control to enable/disable. State will be synced with the cloud in the same interval as the smart home deice data. | true / false                  |\n| #includeInAllQuery | Should this device be included when querying all device states ?                                                                                              | true / false                  |\n| #query             | query data for this device, only visible when the smart home device/skill supports to retrieve information                                                    | Button                        |\n| active             | shown for scenes when they can be activated/deactivated                                                                                                       | true / false                  |\n| powerState         | Switch power on / off                                                                                                                                         | changeable, true / false      |\n| ...                | Many more possible states depending on the type of the smart home device                                                                                      | Information or changeable :-) |\n\n**-> Special states for color/light devices**\n\n| State name               | meaning                                                                 | value                          |\n|--------------------------|-------------------------------------------------------------------------|--------------------------------|\n| brightness               | brightness of the HUE light                                             | changeable 0-100%              |\n| color-Brightness         | brightness for color definition (together with hue and saturation, HSV) | Information, 0-1%              |\n| color-hue                | hue value of the color (together with brightness and saturation, HSV)   | Information, 0-360°            |\n| color-saturation         | saturation of the color (together with brightness and hue, HSV)         | Information, 0-1               |\n| colorRGB                 | RGB code of actual color build out of color-* values                    | Information, #rrggbb           |\n| colorName                | Name of the color as defined by Alexa - fixed values                    | changeable to set color, 0-144 |\n| colorTemperatureInKelvin | Color temperature in Kelvin                                             | Information, 1000-10000K       |\n| colorTemperatureName     | Color temperature name as defined by Alexa - fixed values               | changeable to set, 0-18        |\n\nWith #brightness you can adjust the brightness of your light, #colorName is to pick one predefined color (0-144). For HUE Ambient light, you can choose between 19 Values from 0-18 in #colorTemperatureName. All light can switched on and off with #powerState.\n\n### alexa2.0.Info.*\n| State name | meaning                                                                             | value                       |\n|------------|-------------------------------------------------------------------------------------|-----------------------------|\n| connection | If connection to Alexa is OK                                                        | Information -> true / false |\n| cookie     | Alexa cookie, use with several external scripts that also want to access Alexa APIs | Information                 |\n| csrf       | Alexa CSRF, use with several external scripts that also want to access Alexa APIs   | Information                 |\n\n\n## Installation\nAs usual using stable repository, the latest repository or uses the ioBroker \"Install\" options from GitHub\n\n## Send Alexa Device Command sequences via messages\nAll commands to the alexa devices can be sent via the adapter to single devices or to groups. The adapter supports sending of these commands and - if needed - also combines them to set a specific volume before a voice output and restore the original volume afterward.\n\nWhen you want to send custom sequences to alexa devices, you can create a Routine and trigger the routine also via the states.\n\nIf this is not flexible enough, the adapter offers since version 3.14.0 a way to send commands via messages.\n\nYou provide an array structure which will be converted to commands. There are two types of options for one array element:\n\n**A command**\n```json\n{\n    \"command\": \"speak\", // command like the state name in Commands states\n    \"value\": \"This is a test speak.\", // value like value you set on state\n    \"device\": \"...\" // optional: serialNumber of the device to send this command to\n}\n```\n\n**A sequence definition**\n\n```json\n{\n    \"sequenceType\": \"...\", // \"SerialNode\" or \"ParallelNode\"\n    \"nodes\": [...] // array of commands or sequences\n}\n```\n\nSending the message e.g. using JavaScript adapter looks like this:\n\n```javascript\nadapter.sendTo(\n    \"alexa.0\", // target\n    \"sendSequenceCommand\", // command\n    { // value\n        \"deviceSerialNumber\": \"...\", // Serial number of one device to get Meta data which will be used if no device is pecified on the commands\n        \"sequenceNodes\": [...], // list of sequences or commands\n        \"sequenceType\": \"SerialNode\" // \"SerialNode\" or \"ParallelNode\" for the provided sequenceNodes on main level. Default is \"SerialNode\"\n    }, (err, res) => {\n        console.log(err);\n        console.log(JSON.stringify(res));\n    }\n);\n```\n\nWhen commands are executed as \"ParallelNode\" in parallel, which mainly makes sense to send commands to different devices. Commands as \"SerialNode\" are executed one after the other - **Amazon takes care about this and handles this, not the adapter!** \n\nA structure like the following is possible:\n\n```json\n... // use ParallelNode on main level\n\"sequenceNodes\": [\n    {\n        \"sequenceType\": \"SerialNode\",\n        \"nodes\": [\n            {\n                \"command\": \"speak\",\n                \"value\": \"This is a test speak.\",\n                \"device\": \"DeviceA\"\n            },\n            {\n                \"command\": \"speak\",\n                \"value\": \"This is a second test speak.\",\n                \"device\": \"DeviceA\"\n            }\n        ]\n    },\n    {\n        \"sequenceType\": \"SerialNode\",\n        \"nodes\": [\n            {\n                \"command\": \"speak\",\n                \"value\": \"This is a test speak.\",\n                \"device\": \"DeviceB\"\n            },\n            {\n                \"command\": \"speak\",\n                \"value\": \"This is a second test speak.\",\n                \"device\": \"DeviceB\"\n            },\n            {\n                \"sequenceType\": \"ParallelNode\",\n                \"nodes\": [\n                    {\n                        \"command\": \"flashbriefing\",\n                        \"device\": \"DeviceC\"\n                    },\n                    {\n                        \"command\": \"flashbriefing\",\n                        \"device\": \"Device B\"\n                    }\n                ]\n            }\n        ]\n    }\n]\n\n```\n\n## Troubleshooting\n\n### Problem with Cookie determination with SMS based 2FA flow\nIf you still use the SMS/E-Mail based 2FA flow, then this might not work. Please update the 2FA/OTP method in the amazon settings to the current process! Not working could also mean that a Error 404/Page not found is shown. ALso then check and upgrade OTP settings!\n\n### The Alexa App opens when I try to log in\nIf you open the Proxy URL from a mobile device where also the Alexa App is installed on, it might be that it does not work because Amazon might open the Alexa App. So please use a device or PC where the Alexa App is not installed!\n\n### I get a page shown with a QR code telling me to scan it\nIf you see a page that tells you that \"alexa.amazon.xx is deprecated\" and you should use the alexa app and with a QR code on it when you enter the Proxy URL\" then this means that you call the proxy URL ith a different IP/Domainname then you entered in the \"proxy own IP\" settings, or you adjusted the IP shown in the Adapter configuration.\n\nThe \"own IP\" setting **needs to** match the IP/Domainname you use to call the proxy URL!\n\n### Problems with Cookie determination via E-Mail/Password\nSometimes Amazon has weired checks in place when they detect unexpected traffic on Login.\nThis can result in the problem that a captcha needs to be answered in order to log in.\nMostly, this captcha needs to be answered once and after this, the login works without Captcha.\n\nWhen you need to answer such a captcha, then try to do the following:\n* Use a common Browser (e.g., Chrome)\n* disable Javascript!\n* clear all cookies that may exist for Amazon or use Private/Incognito mode of the browser\n* call https://alexa.amazon.de\n* you should get a login form (normally displayed for older mobile browsers)\n* login there with your Amazon credentials where the Echo/Alexa is registered in\n* you may need to log in twice or solve a Captcha\n* In the end, you should see \"https://alexa.amazon.de/spa/index.html\" as URL but without any real content (because JS is still disabled), BUT THIS IS COMPLETELY OK!!!!\n* now try to get cookie again\n* if it still not works, do it again and check the User-Agent and accept-Language from your browser and use those in adapter on next try\n\nAdditionally, the Accept-Language-Header (default to \"de-DE\") needs to match with your language/the browser language/the language of the amazon page you log in.\n\nYou can also try to play around with the User-Agent and use one which more matches to the system type you use.\nAs example using \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\" as User-Agent was reported as working better when ioBroker runs on a linux system.\n\nYou can override all those parameters in the adapter configuration.\n\n### Push Connections do not connect\nSometimes it could happen that because of too many connection tries aAmazon blocks the push connection endpoint for a specific IP and \"device\".\n\nIf the Push connection is never established, then you can try to use the following:\n* delete the instance of the adapter\n* check if there are files like /opt/iobroker/node_modules/iobroker.alexa2/formerDataStore*.json - if existing please delete them\n* add new instance and get new cookie\n\nThen it should work again\n\n### I have too many App/\"This device\" devices in my list of Echo-Devices\nThe adapter reads whatever Amazon reports. Sometimes unused and old Apps or other connections stay in that list.\nIf you want to clean this up, you need to do that by visiting the Amazon website and remove the devices there.\n\nLink: https://www.amazon.de/hz/mycd/digital-console/devicedetails?deviceFamily=ALEXA_APP\n\nAfter deleting an unused device, please restart the adapter to remove them there too.\n\n### 🌍 Region‑specific Amazon configuration\n\nSome users may encounter crashes when first configuring the adapter.  \nThis is often caused by the adapter being **preconfigured for the German Amazon region (amazon.de)**.  \nIf your Alexa account is registered in another Amazon marketplace, you must update few settings so the adapter communicates with the correct regional endpoints.\n\nWhen these values are not adjusted, authentication and cookie might work but failure will occurs when fetching your devices, leading to adapter startup errors and got a terminate signal TERMINATE_YOURSELF.\n\nTo correct the regional endpoints, go to `Instances > alexa2.x > Settings > Cookie` and configure the values as below.\n\n| Region / Country | Alexa Base URL        | Accept‑Language  | Cookie Amazon URL  |\n|------------------|-----------------------|------------------|--------------------|\n| **France** 🇫🇷    | `alexa.amazon.fr`     | `fr-FR`          | `amazon.fr`        |\n| *(Add more...)*  |                       |                  |                    |\n\nIf you region is not present, and you find out the value, do not hesitate to enrich this documentation with your findings.\n\n## Changelog\n<!-- ### __WORK IN PROGRESS__ -->\n### 3.27.4 (2025-11-06)\n* (@Apollon77) Adjusts authentication check to recent Amazon changes\n\n### 3.27.3 (2025-07-15)\n* (Apollon77) Adjust Lists to recent Amazon changes\n* (Apollon77) Adjust retrieving the Smart home device list to recent Amazon changes\n* (Apollon77) Delete all ioBroker originated Smart Home devices because not controllable anyway\n* (Apollon77) Adjust usage of some deprecated js-controller methods\n\n### 3.26.7 (2024-10-24)\n* (Apollon77) Fix Sentry integration\n\n### 3.26.6 (2024-10-20)\n* (simatec) Responsive Design added\n\n### 3.26.5 (2024-04-16)\n* (Apollon77) Adjust History query to recent Amazon changes\n\n### 3.26.4 (2024-01-25)\n* (Apollon77) Removed Weblink\n* (Apollon77) Adjust History query to recent Amazon changes\n\n### 3.26.3 (2023-11-25)\n* (Apollon77) Fixed the proxy login process\n\n### 3.26.2 (2023-11-24)\n* (Apollon77) Removed infos how to get cookies manually because no longer available\n* (Apollon77) Optimized Admin configuration order of settings for history\n* (Apollon77) Fixed some crash cases reported by Sentry\n* (Diginix/Apollon77) Added some more device types\n\n### 3.26.1 (2023-11-08)\n* (Apollon77) Fix missing text in Admin Config\n\n### 3.26.0 (2023-11-08)\n* (Apollon77) Automatically query of activity/history needs to be enabled manually (if you need it!)\n\n### 3.25.5 (2023-10-29)\n* (Apollon77) Optimize activity detection to process all relevant entries in all cases\n\n### 3.25.4 (2023-10-27)\n* (Apollon77) Optimize activity detection to process all relevant entries and not just the last one\n\n### 3.25.3 (2023-10-27)\n* (Apollon77) Adjust History update to work with recent Amazon changes\n\n### 3.25.2 (2023-09-12)\n* (Apollon77) Optimize reconnection handling for push connections\n\n### 3.25.1 (2023-09-09)\n* IMPORTANT: Minimum required Node-js version is 16 now!\n* (Apollon77) Updated the Push connection after Amazon technology changes\n* (Apollon77) Added some more device types\n\n### 3.24.1 (2023-08-08)\n* (Apollon77) Work around Amazon changes that affected all functions over the Push connection\n* (Apollon77) Some smaller fixes and adjustments\n\n### 3.23.2 (2022-11-30)\n* (Apollon77) Prioritize real devices higher than app devices when serialnumbers overlap\n\n### 3.23.1 (2022-11-26)\n* (Apollon77) Enhance checks when changing smart device values\n* (Apollon77) Try to prevent Amazon rate limiting (again)\n\n### 3.21.0 (2022-11-03)\n* IMPORTANT: Because of rate limits by Amazon, I decided to remove the update of smart home device values in intervals because it seems to produce too much load in Skills and Amazon systems.\n* (Apollon77) Optimizes loading of smart home device states\n* (Apollon77) Fixed issue with enabling/disabling of Alarms in combination with non-default music for the alarm\n* (Apollon77) Prevented that Timers or Alarms that are long in the future to trigger their trigger state too early\n* (Apollon77) Fixed deleting own user Messages state\n* (Apollon77) Already request Notification updates when history registered a Notification action and do not wait for Push info to come in\n\n### 3.20.1 (2022-10-29)\n* (Apollon77) make sure caching works correctly with multiple instances of the adapter\n\n### 3.20.0 (2022-10-29)\n* (Apollon77) Increase minimum interval for requesting smart home device data to 15 minutes (900s) because of Amazon rate limits. Please do not try to work around it!\n* (Apollon77) Cache Smart home device list and data to prevent too many requests when restarting adapter in short intervals and to prevent deleted smart home devices also on further rate limit issues\n\n### 3.19.10 (2022-10-27)\n* (Apollon77) Fix issue in retry handling when rate limit exceeded is returned by Amazon\n* (Apollon77) Do not clean up Smart Home Device objects for now - delete manually if you remove a device\n\n### 3.19.9 (2022-09-12)\n* (Apollon77) Receive the correct player status again when musik is stopped\n\n### 3.19.8 (2022-09-07)\n* (Apollon77) Add safeguard for too high intervals\n\n### 3.19.7 (2022-08-19)\n* (Apollon77) Fix doNotDisturb when using a time string\n\n### 3.19.6 (2022-08-18)\n* (Apollon77) Fix doNotDisturb when using a time string\n\n### 3.19.5 (2022-08-09)\n* (Apollon77) Fix doNotDisturb for groups\n\n### 3.19.4 (2022-08-07)\n* (Apollon77) Prevent unwanted device Name updates\n\n### 3.19.3 (2022-08-07)\n* (Apollon77) Fix crash check with multiple adapter instances\n\n### 3.19.2 (2022-08-06)\n* (Apollon77) Fix Alarm creation when just providing time and it is for next day\n\n### 3.19.1 (2022-08-04)\n* (Apollon77) Fix retry handling\n\n### 3.19.0 (2022-08-04)\n* (Apollon77) Preserve Names as soon as it is an App type in general\n* (Apollon77) Enhance checks and safeguards for polling intervals\n* (Apollon77) Check for restart intervals that do not make sense and stop adapter if detected\n* (Apollon77) Add additional crash-loop detection\n\n### 3.18.6 (2022-07-19)\n* (Apollon77) Fix deviceStop sequence command\n\n### 3.18.5 (2022-07-19)\n* (Apollon77) Fix crash case reported by Sentry\n* (arteck) Add image for Fire Cube\n\n### 3.18.3 (2022-07-18)\n* (Apollon77) Fix doNotDisturb and doNotDisturb for All devices\n* (Apollon77) Update do not disturb status after set for all devices\n* (Apollon77) preserve a changed name for a \"This device\" device object\n\n### 3.18.2 (2022-07-18)\n* (Apollon77) Enable commands again for Apps with type A2TF17PFR55MTB - will only work sometimes as it seems\n\n### 3.18.1 (2022-07-18)\n* (Apollon77) Optimize Handling when push connection could not be established\n\n### 3.18.0 (2022-07-18)\n* IMPORTANT: Smart home device values are from now on only synchronized when enabled via #includeInIntervalQuery state. Enable only what's really needed!\n* (Apollon77) Allow to query several more smart home device states (incl. the Echo own Temperature-Sensor if available) and more optimizations\n* (Apollon77) Optimize querying smart home device states to only request relevant properties\n* (Apollon77) Exclude some value types again from requesting from Amazon because they make no sense and will never contain meaningful data\n* (Apollon77) Add FireTV commands for FireTV devices\n* (Apollon77) Add CommandsAll.deviceStop and CommandsAll.deviceDoNotDisturb commands to be sent to all devices\n* (Apollon77) Add Equalizer preferences (if supported by devices)\n* (Apollon77) Add Speaker and AUX preferences (if supported by devices)\n* (Apollon77) Add Display (enabled, brightness, adaptive brightness) preferences (if supported by devices)\n* (Apollon77) Enhance doNotDisturb state to also allow specifying a enable duration or end timepoint\n* (Apollon77) Add a fallback to update music player when a new history record mentions music as target for the spoken words. Could help as fallback when push infos are not coming in sometimes with Sonos\n* (Apollon77) Delay initialization of push connection to when basic structures are initialized\n* (Apollon77) Add some more devices\n\n### 3.17.5 (2022-07-14)\n* (Apollon77) Minimum smart home device query interval is now 5 minutes and not 1 minute anymore to remove some requests for now\n\n### 3.17.4 (2022-07-13)\n* (Apollon77) Make sure disabling query intervals really work (disabling smart home device and state and configuration was not possible)\n\n### 3.17.3 (2022-07-12)\n* (Apollon77) Prevent datatype warnings in log\n\n### 3.17.2 (2022-07-12)\n* (Apollon77) Another adjustment for smart home device data readings\n* (Apollon77) Fix crash cases reported by Sentry\n\n### 3.17.1 (2022-07-12)\n* (Apollon77) Work around timing issues with speak-volume when using announcement\n* (Apollon77) Correctly initialize volume/mute on startup also when player data are not available\n* (Apollon77) Do not overwrite speak-volume (and some other fields) with null on adapter start\n* (Apollon77) Fix crash cases reported by Sentry\n\n### 3.17.0 (2022-07-11)\n* (Apollon77) Add support to play Audible books in Music-Providers\n* (Apollon77) Optimize deletion of alarms and reminders\n* (Apollon77) Optimize requesting smart home device data\n\n### 3.16.1 (2022-07-11)\n* (Apollon77) Fix deletion and cancellation of Alarms and Reminders\n\n### 3.16.0 (2022-07-11)\n* (Apollon77) Add (official) support for Music-Alarms - they are now listed under \"Alarms\" together with the other Alarms! The \"sound\" list will contain the device specific music targets - so you can basically zse the ones that you created at least once via voice commands.\n* (Apollon77) For a Music Alarm the \"customVolume\" on the alarm is used to set the normal device volume 2s before the alarm but do not (!) reset it afterwards\n* (Apollon77) Prevent crashes on one time Alarms that just triggered\n\n### 3.15.2 (2022-07-09)\n* (Apollon77) Fix case where initialization of the adapter was never finished and so nothing was controllable when App devices where not synced\n\n### 3.15.1 (2022-07-09)\n* (Apollon77) Convert Smarthome device values if wrong datatype is delivered by device\n* (Apollon77) Add handling for two more battery health states for smart home devices\n* (Apollon77) Fix crash case when initializing notifications\n\n### 3.15.0 (2022-07-09)\n* (Apollon77) IMPORTANT: Format to specify multiple Details on \"New\" for Alarms and Reminders changed, see documentation!\n* (Apollon77) Add Alarm/Reminder triggered state per device which will contain the ID of the alarm that got triggered when it is triggered\n* (Apollon77) Add several more fields for Alarms and Reminders to show better the details of the alarm\n* (Apollon77) Allow to cancel Reminders and Alarms as in the Alexa App\n* (Apollon77) Allow to also edit Alarm/Reminder Dates additionally to the times\n* (Apollon77) Allow to set a custom Volume for Reminders and Alarms - it will be set 2s before the expected trigger and restored afterwards\n* (Apollon77) Calculate the \"nextTriggerDate\" as Timestamp of next expected triggering\n* (Apollon77) Add a JSON-Array with all running timers and the \"next id\" as state\n* (Apollon77) Allow to stop a timer by ID\n* (Apollon77) Add the days-list of Alarms when configured for recurrency\n* (Apollon77) Add new Commands skill and skillYours to start Skills\n* (Apollon77) Add Notification volume, Ascending Alarm setting and default notification sounds as preferences\n* (Apollon77) Slow down the initialization of all data a bit, so startup could take longer\n\n### 3.14.0 (2022-07-06)\n* (Apollon77) Allow to define if Lists and Smart home devices are synced by the adapter with the Amazon Cloud at all\n* (Apollon77) Enhance Smart Home Device support by adding various controllers and states. If in your Alexa App something is configurable which is not in ioBroker please send a debug log!\n* (Apollon77) Re-Introduce the ability to poll smart home device states in intervals, but only devices are queried that report their status proactively to Amazon-Cloud to prevent Skill developer costs! ioBroker (and OpenHab) devices are NOT queried! The interval can be configured but must not be lower than 60s! Querying is disabled by default.\n* (Apollon77) Add message to send out sequences of commands to alexa devices\n* (Apollon77) Add Info states for macAddress and WifiSSID of the Alexa devices\n* (Apollon77) Add several new states for Player for allowed actions, medium quality\n* (Apollon77) Add mediaId and also allow to set it to jump to a defined entry in the playlist\n* (Apollon77) Add Commands.sound to play a sound\n* (Apollon77) Do not set the speak-volume when executing textCommand and deviceStop\n* (Apollon77) Do not set speak-volume if the volume is already as wanted when executing commands\n* (Apollon77) update Do-Not-Disturb status once on start and with device configuration updates\n* (Apollon77) Allow to specify the title in notification commands\n* (Apollon77) When a device plays music in a group then new states in \"Player\"will indicate this together with the group ID\n* (Apollon77) Allow to enable and disable smart home devices - this will be synced together with the smart home state updates from the cloud if changed in the app!\n* (Apollon77) Detect Rate limit exceeded response and do one automatic request retry 10s later (plus a random part)\n* (Apollon77) Slow down the update of player status to prevent rate limit exceeded errors. initial update of the player states is delayed on startup of the adapter\n* (Apollon77) Restore character replacement for Music providers (space is now again a \"-\")\n* (Apollon77) Add more devices\n* (Apollon77) Optimize startup and unload handling\n\n### 3.13.0 (2022-07-02)\n* IMPORTANT List Names are now checked for invalid characters and replaced. Might change the name of objects in ioBroker. Old ones need to be deleted manually!\n* (Apollon77) Fix command sending in multi owner environments (e.g. Family shared devices)\n* (Apollon77) Add some new devices\n* (ammawel) Add the date of an alarm as state, not only time\n* (Apollon77) Add option to also query the App Devices to allow to send commands to them\n* (Apollon77) Rework Multiroom for commands to prevent rate limiting issues!\n* (Apollon77) Fix Routine Naming if triggers were used\n* (Apollon77) Support devices with \"Ziggy\" as wake word\n* (Apollon77) All commands with voice output respect the defined speak-volume now\n* (Apollon77) Allow again to directly enter TuneIn station Ids (s*) and topicIds (t*)\n* (Apollon77) Add media states to show remaining time of media playback\n* (simatec) Adjust link color im Admin configuration\n* (Apollon77) Some requests are automatically retried with a slight delay if Amazon responds with error 503\n\n### 3.12.0 (2021-11-13)\n* (Apollon77) SequenceNodes created for a device are now bound to the \"deviceOwnCustomer\" - should help in mixed owner groups\n* (ammawel) Add recurringPattern for Notifications (see Readme)\n* (Apollon77) Fix crash case\n* (Apollon77) Make sure states are not set too early before objects are created\n\n### 3.11.2 (2021-10-12)\n* (Apollon77) Fix crash case (Sentry IOBROKER-ALEXA2-AT)\n\n### 3.11.1 (2021-10-12)\n* (Apollon77) Prevent warnings with js-controller 3.3\n\n### 3.11.0 (2021-10-12)\n* (Apollon77) Add support for Multi Utterance Routines\n* (Apollon77) Fix object deletion for lists\n* (Apollon77) Fix Creation of new Lists and add deletion support\n* (Apollon77) Allow Commands for Stereo Pairs\n* (Apollon77) Optimize Push Connection and History retrieval\n\n### 3.10.4 (2021-10-11)\n* IMPORTANT: Node.js 10 support is dropped, supports LTS versions of Node.js starting with 12.x\n* (Apollon77) Update Push Connection\n\n### 3.9.3 (2021-07-11)\n* (Apollon77) Try to fix setting targetTemperature for ThermostatController\n\n### 3.9.2 (2021-07-05)\n* (Apollon77) Only ignore empty history entries if both, summary and alexaResponse is empty\n\n### 3.9.1 (2021-06-04)\n* (Apollon77) Fix cookie exchange and cookie validation checks\n\n### 3.9.0 (2021-05-11)\n* (Apollon77) Add some new devices\n* (Apollon77) Always recognize \"alexa\" as wakeword to handle commands via the apps correctly\n\n### 3.8.4 (2021-05-11)\n* (Apollon77) Optimize Cookie refresh handling\n* (Apollon77) Fix warnings from js-controller 3.3 and optimize\n\n### 3.8.2 (2021-04-19)\n* (Apollon77) Adjust automatic Cookie Refresh interval from 7 to 4 days\n\n### 3.8.1 (2021-02-09)\n* (Apollon77) Initialize volume for all devices on start\n\n### 3.8.0 (2021-02-04)\n* (Apollon77) Add configuration option to not write history entries where no command text was recognized\n\n### 3.7.1 (2021-02-03)\n* (Apollon77) add some more detected text into summary and answerText states (textCommand commands should be in history back again)\n\n### 3.7.0 (2021-02-03)\n* (Apollon77) IMPORTANT: History entries are now requested via a different data source because Amazon seems to tun off the old option. History.status is for this no longer filled, but new states were added. Only voice commands are reported ( textCommand entries not longer)\n* (Apollon77) other optimizations in communications and prevent hammering amazon with requests in error cases\n\n### 3.6.1 (2021-02-02)\n* (fbeister) Add and adjust some known devices\n* (Apollon77) Optimize object deletion\n\n### 3.6.0 (2021-01-28)\n* (Apollon77) Update Routines API because of amazon changes\n\n### 3.5.6 (2021-01-22)\n* (Apollon77) Catch error when deleting objects\n\n### 3.5.4 (2021-01-22)\n* (Apollon77) restart adapter when no initial cookie could be requested\n\n### 3.5.2 (2021-01-17)\n* (Apollon77) Prevent to write non-existing state values\n* (Apollon77) Add and adjust some known devices\n\n### 3.5.0 (2020-12-24)\n* (Apollon77) Remove bespoken because textCommand is more flexible\n* (Apollon77) Add and adjust some known devices, add Echo 4 image\n\n### 3.4.0 (2020-12-11)\n* (Apollon77) add support for textCommand - tell an Alexa device a text as you would speak it\n* (Apollon77) make sure discovery of devices is still possible also after deleting all devices before\n\n### 3.3.5 (2020-12-03)\n* (Apollon77) make sure music providers with empty names do not produce errors\n\n### 3.3.2 (2020-11-23)\n* (Apollon77) prevent crash cases and optimize reconnection handling\n\n### 3.3.1 (2020-07-24)\n* (Apollon77) Further optimize Cookie handling\n\n### 3.3.0 (2020-07-19)\n* (Apollon77) Hopefully allow easier upgrades if old deviceId is invalid now\n* (Apollon77) Allow to have separate deviceIds per instance\n\n### 3.2.8 (2020-07-16)\n* (Apollon77) Work around Amazon Security changes and make proxy working again\n\n### 3.2.7 (2020-07-15)\n* (Apollon77) Work around Amazon Security changes and make proxy working again\n* (arteck) add echo studio\n\n### 3.2.6 (2020-07-15)\n* (Apollon77) Work around Amazon Security changes and make proxy working again\n\n### 3.2.5 (2020-07-13)\n* (Apollon77) Work around Amazon Security changes and make proxy working again \n* (Apollon77) fix Sentry crash case when Amazon do not respond correctly (IOBROKER-ALEXA2-1C)\n\n### 3.2.4 (2020-06-18)\n* (Apollon77) Update Alexa-Remote Library to optimize communication error/timeout cases\n\n### 3.2.3 (2020-06-17)\n* (Apollon77) Fix currentState handling\n\n### 3.2.2 (2020-06-17)\n* (Apollon77) remove goodnight because was not working\n* (Apollon77) Fix Play/Pause states and some media optimizations\n\n### 3.2.1 (2020-06-17)\n* (Apollon77) update amazon-cookie library: another optimization for Node.js 14\n\n### 3.2.0 (2020-06-17)\n* (Apollon77/hive) add new commands, jokes/facts/goodnight/cleanup\n* (Apollon77/hive) add new command curatedtts with allowed values [\"goodbye\", \"confirmations\", \"goodmorning\", \"compliments\", \"birthday\", \"goodnight\", \"iamhome\"] to play random curated sentences\n* (Apollon77) Prevent some crashes\n* (Apollon77) Make sure Timer are not triggering the state when deleted\n* (Apollon77) make sure that Lists objects are deleted correctly when deleting\n* (Apollon77) Make compatible with nodejs 14\n* (Apollon77) Adjust to changes from Amazon so that initial Proxy process works again\n* (OberstVonGatow) Make sure that for Spotify Media data requests do not have negative effects and stop the playback\n\n### 3.1.2 (2020-03-18)\n* (Gieskanne/Apollon77) Add Next Timer Date as state\n* (Apollon77) Fix missing history entries\n* (Apollon77) Prevent List deletions from logging errors\n* (Apollon77) optimiztions, dependency updates and fixes\n* (Apollon77) Switch to ioBroker own sentry instance\n* (Apollon77) add Info.softwareVersion\n\n### 3.0.8 (2020-01-19)\n* (Apollon77) fix some crash cases\n* (Apollon77) Update Sentry DSN and add filtering\n* (Apollon77) Update deps\n\n### 3.0.7 (2019-12-28)\n* (Apollon77) Prevent some errors\n\n### 3.0.6 (2019-12-26)\n* (Apollon77) Prevent some errors\n\n### 3.0.5 (2019-12-25)\n* (Apollon77) Prevent some errors\n\n### 3.0.4 (2019-12-24)\n* (Apollon77) Prevent some errors\n\n### 3.0.3 (2019-12-24)\n* Adapter needs nodejs 8+ and js-controller 2.0 now!\n* (Zefau) add functionality for handling of lists\n* (Apollon77) Add answerText when available from history\n* (Apollon77) handle error for empty valueMaps for ColorTemperatures\n* (Apollon77) also support names for new special routines (Alarm Notifications, Sensor Detections, ..)\n* (Apollon77) GitHub Actions for Test& Build\n* (Apollon77) Add Sentry for error reporting\n* (Apollon77) prevent some crashed after changes by Amazon\n* (Apollon77) fix Routine names after changes by Amazon\n* (Apollon77) add some devices and new images\n* (Apollon77) Add more situations to update player status because amazon send no info anymore on title changes\n\n### 2.6.4 (2019-07-25)\n* (Apollon77) add some error handling for contacts\n\n### 2.6.1 (2019-07-22)\n* (Apollon77) add new device\n* (Apollon77) fix volume logic for ssml\n* (Apollon77) Allow reminders to bet set >+ 1day\n\n### 2.6.0 (2019-07-21)\n* (Apollon77) added possibility to send text messages to users including himself, allows deletion of all messages to himself\n* (Apollon77) added option to reset Cookies. After sahev the adapter will restart and needs to get a new Login (see adapter config)\n* (Apollon77) change announcement and ssml to send commands more synchronous\n\n### 2.5.0/1 (2019-07-07/18)\n* (INgoRah) Support compact mode\n* (Apollon77) enhance error handling for broken authentications\n\n### 2.4.6 (2019-07-05)\n* (Apollon77) enhance error handling for broken authentications\n\n### 2.4.5 (2019-07-01)\n* (Apollon77) enhance error handling for broken authentications\n\n### 2.4.4 (2019-06-26)\n* (Apollon77) new devices added\n\n### 2.4.3 (2019-06-25)\n* (Apollon77) enhance error handling for Amazon Push Infos\n\n### 2.4.1/2 (2019-06-23)\n* (Apollon77) Allow to specify an external docker container IP to override Proxy-IP\n* (Apollon77) Add more Devices from GitHub\n* (Apollon77) try to work around an Image URL bug from Amazon\n* (Apollon77) optimize Admin display of Status/Link\n* (Apollon77) add Link to https://alexa.amazon.com to Admin instance overview\n* (Apollon77) Remove Admin2 support\n* (Apollon77) Optimize Handling from DNS errors (hopefully) to prevent stopped Adapters on Internet/DNS problems\n\n### 2.3.3 (2019-06-21/22)\n* (Apollon77) adjust to current Amazon changes\n* (Apollon77) fix volume handling\n* (Apollon77) Add some more devices\n* (Apollon77) Logging reduced\n* (Apollon77) unknown devices get commands activated automatically\n* (Apollon77) remove Email/Password fields and add info about login to Admin screen (still needs to be polished, only Admin v3)\n* (Apollon77) detect App-Devices and remove them from the list because they are not usable in any way\n\n### 2.2.0 (2019-01-xx) [unpublished]\n* (Apollon77) add new sequenceCommands \"calendarNext\", \"calendarToday\", \"calendarTomorrow\"\n* (Apollon77) fix wake word handling and history sanitizing\n\n### 2.1.0 (2019-01-13) [unpublished]\n* (Apollon77) cookie handling completely rewritten, no email/password anymore, only Proxy (still only from log)\n* (Apollon77) fixes routine triggering that triggered on wrong device sometimes\n* (Apollon77) added new commands \"deviceStop\", \"announcement\", \"notification\", and \"ssml\" (see documentation above)\n\n### 1.1.3 (2018-11-17)\n* (Apollon77) optimize cookie handling again\n\n### 1.1.2 (2018-11-17)\n* (Apollon77) new devices\n* (Apollon77) make proxy for cookies work again\n\n### 1.1.1 (2018-11-09)\n* (Apollon77) new devices\n* (Apollon77) make proxy for cookies work again\n\n### 1.1.0 (2018-09-18)\n* (Apollon77) Further optimizations to lower number of requests\n* (Apollon77) Experimental support for Playlist IDs (p1234567) in TuneIn-Station\n\n### 1.0.1 (2018-09-16)\n* (Apollon77) fixes and important changes to make sure not too many requests are sent\n\n### 1.0.0 (2018-09-06)\n* (Apollon77) polishng and finalization, make it 1.0.0\n\n### 0.7.5 (2018-09-04)\n* (Apollon77) speak can now contain separated text by semicolons. These Texts will then be spoken sequencially. So the old limit if 250 characters is only existing for one such text part. So, now longer texts are possible too. Separate it with a semicolon.\n* (Apollon77) more color handling fixes\n\n### 0.7.0 (2018-08-30)\n* (Apollon77) Add Bespoken Virtual device support to be able to interact with Alexa infrastructure\n* (Apollon77) add new Device Types for Smarthome-integration (Contact and Motion sensors)\n\n### 0.6.4 (2018-08-30)\n* (Apollon77) fixes to colorhandling\n* (Apollon77) allow to deliver a volume together with aspeak command by using \"80;text\" and then volume is set before speak and reset afterwards. Experimental!\n\n### 0.6.1 (2018-08-24)\n* (Apollon77) sometimes new alarms were not triggered in adapter\n* (Apollon77) add support to control smart devices and groups (and also add groups). Because I was only able to test a few types i added logging. please check log, try out and report back!\n* (Apollon77) When routines are executed via voice command and push connection is enabled the routine state is also triggered by \"true\" with ack=true when routine trigger text is matching exactly to spoken text\n* (Apollon77) corrected volume and mute handling in states, a volume of 0 is also seen as \"muted\" if muting flag is not supported by device\n* (Apollon77) when speak text is coming from cloud adapter and contains SSML tags they will be filtered out, so you can use a speak endpoint directly to output response from Smart Home skill actions\n\n### 0.5.2 (2018-08-16)\n* (Apollon77) fix an error when getting new cookie\n* (Apollon77) add new \"Playlist\" states for the Music providers to directly prepend \"playlist\" :-)\n* (Apollon77) Volumes are not updated for multiroom devices when === 0\n* (Apollon77) Add Reminder and Alarms support. Write time and pot. text separated by comma into the \"New\" stat to create a new one (e.g. \"10:00:00, Test-Reminder\")\n* (Apollon77) Also with Push-Connection some times states are generally updated to make sure data are correct (e.g. player media info will disappear 2h after stopping the music)\n* (Apollon77) Added some more deviceTypes\n\n### 0.4.0 (2018-08-13)\n* (Apollon77) internal Refactoring\n* (Apollon77) states that are not needed anymore will be removed. This will be logged for now, so please check this and give feedback!\n* (Apollon77) sanitized music provider state names (spaces are now dashes ... should be removed automatically)\n* (Apollon77) Renamed TuneIn-Direct to TuneIn-Station (even if you still can enter text to search, this works with stations too) ... should be removed automatically)\n* (Apollon77) Device and Bluetooth status is now also checked at states update\n* (Apollon77) After enabling Push-Connection the configured polling is turned off and anything is done based on real time informations from Alexa. Test it\n* (Apollon77) Enhanced History states to include the status of the action (SUCCESS, FAIL ...), infos from returned cards (if available) and info on accessed skill for this action.\n* (Apollon77) When using Push-Connection History update is also updated automatically. An empty summary with status DISCARDED_NON_DEVICE_DIRECTED_INTENT means the activation of the echo by saying the wake word\n* (Bluefox) Add icons for some of the devices for Admin\n\n### 0.3.8 (2018-07-27)\n* (Apollon77) Several Multiroom-fixes\n* (Apollon77) fixed shuffle/repeat\n* (Apollon77) fixed status for play, pause, shuffle and repeat\n\n### 0.3.4 (2018-07-27)\n* (Apollon77) Only 20 Routines were queried, now up to 2000\n* (Apollon77) Also allow commands including speak for multiroom, BUT it is triggered per device, so NO synchronous audio output!!\n* (Apollon77) Thanks to Matten-Matten also Music-provers can be started on multiroom devices\n\n### 0.3.2 (2018-07-25)\n* (Apollon77) Fix volume settings for multiroom devices (please report other devices where it is not working)\n* (Apollon77) Add serial number and name to Info\n\n### 0.3.0 (2018-07-24)\n* (Bluefox) Admin3 fixes and slight changes to roles and code\n* (Apollon77) Reworked state names (hopefully last time!)\n* (Apollon77) Combine Player-Control and Player-Info into channel Player to support better detection and material support\n* (Apollon77) Added further information in Infos states per echo device\n* (Apollon77) Try to detect the type of the device different and decide if commands are available or not (till capabilities are known better)\n* (Apollon77) New \"Music-Provider\" states depending on available music providers with possibility to enter a text to play something (same as you would speak it)\n* (Apollon77) Volume is send different now, so that it also works when Device player get's inactive\n\n### 0.2.4 (2018-07-22)\n* (pix) materialize settings window\n* (Apollon77) WOn IP is set automatically with IP from first network interface\n* (Apollon77) fix comma replacements in speaks, do not speak empty text\n* (Apollon77) if Device is Multiroom, the do not create Routines and Commands and not bluetooth\n* (Apollon77) add information about multiroom device and master (later we can use this to sort out commands that are impossible with multiroom)\n* (Apollon77) History is also stored as JSON, so it can be used to monitor one datapoint and have all infos on updateState\n* (Apollon77) Several other fixes\n\n### 0.2.3 (2018-07-20)\n* (Apollon77) in Numbers with . are replaced by commas\n\n### 0.2.2 (2018-07-20)\n* (Apollon77) Finally fix device renaming\n\n### 0.2.1 (2018-07-20)\n* (Apollon77) Small fix of history channel type and setting states initially\n\n### 0.2.0 (2018-07-20) (as iobroker.alexa2)\n* (Apollon77) 0.2.0: added many Player-Info datapoints including \"progress updates \" when media is playing\n* (Apollon77) 0.2.0: removed \"Notifications\" because the only benefit for now is to show them, no interaction or change possible\n* (Apollon77) 0.2.0: adapter now allows to configure intervals for history updates and other data updates like player info\n* (Apollon77) 0.2.0: if cookie could not be determined correctly a proxy is started to allow manual login and cookie is catched in the background on success\n* (Apollon77) 0.2.0: add info datapoints for connection (connected to Alexa), cookie and csrf\n* (Apollon77) 0.2.0: rework complete logic to not use soef library anymore\n* (Apollon77) 0.2.0: Speaking free text at any timepoint is available under Commands.speak\n* (Apollon77) 0.2.0: Sequence-Commands (weather, traffic, flashbriefing, goodmorning, singasong, tellstory) are available to be triggered under \"Commands\"\n* (Apollon77) 0.2.0: Automation-Routines are now available to be triggered per device under \"Routines\"\n* (Apollon77) 0.2.0: Automatically use different user-agents for Win32, MacOS and Linux based systems\n* (Apollon77) 0.2.0: Automatically use different user-agents for Win32, MacOS and Linux based systems\n* (Apollon77) 0.2.0: Also support entering TuneIn-Station IDs (\"s\" plus 4-6 digits) to play that station\n\n### 0.1.0 (2018-07-10)\n* (Apollon77) get Adapter working again, especially getting cookie and optimize refresh\n\n### 0.0.x\n* soef versions\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2018-2025 Ingo Fischer <iobroker@fischer-ka.de>, 2017-2018 soef <soef@gmx.net>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "admin/i18n/de/translations.json",
    "content": "{\n    \"Email\": \"E-Mail\",\n    \"Password\": \"Passwort\",\n    \"Please wait while loading Adapter-Status ...\": \"Der Adapter-Status wird geladen. Bitte warten …\",\n    \"ProxyInfo\": \"Bitte den <a href=\\\"%s\\\" target=\\\"_blank\\\">folgenden Link zur einmaligen Anmeldung im Amazon-Konto</a> nutzen, um die Adapterverbindung zu Ihren Alexa-Geräten zu aktivieren.\",\n    \"Settings\": \"Einstellungen\",\n    \"StatusInfo\": \"Alexa-Cookies sind aktuell und zuletzt aktualisiert am %s\",\n    \"acceptLanguage\": \"Eigener Accept-Language-Header\",\n    \"alexaLogin\": \"Alexa Login Daten\",\n    \"alexaServiceHost\": \"Alexa Basis URL\",\n    \"auth\": \"Login-Daten für das Amazon-Konto, in dem die Alexa Geräte registriert sind\",\n    \"auth_info\": \"Die Login-Daten werden verschlüsselt in der lokalen ioBroker-Instanz gespeichert. Wenn die Daten nicht angegeben werden, muss das Cookie selbst ausgelesen werden oder über den Proxy des Adapters der Login vorgenommen werden, sodass das Cookie ermittelt werden kann. Dies muss aber manuell wiederholt werden sobald der Cookie ungültig geworden ist.\",\n    \"autoQueryActivityOnTrigger\": \"Fragen Sie die Aktivität bei Geräteinteraktion automatisch ab\",\n    \"autoQueryActivityOnTrigger_info\": \"Bitte nur aktivieren, wenn Sie Aktivitätsinformationen wirklich automatisch benötigen!\",\n    \"bespoken\": \"Virtuelles Bespoken Gerät\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"Die Bespoken Virtual Devices können verwendet werden, um Textbefehle an Ihr Amazon-Konto zu senden und gibt Ihnen eine eingeschränkte Kontrolle über Ihre Geräte - wenn möglich mit gesprochenen Wörtern. <br/> <br/> Sie müssen ein Bespoken-Konto und dann ein virtuelles Gerät erstellen Holen Sie sich ein Bespoken-Token wie unter https://read.Bespoken.io/end-to-end/setup/ beschrieben. Dann geben Sie unten das Token ein und Sie sehen neue Objekte für den Adapter. <br/> <br/> Hinweis: Bespoken erstellt aus Ihrem Text eine Audiodatei und sendet diese an die Alexa-APIs. Also ist die Verständnisqualität von Alexa so gut wie diese Sprachgenerierung, so dass es mit speziellen Wörtern problematisch sein kann.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Eigene Cookie-Amazon-URL\",\n    \"device_settings\": \"Geräteeinstellungen\",\n    \"device_settings_info\": \"Einstellungen für die Geräte, die von Amazon abgerufen werden\",\n    \"history\": \"Verlaufseinstellungen\",\n    \"historyIgnoreEmptySummary\": \"Verlaufseinträge ignorieren, wenn kein Befehl erkannt wurde\",\n    \"history_info\": \"Einstellungen für Verlaufsdaten, die in den \\\"History\\\" Zuständen verfügbar sind\",\n    \"includeAppDevices\": \"Auch App-Devices abfragen und hinzufügen\",\n    \"interval\": \"Datenaktualisierungsintervall\",\n    \"interval_info\": \"Bestimmte Daten können automatisch aktualisiert werden. Wie oft dies passiert, wird hier in Sekunden ferstgelegt. Über den Datenunkt history.#trigger kann jederzeit manuell eine Aktualisierung angestossen werden. Die Angabe von 0 deaktiviert die automatischen Aktualisierungen. \",\n    \"overwrite\": \"Überschreiben spezieller Parameter zur Cookie-Ermittlung und Kommunikation\",\n    \"overwrite_info\": \"Standardmäßig verbindet sich der Adapter mit einem deutschen Amazon-Konto. Bei der Nutzung von Accounts aus anderen Amazon-Ländern müssen hier die korrekten Daten eingetragen werden. Einige Hinweise dazu sind in der Adapter-README zu finden. Auch in speziellen Fällen, wenn die automatische Cookie-Ermittlung fehlschlägt, kann es helfen die Felder User-Agent und Accept-Language anzupassen.\",\n    \"password\": \"Passwort\",\n    \"proxy\": \"Proxy-Einstellungen\",\n    \"proxyListenBind\": \"Proxy-Beschränkung auf folgende IP (Listen Bind)\",\n    \"proxyOverrideIp\": \"Externe Container-IP (Docker)\",\n    \"proxyOwnIp\": \"Eigene IP oder Hostname für den Proxy-Link\",\n    \"proxyPort\": \"Proxy-Port\",\n    \"proxyPort_info\": \"(0 = zufälliger Port)\",\n    \"proxy_info\": \"Wenn die automatische Cookie-Ermittlung nicht funktioniert öffnet der Adapter einen Proxy um einen manuellen Login zu erlauben. Das Logfile und die Admin-Seite zeigt in dem Fall den aufzurufenden Link an. Diese mit dem Browser aufrufen, bei Amazon einloggen, alle potentiell angezeigten Captchas und Zwei-Faktor-Abfragen beantworten und das Cookie wird bei Erfolg im Hintergrund automatisch ermittelt.\",\n    \"push_connection\": \"Push-Verbindung\",\n    \"push_connection_info\": \"Mit der direkten Push-Verbindung werden die Änderungen von den Alexa-Geräten an den Adapter gemeldet und verwendet, um nur benötigte Daten in naher Echtzeit zu aktualisieren. Wenn einige aktivierte Daten-Update-Intervalle unten nicht verwendet werden. Es wird dringend empfohlen, die Push-Verbindung zu verwenden, um die von der Amazon-Cloud angeforderten Daten zu begrenzen!\",\n    \"resetCookie\": \"Amazon-Cookies zurücksetzen\",\n    \"s (0 = disabled)\": \"s (0 = deaktiviert)\",\n    \"sync_settings\": \"Synchronisierungseinstellungen\",\n    \"sync_settings_info\": \"Definition welche Daten von der Amazon Cloud zur ioBroker-Instanz synchronisiert werden sollen. Bitte nur die Daten aktivieren, die wirklich benötigt werden!\",\n    \"synchronizeLists\": \"Listen synchronisieren (z. B. Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Smart-Home-Geräte synchronisieren\",\n    \"updateConfigurationInterval\": \"Aktualisierungsintervall der Gerätekonfiguration\",\n    \"updateConfigurationInterval_info\": \"Die Gerätekonfiguration kann automatisch aktualisiert werden. Das Update-Intervall definiert, wie oft dies geschehen sollte. Stellen Sie das nicht zu niedrig, weil sonst Amazon Sie blockieren könnte! Das Minimum ist 300s oder auf 0 gesetzt, um das Update zu deaktivieren.\",\n    \"updateHistoryInterval\": \"History-Aktualisierungsintervall\",\n    \"updateHistoryInterval_info\": \"Das History Update-Intervall definiert, wie oft die History-Daten von den Amazon-Servern aktualisiert werden sollen. Das Minimum ist 60s oder auf 0 gesetzt, um das Update zu deaktivieren. Setzen Sie das nicht zu niedrig, weil sonst Amazon Sie blockieren könnte! Wenn die Push-Connection festgestellt wird, wird das Update-Intervall nicht verwendet, da Push-Informationen es bei Bedarf automatisch auslösen.\",\n    \"updateSmartHomeDevicesInterval\": \"Aktualisierungsintervall für Smart-Home-Geräte\",\n    \"updateSmartHomeDevicesInterval_info\": \"Bitte dieses Intervall nur aktivieren, wenn die Werte der Smart Home-Geräte wirklich benötigt werden! Nur die intelligenten Home-Geräte, die #includeInIntervalQuery in den Objekten aktiviert haben und aktiv ihren Status auf Amazon-Systeme melden, werden aktualisiert, um sicherzustellen, dass keine Probleme und Kosten auf der Entwicklerseite des Skills entstehen! Bitte das Intervall nicht zu niedrig, setzen, weil sonst Amazon Sie blockieren könnte! Das Minimum ist 900s oder kann auf 0 gesetzt werden, um das Update zu deaktivieren (Update kann manuell über #query Zustände ausgelöst werden).\",\n    \"updateStateInterval\": \"Datenaktualisierungsinterval (Player-Info ...)\",\n    \"updateStateInterval_info\": \"Das allgemeine Datenupdate-Intervall des Geräts definiert, wie oft die Daten von den Amazon-Servern aktualisiert werden sollen. Das Minimum ist 300s oder wird auf 0 gesetzt um das Update zu deaktivieren. Setzen Sie das nicht zu niedrig, weil Amazon Sie sonst blockieren könnte! Wenn die Push-Connection hergestellt ist wird das Update-Intervall auf 60 Minuten gestellt, falls aktiviert.\",\n    \"usePushConnection\": \"Push-Verbindung verwenden\",\n    \"userAgent\": \"Eigener User-Agent\"\n}\n"
  },
  {
    "path": "admin/i18n/en/translations.json",
    "content": "{\n    \"Email\": \"E-Mail\",\n    \"Password\": \"Password\",\n    \"Please wait while loading Adapter-Status ...\": \"Please wait while loading Adapter-Status …\",\n    \"ProxyInfo\": \"Please click <a href=\\\"%s\\\" target=\\\"_blank\\\">here to log in to your Amazon Account once</a> to activate the connection to your Alexa devices for this instance.\",\n    \"Settings\": \"Settings\",\n    \"StatusInfo\": \"Alexa-Cookies are up to date and last updated on %s\",\n    \"acceptLanguage\": \"Custom Accept-Language Header\",\n    \"alexaLogin\": \"Alexa login data\",\n    \"alexaServiceHost\": \"Alexa base url\",\n    \"auth\": \"Login Details for Amazon Account where your Alexa devices are registered\",\n    \"auth_info\": \"The credentials will be stored encrypted in your local ioBroker instance. You can also leave them empty, but then you need to fetch the Amazon cookie by your own or use the proxy from the adapter to login and get the cookie.\",\n    \"autoQueryActivityOnTrigger\": \"Automatically query activity on device interaction\",\n    \"autoQueryActivityOnTrigger_info\": \"Please only enable if you really need activity information automatically!\",\n    \"bespoken\": \"Bespoken Virtual Device\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"The Bespoken Virtual Devices can be used to send text commands to you Amazon Account and gives limited control over your Devices - when possible using spoken words.<br/><br/>You need to create a Bespoken Account and then a Virtual Device to get a Bespoken Token as described on https://read.Bespoken.io/end-to-end/setup/ . Then you enter the token below and you will see new objects for the adapter.<br/><br/>Please note: Bespoken creates a audio file out of your text and sends this to the Alexa-APIs. So the understanding quality of Alexa is as good as this speech generation, so it may be problematic with special words.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Custom Cookie-Amazon-URL\",\n    \"device_settings\": \"Device Settings\",\n    \"device_settings_info\": \"Settings for the devices that are fetched from Amazon\",\n    \"history\": \"History Settings\",\n    \"historyIgnoreEmptySummary\": \"Ignore History-Entries when no command was recognized\",\n    \"history_info\": \"Settings for History data that are available in the \\\"History\\\" states\",\n    \"includeAppDevices\": \"Query and add also App-Devices\",\n    \"interval\": \"Data update interval\",\n    \"interval_info\": \"Several data can be updated automatically. You define here in seconds how often this should happen. Using the state history.#trigger you can always update the data manually. If you set 0 as interval the automatic update is disabled. \",\n    \"overwrite\": \"Override special parameter for cookie determination and communication\",\n    \"overwrite_info\": \"By default the parameters are set to connect with a german Amazon account. If your account is located in an other Amazon country you need to enter the correct details here. Check in Adapter README for some details. Also in certain cases when the automatic cookie determination is failing it can help to play around here especially with the Accept-Language and user-agent settings.\",\n    \"password\": \"Password\",\n    \"proxy\": \"Proxy Settings\",\n    \"proxyListenBind\": \"Proxy Binding to certain IP\",\n    \"proxyOverrideIp\": \"External Container-IP (Docker)\",\n    \"proxyOwnIp\": \"Own IP or hostname to use in proxy link\",\n    \"proxyPort\": \"Proxy-Port\",\n    \"proxyPort_info\": \"(0 = random port)\",\n    \"proxy_info\": \"If the automatic determination of a cookie was not possible, the adapter opens a proxy to allow a manual login to amazon. The Logfile and Admin screen will tell you the URL to use then. Open this in your browser, manually login into Amazon, solve all potential Captchas or Two-Factor-Authentications and the cookie is catched automatically in the background if successful.\",\n    \"push_connection\": \"Push-Connection\",\n    \"push_connection_info\": \"With the direct push connection the changes from the Alexa devices are notified to the adapter and are used to update only needed data in near real time. If enabled some data update intervals below are not used. It is Urgently recommended to use the push connection to limit the data requested from the amazon cloud!\",\n    \"resetCookie\": \"Reset Amazon Cookies\",\n    \"s (0 = disabled)\": \"s (0 = disabled)\",\n    \"sync_settings\": \"Synchronization Settings\",\n    \"sync_settings_info\": \"Specify which data should be synced from the Amazon Cloud to the ioBroker instance. Please only enable the data you really need!\",\n    \"synchronizeLists\": \"Synchronize Lists (e.g. Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Synchronize Smart Home Devices\",\n    \"updateConfigurationInterval\": \"Device Configuration update interval\",\n    \"updateConfigurationInterval_info\": \"The device configuration can be updated automatically. The update interval defines how often this should happen. Do not set this too low because else Amazon might block you! The Minimum is 300s or set to 0 to disable the update.\",\n    \"updateHistoryInterval\": \"History update interval\",\n    \"updateHistoryInterval_info\": \"The History update interval defines how often the History data should be updated from the Amazon servers. The minimum is 60s or set to 0 to disable the update. Do not set this too low because else Amazon might block you! When the Push-Connection is established the update interval is not used because push information will trigger it when needed automatically.\",\n    \"updateSmartHomeDevicesInterval\": \"Smart Home devices update interval\",\n    \"updateSmartHomeDevicesInterval_info\": \"Please enable this interval only if you really need the values of the smart home devices! Only the smart home devices that have #includeInIntervalQuery activated in the objects and actively report their status to Amazon systems are updated to make sure to not create issues and costs on the skill developer side! Do not set this too low because else Amazon might block you! The Minimum is 900s or set to 0 to disable the update (can be triggered manually via #query states).\",\n    \"updateStateInterval\": \"Data update interval (player info …)\",\n    \"updateStateInterval_info\": \"The general device data update interval defines how often the data should be updated from the Amazon servers. The minimum is 300s or set to 0 to disable the update. Do not set this too low because else Amazon might block you! When the Push-Connection is established the update interval is set to 60 minutes when enabled.\",\n    \"usePushConnection\": \"Use push connection\",\n    \"userAgent\": \"Custom User-Agent\"\n}\n"
  },
  {
    "path": "admin/i18n/es/translations.json",
    "content": "{\n    \"Email\": \"Usuario/Correo electrónico\",\n    \"Password\": \"Contraseña\",\n    \"Please wait while loading Adapter-Status ...\": \"Por favor, espere mientras se carga el adaptador de estado ...\",\n    \"ProxyInfo\": \"Haga clic <a href=\\\"%s\\\" target=\\\"_blank\\\">aquí para iniciar sesión en su cuenta de Amazon una vez</a> para activar la conexión a sus dispositivos Alexa para esta instancia.\",\n    \"Settings\": \"Configuraciones\",\n    \"StatusInfo\": \"Las cookies de Alexa están actualizadas y actualizadas por última vez en %s \",\n    \"acceptLanguage\": \"Encabezado personalizado de idioma de aceptación\",\n    \"alexaLogin\": \"Datos de acceso de Alexa\",\n    \"alexaServiceHost\": \"URL base de Alexa\",\n    \"auth\": \"Detalles de inicio de sesión para Amazon Cuenta donde están registrados los dispositivos Echo\",\n    \"auth_info\": \"Las credenciales se almacenarán encriptadas en su instancia local de ioBroker. También puede dejarlos vacíos, pero luego debe buscar la cookie de Amazon por su cuenta o usar el proxy del adaptador para iniciar sesión y obtener la cookie.\",\n    \"autoQueryActivityOnTrigger\": \"Consultar automáticamente la actividad en la interacción del dispositivo\",\n    \"autoQueryActivityOnTrigger_info\": \"¡Habilítelo solo si realmente necesita información de actividad automáticamente!\",\n    \"bespoken\": \"Dispositivo virtual Bespoken\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"Los dispositivos virtuales Bespoken se pueden utilizar para enviar comandos de texto a su cuenta de Amazon y le dan un control limitado sobre sus dispositivos, cuando sea posible utilizando palabras habladas. <br/> <br/> Usted necesita crear una cuenta de Bespoken y luego un dispositivo virtual para obtener un token Bespoken como se describe en https://read.Bespoken.io/end-to-end/setup/. A continuación, ingrese el token a continuación y verá nuevos objetos para el adaptador. <br/> <br/> Tenga en cuenta: Bespoken crea un archivo de audio de su texto y lo envía a las API de Alexa. Entonces, la calidad de comprensión de Alexa es tan buena como esta generación de habla, por lo que puede ser problemático con palabras especiales.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Cookie personalizada-Amazon-URL\",\n    \"device_settings\": \"Configuración de dispositivo\",\n    \"device_settings_info\": \"Configuración para los dispositivos que se obtienen de Amazon\",\n    \"history\": \"Configuración del historial\",\n    \"historyIgnoreEmptySummary\": \"Ignorar entradas de historial donde no se reconoció ningún comando\",\n    \"history_info\": \"Configuración de los datos del historial que están disponibles en los estados \\\"History\\\"\",\n    \"includeAppDevices\": \"Consultar y agregar también App-Devices\",\n    \"interval\": \"Intervalos de actualización de datos\",\n    \"interval_info\": \"Varios datos se pueden actualizar automáticamente. Usted define aquí en segundos con qué frecuencia esto debería suceder. Con el disparador de historial de estado. #, Siempre puede actualizar los datos manualmente. Si configura 0 como intervalo, la actualización automática está deshabilitada.\",\n    \"overwrite\": \"Anula el parámetro especial para la determinación y comunicación de cookies\",\n    \"overwrite_info\": \"Por defecto, los parámetros están configurados para conectarse con una cuenta amazon alemana. Si está utilizando otros países amazónicos, debe establecer los datos correctos aquí. Verifique en el adaptador README para algunos detalles. También en algunos casos, la determinación automática de cookies está fallando, puede ayudar a jugar aquí especialmente con Accept-Language y User-Agent.\",\n    \"password\": \"contraseña\",\n    \"proxy\": \"Configuración de proxy\",\n    \"proxyListenBind\": \"Enlazado Proxy a cierta IP\",\n    \"proxyOverrideIp\": \"Contenedor externo-IP (Docker)\",\n    \"proxyOwnIp\": \"IP propia o nombre de host para usar en el enlace proxy\",\n    \"proxyPort\": \"Puerto proxy\",\n    \"proxyPort_info\": \"(0 = puerto aleatorio)\",\n    \"proxy_info\": \"Si la determinación automática de una cookie no puede, el adaptador abre un proxy para permitir un inicio de sesión manual en Amazon. El archivo de registro le dirá la URL que debe usar. Abra esto en su navegador, inicie sesión manualmente en Amazon, resuelva todas las posibles Captchas o Autenticaciones de dos factores y la cookie queda atrapada en segundo plano automáticamente si tiene éxito.\",\n    \"push_connection\": \"Push-Connection\",\n    \"push_connection_info\": \"Con la conexión de empuje directo los cambios de los dispositivos Alexa se notifican al adaptador y se utilizan para actualizar sólo los datos necesarios en tiempo real cercano. Si se activan algunos intervalos de actualización de datos a continuación no se utilizan. Se recomienda urgentemente utilizar la conexión push para limitar los datos solicitados desde la nube amazon!\",\n    \"resetCookie\": \"Restablecer las cookies de Amazon\",\n    \"s (0 = disabled)\": \"s (0 = deshabilitado)\",\n    \"sync_settings\": \"Configuración de sincronización\",\n    \"sync_settings_info\": \"Especifique qué datos deben ser sincronizados desde la nube amazónica a la instancia ioBroker. Por favor, sólo active los datos que realmente necesita!\",\n    \"synchronizeLists\": \"Sincronizar listas (por ejemplo, Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Sincronizar dispositivos domésticos inteligentes\",\n    \"updateConfigurationInterval\": \"Intervalo de actualización de la configuración del dispositivo\",\n    \"updateConfigurationInterval_info\": \"La configuración del dispositivo se puede actualizar automáticamente. El intervalo de actualización define con qué frecuencia debería ocurrir esto. No pongas esto demasiado bajo porque si no Amazon podría bloquearte! El Mínimo es de 300 o se establece a 0 para desactivar la actualización.\",\n    \"updateHistoryInterval\": \"Intervalo de actualización de historial\",\n    \"updateHistoryInterval_info\": \"El intervalo de actualización de la historia define con qué frecuencia los datos de la historia deben actualizarse desde los servidores de Amazon. El mínimo es de 60 o se establece a 0 para desactivar la actualización. No pongas esto demasiado bajo porque si no Amazon podría bloquearte! Cuando el Push-Connection se establece el intervalo de actualización no se utiliza porque la información de empuje lo activará cuando sea necesario automáticamente.\",\n    \"updateSmartHomeDevicesInterval\": \"Intervalo de actualización de dispositivos domésticos inteligentes\",\n    \"updateSmartHomeDevicesInterval_info\": \"Por favor, active este intervalo sólo si realmente necesita los valores de los dispositivos caseros inteligentes! Sólo los dispositivos inteligentes caseros que tienen #includeInIntervalQuery activados en los objetos y reportan activamente su estado en los sistemas de Amazon se actualizan para asegurarse de no crear problemas y costos en el lado desarrollador de habilidades! No pongas esto demasiado bajo porque si no Amazon podría bloquearte! El Mínimo es de 900 o se establece a 0 para desactivar la actualización (puede activarse manualmente a través de estados #query).\",\n    \"updateStateInterval\": \"Intervalo de actualización de datos (Player-Info ...)\",\n    \"updateStateInterval_info\": \"El intervalo de actualización de datos del dispositivo general define con qué frecuencia los datos deben actualizarse desde los servidores de Amazon. El mínimo es de 300 o se establece a 0 para desactivar la actualización. No pongas esto demasiado bajo porque si no Amazon podría bloquearte! Cuando el Push-Connection se establece el intervalo de actualización se establece a 60 minutos cuando está habilitado.\",\n    \"usePushConnection\": \"Use la conexión push\",\n    \"userAgent\": \"User-Agent personalizado\"\n}\n"
  },
  {
    "path": "admin/i18n/fr/translations.json",
    "content": "{\n    \"Email\": \"Utilisateur/Email\",\n    \"Password\": \"Mot de passe\",\n    \"Please wait while loading Adapter-Status ...\": \"Veuillez patienter pendant le chargement de Adapter-Status ...\",\n    \"ProxyInfo\": \"Veuillez cliquer <a href=\\\"%s\\\" target=\\\"_blank\\\">ici pour vous connecter une fois à votre compte Amazon</a> afin d'activer la connexion à vos appareils Alexa pour cette instance.\",\n    \"Settings\": \"Paramètres\",\n    \"StatusInfo\": \"Les cookies Alexa sont à jour et mis à jour pour la dernière fois le %s \",\n    \"acceptLanguage\": \"En-tête personnalisé Accept-Language\",\n    \"alexaLogin\": \"Données de connexion Alexa\",\n    \"alexaServiceHost\": \"Alexa base de l'Alexa\",\n    \"auth\": \"Détails de connexion pour le compte Amazon où les appareils Echo sont enregistrés\",\n    \"auth_info\": \"Les informations d'identification seront stockées cryptées dans votre instance ioBroker locale. Vous pouvez également les laisser vides, mais vous devez ensuite récupérer le cookie Amazon par vous-même ou utiliser le proxy de l'adaptateur pour vous connecter et obtenir le cookie.\",\n    \"autoQueryActivityOnTrigger\": \"Interroger automatiquement l'activité sur l'interaction de l'appareil\",\n    \"autoQueryActivityOnTrigger_info\": \"Veuillez l'activer uniquement si vous avez vraiment besoin automatiquement d'informations sur votre activité !\",\n    \"bespoken\": \"Appareil virtuel Bespoken\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"Les périphériques virtuels Bespoken peuvent être utilisés pour envoyer des commandes de texte à votre compte Amazon et donner un contrôle limité sur vos périphériques - si possible en utilisant des mots prononcés. <br/> <br/> Vous devez créer un compte Bespoken puis un périphérique virtuel pour obtenir un jeton Bespoken comme décrit sur https://read.Bespoken.io/end-to-end/setup/. Ensuite, vous entrez le jeton ci-dessous et vous verrez de nouveaux objets pour l'adaptateur. <br/> <br/> Remarque: Bespoken crée un fichier audio à partir de votre texte et l'envoie aux API Alexa. Donc, la qualité de la compréhension d’Alex est aussi bonne que cette génération de discours, donc il peut être problématique avec des mots spéciaux.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Cookie personnalisé-Amazon-URL\",\n    \"device_settings\": \"Réglages de l'appareil\",\n    \"device_settings_info\": \"Paramètres des appareils récupérés sur Amazon\",\n    \"history\": \"Paramètres d'historique\",\n    \"historyIgnoreEmptySummary\": \"Ignorer les entrées d'historique où aucune commande n'a été reconnue\",\n    \"history_info\": \"Paramètres des données d'historique disponibles dans les états \\\"History\\\"\",\n    \"includeAppDevices\": \"Interroger et ajouter également des appareils d'application\",\n    \"interval\": \"Intervalles de mise à jour\",\n    \"interval_info\": \"Plusieurs données peuvent être automatiquement mises à jour. Vous définissez ici en secondes combien de fois cela devrait arriver. En utilisant l'historique d'état. # Trigger, vous pouvez toujours mettre à jour les données manuellement. Si vous définissez 0 comme intervalle, la mise à jour automatique est désactivée.\",\n    \"overwrite\": \"Remplacer le paramètre spécial pour la détermination des cookies et la communication\",\n    \"overwrite_info\": \"Par défaut, les paramètres sont définis pour se connecter avec un compte amazon allemand. Si vous utilisez d'autres pays amazon, vous devez définir les données correctes ici. Vérifiez l'adaptateur README pour plus de détails. Dans certains cas, lorsque la détermination automatique des cookies échoue, cela peut aider à jouer ici, en particulier avec Accept-Language et user-agent.\",\n    \"password\": \"mot de passe\",\n    \"proxy\": \"Paramètres du proxy\",\n    \"proxyListenBind\": \"Liaison proxy à certaines adresses IP\",\n    \"proxyOverrideIp\": \"IP de conteneur externe (Docker)\",\n    \"proxyOwnIp\": \"Propre IP ou nom d'hôte à utiliser dans un lien de proxy\",\n    \"proxyPort\": \"Port proxy\",\n    \"proxyPort_info\": \"(0 = port aléatoire)\",\n    \"proxy_info\": \"Si la détermination automatique d'un cookie n'est pas possible, l'adaptateur ouvre un proxy pour autoriser une connexion manuelle à amazon. Le fichier journal vous indiquera l'URL à utiliser ensuite. Ouvrez-le dans votre navigateur, connectez-vous manuellement à Amazon, résolvez tous les captcha potentiels ou les authentifications à deux facteurs et le cookie est automatiquement récupéré en arrière-plan en cas de succès.\",\n    \"push_connection\": \"Push-Connection\",\n    \"push_connection_info\": \"Avec la connexion push directe les changements des appareils Alexa sont notifiés à l'adaptateur et sont utilisés pour mettre à jour seulement les données nécessaires en temps réel. Si activé certains intervalles de mise à jour de données ci-dessous ne sont pas utilisés. Il est urgent d'utiliser la connexion push pour limiter les données demandées sur le nuage amazon!\",\n    \"resetCookie\": \"Réinitialiser les cookies Amazon\",\n    \"s (0 = disabled)\": \"s (0 = désactivé)\",\n    \"sync_settings\": \"Paramètres de synchronisation\",\n    \"sync_settings_info\": \"Préciser quelles données doivent être synchronisées depuis Amazon Cloud jusqu'à l'instance ioBroker. Veuillez uniquement activer les données dont vous avez vraiment besoin !\",\n    \"synchronizeLists\": \"Synchroniser les listes (par exemple Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Synchroniser les appareils domestiques intelligents\",\n    \"updateConfigurationInterval\": \"Intervalle de mise à jour de la configuration de l'appareil\",\n    \"updateConfigurationInterval_info\": \"La configuration de l'appareil peut être mise à jour automatiquement. L'intervalle de mise à jour définit combien de fois cela devrait se produire. Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Le minimum est de 300 ou de 0 pour désactiver la mise à jour.\",\n    \"updateHistoryInterval\": \"Intervalle de mise à\",\n    \"updateHistoryInterval_info\": \"L'intervalle de mise à jour d'Histoire définit la fréquence de mise à jour des données d'Histoire des serveurs Amazon. Le minimum est de 60 ou de 0 pour désactiver la mise à jour. Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Lorsque le Push-Connection est établi, l'intervalle de mise à jour n'est pas utilisé parce que l'information de poussée le déclenchera au besoin automatiquement.\",\n    \"updateSmartHomeDevicesInterval\": \"Intervalle de mise à jour des appareils Smart Home\",\n    \"updateSmartHomeDevicesInterval_info\": \"Veuillez activer cet intervalle seulement si vous avez vraiment besoin des valeurs des appareils à domicile intelligents! Seuls les appareils smart home qui ont #includeInIntervalQuery activés dans les objets et rapportent activement leur statut aux systèmes Amazon sont mis à jour pour s'assurer de ne pas créer de problèmes et de coûts sur le côté développeur de compétences! Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Le minimum est de 900 ou défini à 0 pour désactiver la mise à jour (peut être déclenché manuellement via les états #query).\",\n    \"updateStateInterval\": \"Intervalle de mise à jour des données (Player-Info ...)\",\n    \"updateStateInterval_info\": \"L'intervalle général de mise à jour des données de périphérique définit la fréquence de mise à jour des données des serveurs Amazon. Le minimum est de 300 ou de 0 pour désactiver la mise à jour. Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Lorsque le Push-Connection est établi, l'intervalle de mise à jour est fixé à 60 minutes lorsque activé.\",\n    \"usePushConnection\": \"Utiliser la connexion push\",\n    \"userAgent\": \"User-Agent personnalisé\"\n}\n"
  },
  {
    "path": "admin/i18n/hu/translations.json",
    "content": "{\n    \"Password\": \"Jelszó\",\n    \"Settings\": \"Beállítások\",\n    \"StatusInfo\": \"Az Alexa-sütik naprakészek, utolsó frissítés időpontja: %s\",\n    \"alexaLogin\": \"Alexa belépési adatok\",\n    \"alexaServiceHost\": \"Alexa alap-url\",\n    \"acceptLanguage\": \"Egyedi Accept-Language fejléc\",\n    \"auth\": \"Bejelentkezési adatok az Amazon Account-hoz, ahol az Alexa eszköz regisztrálva lett\",\n    \"auth_info\": \"Az azonosító adatok az ioBroker egységén titkosítva tárolódnak. Üresen hagyhatóak, de az Amazon-sütiket egyedileg kell megszerezni, vagy proxy-t kell használni a bejelentkezéshez és a sütik megszerzéséhez.\",\n    \"Email\": \"E-Mail\",\n    \"cookie\": \"Süti\",\n    \"history\": \"Beállítási napló\",\n    \"Please wait while loading Adapter-Status ...\": \"Kérem várjon, míg az Adapter-állapota betöltődik…\",\n    \"ProxyInfo\": \"Kattintson a <a href=\\\"%s\\\" target=\\\"_blank\\\"> címre az Amazon Account belépéshez </a> és az Amazon eszközének aktíválásához.\",\n    \"cookieLoginUrl\": \"Egyedi Amazon-Süti-URL\",\n    \"interval\": \"Adatfrissítési periódus\",\n    \"proxyPort_info\": \"(0 = véletlen választott port)\",\n    \"sync_settings_info\": \"Jelölje meg mely adatokat szinkronizálja az Amazon Felhőből az ioBrokerbe. Kérem, csak a legfontosabb adatokat jelölje ki!\",\n    \"password\": \"Jelszó\",\n    \"proxy\": \"Proxy beállítások\",\n    \"proxyPort\": \"Proxy-port\",\n    \"resetCookie\": \"Amazon sütik törlése\",\n    \"s (0 = disabled)\": \"s (0 = kikapcsolva)\",\n    \"updateConfigurationInterval_info\": \"Az eszközbeálltások automatikusan frissültek. Az frissítés intervallumának gyakorisága beállítható. Ne állítsa nagyon alacsony értékre, mert az Amazon blokkolhatja! A minimum érték 300 másodperc vagy a 0 beállításával kikapcsolható a frissítés.\",\n    \"updateHistoryInterval\": \"Az előzmények frissítésének időtartama\",\n    \"proxyListenBind\": \"Proxy-kapcsolat bizonyos IP-címekhez\",\n    \"proxyOwnIp\": \"Saját IP cím vagy hosztnév a proxy kapcsolatban\",\n    \"proxyOverrideIp\": \"Külső konténer-IP címe (Docker)\",\n    \"historyIgnoreEmptySummary\": \"Korábbi bejegyzések kihagyása, ha az utasítás nem ismerhető fel\",\n    \"history_info\": \"Beállítások a korábbi adatokhoz, amik a \\\"Korábbi\\\" állapot cimkével elérhetők\",\n    \"updateStateInterval\": \"Adatfrissítési intervallum (lejátszási információ ...)\"\n}\n"
  },
  {
    "path": "admin/i18n/it/translations.json",
    "content": "{\n    \"Email\": \"Utente/E-mail\",\n    \"Password\": \"Parola d'ordine\",\n    \"Please wait while loading Adapter-Status ...\": \"Si prega di attendere durante il caricamento di Adapter-Status ...\",\n    \"ProxyInfo\": \"Fai clic <a href=\\\"%s\\\" target=\\\"_blank\\\">qui per accedere al tuo account Amazon una volta</a> per attivare la connessione ai tuoi dispositivi Alexa per questa istanza.\",\n    \"Settings\": \"impostazioni\",\n    \"StatusInfo\": \"Alexa-Cookies sono aggiornati e aggiornati per l'ultima volta su %s \",\n    \"acceptLanguage\": \"Intestazione di lingua accettabile personalizzata\",\n    \"alexaLogin\": \"Dati di login di Alexa\",\n    \"alexaServiceHost\": \"Alexa base url\",\n    \"auth\": \"Dettagli di accesso per l'account Amazon in cui sono registrati i dispositivi Echo\",\n    \"auth_info\": \"Le credenziali verranno archiviate crittografate nell'istanza ioBroker locale. Puoi anche lasciarli vuoti, ma poi devi recuperare il cookie Amazon da solo o utilizzare il proxy dall'adattatore per accedere e ottenere il cookie.\",\n    \"autoQueryActivityOnTrigger\": \"Interroga automaticamente l'attività sull'interazione del dispositivo\",\n    \"autoQueryActivityOnTrigger_info\": \"Abilitalo solo se hai veramente bisogno delle informazioni sull'attività automaticamente!\",\n    \"bespoken\": \"Dispositivo virtuale Bespoken\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"I dispositivi virtuali Bespoken possono essere utilizzati per inviare comandi di testo al tuo account Amazon e forniscono un controllo limitato sui tuoi dispositivi - quando possibile usando parole parlate. <br/> <br/> Devi creare un account Bespoken e poi un dispositivo virtuale per ottenere un token Bespoken come descritto su https://read.Bespoken.io/end-to-end/setup/. Quindi inserisci il token qui sotto e vedrai nuovi oggetti per l'adattatore. <br/> <br/> Nota: Bespoken crea un file audio dal tuo testo e lo invia alle API Alexa. Quindi la qualità della comprensione di Alexa è buona quanto questa generazione di parlato, quindi potrebbe essere problematica con parole speciali.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Cookie personalizzato-Amazon-URL\",\n    \"device_settings\": \"Impostazioni del dispositivo\",\n    \"device_settings_info\": \"Impostazioni per i dispositivi recuperati da Amazon\",\n    \"history\": \"Impostazioni cronologia\",\n    \"historyIgnoreEmptySummary\": \"Ignora voci di cronologia in cui non è stato riconosciuto alcun comando\",\n    \"history_info\": \"Impostazioni per i dati della cronologia disponibili negli stati \\\"History\\\"\",\n    \"includeAppDevices\": \"Interroga e aggiungi anche App-Devices\",\n    \"interval\": \"Intervalli di aggiornamento dei dati\",\n    \"interval_info\": \"Diversi dati possono essere aggiornati automaticamente. Definisci qui in secondi quanto spesso ciò dovrebbe accadere. Utilizzando la cronologia dello stato. # Trigger puoi sempre aggiornare i dati manualmente. Se si imposta 0 come intervallo, l'aggiornamento automatico è disabilitato.\",\n    \"overwrite\": \"Sostituisci i parametri speciali per la determinazione e la comunicazione dei cookie\",\n    \"overwrite_info\": \"Di default i parametri sono impostati per connettersi con un account tedesco di Amazon. Se stai utilizzando altri paesi di Amazon, devi impostare qui i dati corretti. Controllare in Adapter README per alcuni dettagli. Inoltre, in alcuni casi in cui la determinazione automatica dei cookie non funziona, può aiutare a giocare qui, specialmente con Accept-Language e user-agent.\",\n    \"password\": \"parola d'ordine\",\n    \"proxy\": \"Impostazioni proxy\",\n    \"proxyListenBind\": \"Collegamento a determinati indirizzi IP\",\n    \"proxyOverrideIp\": \"External Container-IP (Docker)\",\n    \"proxyOwnIp\": \"Proprio IP o nome host da utilizzare nel collegamento proxy\",\n    \"proxyPort\": \"Porta proxy\",\n    \"proxyPort_info\": \"(0 = porta casuale)\",\n    \"proxy_info\": \"Se la determinazione automatica di un cookie non è possibile, l'adattatore apre un proxy per consentire l'accesso manuale ad Amazon. Il file di registro ti indicherà l'URL da utilizzare in quel momento. Apri questo nel tuo browser, accedi manualmente ad Amazon, risolvi tutti i potenziali Captcha o Autenticazione a due fattori e il cookie viene automaticamente catturato in background in caso di successo.\",\n    \"push_connection\": \"Push-Connection\",\n    \"push_connection_info\": \"Con la connessione a spinta diretta le modifiche dei dispositivi Alexa vengono notificate all'adattatore e vengono utilizzate per aggiornare solo i dati necessari in tempo reale. Se abilitato alcuni intervalli di aggiornamento dati di seguito non vengono utilizzati. È urgentemente consigliato utilizzare la connessione push per limitare i dati richiesti dal cloud amazon!\",\n    \"resetCookie\": \"Reimposta i cookie Amazon\",\n    \"s (0 = disabled)\": \"s (0 = disabilitato)\",\n    \"sync_settings\": \"Impostazioni di sincronizzazione\",\n    \"sync_settings_info\": \"Specificare quali dati dovrebbero essere sincronizzati da Amazon Cloud all'istanza ioBroker. Si prega di abilitare solo i dati di cui hai veramente bisogno!\",\n    \"synchronizeLists\": \"Sincronizza elenchi (ad es. Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Sincronizza i dispositivi Smart Home\",\n    \"updateConfigurationInterval\": \"Intervallo di aggiornamento della configurazione del dispositivo\",\n    \"updateConfigurationInterval_info\": \"La configurazione del dispositivo può essere aggiornata automaticamente. L'intervallo di aggiornamento definisce quanto spesso questo dovrebbe accadere. Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Il Minimo è 300 o impostato a 0 per disabilitare l'aggiornamento.\",\n    \"updateHistoryInterval\": \"Intervallo di aggiornamento della cronologia\",\n    \"updateHistoryInterval_info\": \"L'intervallo di aggiornamento di Storia definisce quanto spesso i dati di Storia dovrebbero essere aggiornati dai server Amazon. Il minimo è di 300 o impostato a 0 per disabilitare l'aggiornamento. Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Quando la Push-Connection viene stabilita l'intervallo di aggiornamento non viene utilizzato perché le informazioni push lo innescheranno quando necessario automaticamente.\",\n    \"updateSmartHomeDevicesInterval\": \"Intervallo di aggiornamento dei dispositivi Smart Home\",\n    \"updateSmartHomeDevicesInterval_info\": \"Si prega di attivare questo intervallo solo se hai davvero bisogno dei valori dei dispositivi smart home! Solo i dispositivi smart home che hanno #includeInIntervalQuery attivato negli oggetti e riportano attivamente il loro stato ai sistemi Amazon sono aggiornati per assicurarsi di non creare problemi e costi sul lato sviluppatore di abilità! Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Il Minimo è 900 o impostato a 0 per disabilitare l'aggiornamento (può essere attivato manualmente tramite #query stati).\",\n    \"updateStateInterval\": \"Intervallo di aggiornamento dei dati (Player-Info ...)\",\n    \"updateStateInterval_info\": \"L'intervallo di aggiornamento dei dati del dispositivo generale definisce quanto spesso i dati dovrebbero essere aggiornati dai server Amazon. Il minimo è di 300 o impostato a 0 per disabilitare l'aggiornamento. Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Quando viene stabilita la Push-Connection, l'intervallo di aggiornamento viene impostato a 60 minuti quando abilitato.\",\n    \"usePushConnection\": \"Utilizzare la connessione push\",\n    \"userAgent\": \"User-Agent personalizzato\"\n}\n"
  },
  {
    "path": "admin/i18n/nl/translations.json",
    "content": "{\n    \"Email\": \"User/E-mail\",\n    \"Password\": \"Wachtwoord\",\n    \"Please wait while loading Adapter-Status ...\": \"Een ogenblik geduld tijdens het laden van de adapter-status ...\",\n    \"ProxyInfo\": \"Klik <a href=\\\"%s\\\" target=\\\"_blank\\\">hier om u eenmaal aan te melden bij uw Amazon-account</a> om de verbinding met uw Alexa-apparaten voor deze instantie te activeren.\",\n    \"Settings\": \"instellingen\",\n    \"StatusInfo\": \"Alexa-cookies zijn up-to-date en voor het laatst bijgewerkt op %s \",\n    \"acceptLanguage\": \"Aangepaste acceptatie-taalkop\",\n    \"alexaLogin\": \"Alexa-inloggegevens\",\n    \"alexaServiceHost\": \"Alexa-basis-URL\",\n    \"auth\": \"Aanmeldgegevens voor Amazon-account waarop Echo-apparaten zijn geregistreerd\",\n    \"auth_info\": \"De inloggegevens worden gecodeerd opgeslagen in uw lokale instantie ioBroker. Je kunt ze ook leeg laten, maar dan moet je de Amazon-cookie zelf ophalen of de proxy van de adapter gebruiken om in te loggen en de cookie te krijgen.\",\n    \"autoQueryActivityOnTrigger\": \"Automatisch opvragen van activiteit bij apparaatinteractie\",\n    \"autoQueryActivityOnTrigger_info\": \"Schakel dit alleen in als u activiteitsinformatie echt automatisch nodig heeft!\",\n    \"bespoken\": \"Bespoken virtueel apparaat\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"De virtuele BESACT-apparaten kunnen worden gebruikt om tekstopdrachten naar uw Amazon-account te verzenden en geeft beperkte controle over uw apparaten - indien mogelijk met behulp van gesproken woorden. <br/> <br/> U moet eerst een Bespoken-account en vervolgens een virtueel apparaat maken een Bespoken-token krijgen zoals beschreven op https://read.Bespoken.io/end-toend/setup/. Vervolgens voert u de token hieronder in en ziet u nieuwe objecten voor de adapter. <br/> <br/> Let op: Bespoken maakt een audiobestand van uw tekst en stuurt dit naar de Alexa-API's. Dus de kenniskwaliteit van Alexa is net zo goed als deze spraakgeneratie, dus het kan problematisch zijn met speciale woorden.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Aangepaste Cookie-Amazon-URL\",\n    \"device_settings\": \"Apparaat instellingen\",\n    \"device_settings_info\": \"Instellingen voor de apparaten die van Amazon worden opgehaald\",\n    \"history\": \"Geschiedenis instellingen\",\n    \"historyIgnoreEmptySummary\": \"Negeer geschiedenis-items waar geen commando werd herkend\",\n    \"history_info\": \"Instellingen voor geschiedenisgegevens die beschikbaar zijn in de status \\\"History\\\"\",\n    \"includeAppDevices\": \"Vraag en voeg ook app-apparaten toe\",\n    \"interval\": \"Gegevens update-intervallen\",\n    \"interval_info\": \"Verschillende gegevens kunnen automatisch worden bijgewerkt. Je definieert hier in seconden hoe vaak dit moet gebeuren. Door de # trigger te gebruiken, kunt u de gegevens altijd handmatig bijwerken. Als u 0 instelt als interval, is de automatische update uitgeschakeld.\",\n    \"overwrite\": \"Speciale parameter voor cookie-bepaling en communicatie overschrijven\",\n    \"overwrite_info\": \"Standaard zijn de parameters ingesteld om verbinding te maken met een Duits Amazon-account. Als u andere Amazon-landen gebruikt, moet u hier de juiste gegevens instellen. Raadpleeg de README van de adapter voor meer informatie. Ook in bepaalde gevallen waar de automatische cookie-bepaling faalt, kan het helpen om hier te spelen, vooral met Accept-Language en user-agent.\",\n    \"password\": \"wachtwoord\",\n    \"proxy\": \"Proxy instellingen\",\n    \"proxyListenBind\": \"Proxy-binding aan bepaalde IP\",\n    \"proxyOverrideIp\": \"External Container-IP (Docker)\",\n    \"proxyOwnIp\": \"Eigen IP of hostnaam om te gebruiken in proxy-koppeling\",\n    \"proxyPort\": \"Proxy-Port\",\n    \"proxyPort_info\": \"(0 = willekeurige poort)\",\n    \"proxy_info\": \"Als de automatische vaststelling van een cookie niet mogelijk is, opent de adapter een proxy om handmatig inloggen bij Amazon mogelijk te maken. Het logbestand geeft u de URL die u vervolgens moet gebruiken. Open dit in uw browser, log handmatig in bij Amazon, los alle mogelijke Captchas of Two-Factor-Authenticaties op en de cookie wordt automatisch in de achtergrond gevangen als het succesvol is.\",\n    \"push_connection\": \"Push-aansluiting\",\n    \"push_connection_info\": \"Met de directe verbinding die de veranderingen van de Alexa apparatuur zijn geïnformeerd aan de adapter en worden gebruikt om gegevens te updaten in de echte tijd. Als een aantal data updates beneden niet gebruikt worden. Het is Urgently aanbevolen de persverbinding te gebruiken om de gegevens te beperken van de Amazonewolk!\",\n    \"resetCookie\": \"Reset Amazon-cookies\",\n    \"s (0 = disabled)\": \"s (0 = uitgeschakeld)\",\n    \"sync_settings\": \"Synchronisatie-instellingen\",\n    \"sync_settings_info\": \"Vermeld welke gegevens moeten worden gesynchroniseerd van de Amazone Cloud naar het ioBroker instance. Bevestig de gegevens die je echt nodig hebt!\",\n    \"synchronizeLists\": \"Lijsten synchroniseren (bijv. Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Synchroniseer Smart Home-apparaten\",\n    \"updateConfigurationInterval\": \"Update-interval apparaatconfiguratie\",\n    \"updateConfigurationInterval_info\": \"Het apparaat kan automatisch bijgewerkt worden. De update interval definieert hoe vaak dit zou moeten gebeuren. Zet dit niet te laag, want anders blokkeert Amazone je. Het Minimum is 300 of klaar om de update uit te schakelen.\",\n    \"updateHistoryInterval\": \"Historisch update-interval\",\n    \"updateHistoryInterval_info\": \"De geschiedenis interval definieert hoe vaak de geschiedenisgegevens moeten worden geüpdateerd van de Amazone servers. Het minimum is 60 of klaar om de update uit te schakelen. Zet dit niet te laag, want anders blokkeert Amazone je. Als de Push-Connectie is vastgesteld dat de update niet gebruikt wordt, omdat informatie het zal activeren als het automatisch nodig is.\",\n    \"updateSmartHomeDevicesInterval\": \"Update-interval voor Smart Home-apparaten\",\n    \"updateSmartHomeDevicesInterval_info\": \"Beveel deze interval alleen als je echt de waarden van de slimme thuis apparaten nodig hebt! Alleen de slimme thuis apparaten die includeïnfiltervalQuery geactiveerd in de objecten en actief hun status aan Amazon systemen rapporteren om geen problemen te creëren en kosten aan de ontwikkelaar. Zet dit niet te laag, want anders blokkeert Amazon je. Het Minimum is 900 of set to 0 om de update te ontmantelen.\",\n    \"updateStateInterval\": \"Gegevensbijwerkingsinterval (Player-Info ...)\",\n    \"updateStateInterval_info\": \"De algemene apparatuur update bepaalt hoe vaak de gegevens moeten worden geüpload van de Amazone servers. Het minimum is 300 of klaar om de update uit te schakelen. Zet dit niet te laag, want anders blokkeert Amazone je. Als de Push-Connectie is vastgesteld dat de update interval is gestart tot 60 minuten wanneer het in staat is.\",\n    \"usePushConnection\": \"Gebruik een push-verbinding\",\n    \"userAgent\": \"Aangepaste User-Agent\"\n}\n"
  },
  {
    "path": "admin/i18n/pl/translations.json",
    "content": "{\n    \"Email\": \"Użytkownik/Email\",\n    \"Password\": \"Hasło\",\n    \"Please wait while loading Adapter-Status ...\": \"Poczekaj na załadowanie statusu adaptera ...\",\n    \"ProxyInfo\": \"Kliknij <a href=\\\"%s\\\" target=\\\"_blank\\\">tutaj, aby zalogować się raz na swoje konto Amazon</a>, aby aktywować połączenie z urządzeniami Alexa dla tej instancji.\",\n    \"Settings\": \"Ustawienia\",\n    \"StatusInfo\": \"Alexa-Cookies są aktualne i ostatnio aktualizowane w %s \",\n    \"acceptLanguage\": \"Niestandardowy nagłówek Accept-Language\",\n    \"alexaLogin\": \"Dane logowania Alexa\",\n    \"alexaServiceHost\": \"Adres bazowy Alexa\",\n    \"auth\": \"Dane logowania do konta Amazon, w którym zarejestrowane są urządzenia Echo\",\n    \"auth_info\": \"Dane uwierzytelniające będą przechowywane w postaci zaszyfrowanej w lokalnej instancji ioBroker. Możesz również pozostawić je puste, ale musisz pobrać plik cookie Amazon samodzielnie lub użyć proxy z adaptera, aby się zalogować i uzyskać plik cookie.\",\n    \"autoQueryActivityOnTrigger\": \"Automatycznie sprawdzaj aktywność związaną z interakcją urządzenia\",\n    \"autoQueryActivityOnTrigger_info\": \"Włącz tylko wtedy, gdy naprawdę potrzebujesz automatycznie informacji o aktywności!\",\n    \"bespoken\": \"Wirtualne urządzenie Bespoken\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"Urządzenia wirtualne Bespoken mogą być używane do wysyłania poleceń tekstowych do konta Amazon i dają ograniczoną kontrolę nad urządzeniami - w miarę możliwości za pomocą wypowiadanych słów. <br/> <br/> Musisz utworzyć konto Bespoken, a następnie urządzenie wirtualne do zdobądź token Bespoken zgodnie z opisem na https://read.Bespoken.io/end-to-end/setup/. Następnie wpisz poniższy token, a zobaczysz nowe obiekty dla adaptera. <br/> <br/> Uwaga: Bespoken tworzy plik audio poza tekstem i wysyła go do Alexa-API. Zatem jakość rozumienia Alexy jest tak dobra, jak ta generacja mowy, więc może być problematyczne w przypadku specjalnych słów.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Niestandardowy plik cookie-Amazon-URL\",\n    \"device_settings\": \"Ustawienia urządzenia\",\n    \"device_settings_info\": \"Ustawienia urządzeń pobieranych z Amazon\",\n    \"history\": \"Ustawienia historii\",\n    \"historyIgnoreEmptySummary\": \"Ignoruj wpisy historii, w których nie rozpoznano żadnego polecenia\",\n    \"history_info\": \"Ustawienia danych historii, które są dostępne w stanach „History”\",\n    \"includeAppDevices\": \"Zapytanie i dodawanie również App-Devices\",\n    \"interval\": \"Interwały aktualizacji danych\",\n    \"interval_info\": \"Kilka danych może być automatycznie aktualizowanych. Tutaj określasz w sekundach, jak często powinno to mieć miejsce. Korzystając z historii stanów. # Trigger zawsze możesz zaktualizować dane ręcznie. Jeśli ustawisz 0 jako interwał, aktualizacja automatyczna zostanie wyłączona.\",\n    \"overwrite\": \"Zastąp specjalny parametr do określania i komunikacji plików cookie\",\n    \"overwrite_info\": \"Domyślnie parametry są ustawione na połączenie z niemieckim kontem Amazon. Jeśli korzystasz z innych krajów amazon, musisz ustawić poprawne dane tutaj. Sprawdź w pliku README na temat niektórych szczegółów. Również w niektórych przypadkach, gdy automatyczne określanie ciasteczek zawodzi, może pomóc w zabawie, szczególnie w Accept-Language i user-agent.\",\n    \"password\": \"hasło\",\n    \"proxy\": \"Ustawienia proxy\",\n    \"proxyListenBind\": \"Wiązanie proxy do określonego adresu IP\",\n    \"proxyOverrideIp\": \"Zewnętrzny kontener-IP (Docker)\",\n    \"proxyOwnIp\": \"Własny adres IP lub nazwa hosta do użycia w łączu proxy\",\n    \"proxyPort\": \"Port proxy\",\n    \"proxyPort_info\": \"(0 = losowy port)\",\n    \"proxy_info\": \"Jeśli automatyczne określenie pliku cookie nie jest możliwe, adapter otwiera proxy, aby umożliwić ręczne logowanie do Amazon. Logfile wskaże ci adres URL, którego będziesz używał. Otwórz to w przeglądarce, ręcznie zaloguj się do Amazon, rozwiąż wszystkie potencjalne Captchas lub uwierzytelnienia dwuetapowe, a ciasteczko zostanie automatycznie przechwycone w tle, jeśli się powiedzie.\",\n    \"push_connection\": \"Push-Connection\",\n    \"push_connection_info\": \"Dzięki bezpośredniemu połączeniu zmian w urządzeniach Aleksy są powiadomione do adaptera i są wykorzystywane do aktualizacji potrzebnych danych w czasie rzeczywistym. Jeśli uaktualnione dane nie są używane. Zaleca się użycie połączenia naciskowego w celu ograniczenia danych żądanych przez niebieską chmurę!\",\n    \"resetCookie\": \"Zresetuj ciasteczka Amazon\",\n    \"s (0 = disabled)\": \"s (0 = wyłączone)\",\n    \"sync_settings\": \"Ustawienia synchronizacji\",\n    \"sync_settings_info\": \"Wskazuje się, że dane powinny być syndykowane z Amazon Cloud na przykład ioBrokera. Pozwoliło na to tylko dane, które potrzebują!\",\n    \"synchronizeLists\": \"Synchronizuj listy (np. Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Synchronizuj inteligentne urządzenia domowe\",\n    \"updateConfigurationInterval\": \"Interwał aktualizacji konfiguracji urządzenia\",\n    \"updateConfigurationInterval_info\": \"Konfiguracja urządzenia może być automatycznie aktualizowana. Przedział aktualizacji definiuje sposób, w jaki często się to dzieje. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Minimum jest 300 lub ustawiony do 0, aby złagodzić aktualizację.\",\n    \"updateHistoryInterval\": \"Interwał aktualizacji historii\",\n    \"updateHistoryInterval_info\": \"Przedział historii określa, jak często dane historyczne powinny być aktualizowane z serwerów Amazon. Minimalna liczba wynosi 60 lub jest ustawiona do 0, aby złagodzić aktualizację. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Kiedy Push-Connection jest ugruntowany przedział uaktualnienia nie jest używany, ponieważ informacje naciśnięte będą uruchamiane automatycznie.\",\n    \"updateSmartHomeDevicesInterval\": \"Interwał aktualizacji urządzeń Smart Home\",\n    \"updateSmartHomeDevicesInterval_info\": \"Pozwólcie to tylko wtedy, gdy potrzebujesz wartości inteligentnych urządzeń. Jedynie inteligentne urządzenia domowe, które #include inIntervalQuery aktywowane w obiektach i aktywnie informują o ich statusie w systemach Amazon, aby nie uaktualnić problemów i kosztów po stronie wytwórcy. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Minimum jest 900 lub ustawiony do 0, aby zdyskwalifikować aktualizację (może być uruchamiany ręcznie przez stany numeryczne).\",\n    \"updateStateInterval\": \"Interwał aktualizacji danych (Player-Info ...)\",\n    \"updateStateInterval_info\": \"Ogólna aktualizacja danych definiuje, jak często dane powinny być aktualizowane z serwerów Amazon. Minimalna liczba wynosi 300 lub jest ustawiona do 0, aby złagodzić aktualizację. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Po ustanowieniu przez Push-Connection przedziału aktualizacji następuje do 60 minut.\",\n    \"usePushConnection\": \"Użyj połączenia push\",\n    \"userAgent\": \"Niestandardowy User-Agent\"\n}\n"
  },
  {
    "path": "admin/i18n/pt/translations.json",
    "content": "{\n    \"Email\": \"E-mail\",\n    \"Password\": \"Senha\",\n    \"Please wait while loading Adapter-Status ...\": \"Por favor, aguarde enquanto carrega o estado do adaptador …\",\n    \"ProxyInfo\": \"Clique <a href=\\\"%s\\\" target=\\\"_blank\\\">aqui para fazer login em sua conta Amazon</a> para ativar a conexão com seus dispositivos Alexa para esta instância.\",\n    \"Settings\": \"Configurações\",\n    \"StatusInfo\": \"Os cookies da Alexa são atuais e foram atualizados pela última vez em %s\",\n    \"acceptLanguage\": \"Próprio Accept-Language-Header\",\n    \"alexaLogin\": \"Dados de login para Alexa\",\n    \"alexaServiceHost\": \"URL básico da Alexa\",\n    \"auth\": \"Dados de login para a conta da Amazon na qual os dispositivos da Alexa estão registrados\",\n    \"auth_info\": \"Os dados de login serão armazenados criptografados na instância local do ioBroker. Se os dados não forem especificados, o próprio cookie deve ser lido ou o login deve ser realizado através do proxy do adaptador para que o cookie possa ser determinado. No entanto, isso deve ser repetido manualmente assim que o cookie se tornar inválido.\",\n    \"autoQueryActivityOnTrigger\": \"Consultar automaticamente a atividade na interação do dispositivo\",\n    \"autoQueryActivityOnTrigger_info\": \"Ative apenas se você realmente precisar de informações de atividades automaticamente!\",\n    \"bespoken\": \"Dispositivo virtual Bespoken\",\n    \"bespokenToken\": \"Token de validação para Bespoken\",\n    \"bespoken_info\": \"Os Dispositivos Virtuais da Bespoken podem ser usados ​​para enviar comandos de texto para sua Conta da Amazon e fornecer controle limitado sobre seus Dispositivos - quando possível, usando palavras faladas. <br/> <br/> Você precisa criar uma Conta Bespoken e, em seguida, um Dispositivo Virtual para obtenha um Token Bespoken conforme descrito em https://read.Bespoken.io/end-to-end/setup/. Em seguida, você insere o token abaixo e verá novos objetos para o adaptador. <br/> <br/> Observação: Bespoken cria um arquivo de áudio com o texto e o envia para as APIs do Alexa. Portanto, a qualidade de compreensão do Alexa é tão boa quanto essa geração de fala, por isso pode ser problemático com palavras especiais.\",\n    \"cookie\": \"Biscoito\",\n    \"cookieLoginUrl\": \"Cookie personalizado-Amazon-URL\",\n    \"device_settings\": \"Configurações do dispositivo\",\n    \"device_settings_info\": \"Configurações para os dispositivos que são buscados na Amazon\",\n    \"history\": \"Configurações de histórico\",\n    \"historyIgnoreEmptySummary\": \"Ignorar entradas de histórico onde nenhum comando foi reconhecido\",\n    \"history_info\": \"Configurações para dados de histórico que estão disponíveis nos estados \\\"History\\\"\",\n    \"includeAppDevices\": \"Consulte e adicione também App-Devices\",\n    \"interval\": \"Intervalos de atualização de dados\",\n    \"interval_info\": \"Vários dados podem ser atualizados automaticamente. Você define aqui em segundos quantas vezes isso deve acontecer. Usando o histórico de estados. # Trigger, você sempre pode atualizar os dados manualmente. Se você definir 0 como intervalo, a atualização automática será desativada.\",\n    \"overwrite\": \"Substituir parâmetro especial para determinação e comunicação de cookies\",\n    \"overwrite_info\": \"Por padrão, os parâmetros são configurados para se conectar a uma conta da Amazon na Alemanha. Se você estiver usando outros países amazônicos, precisará definir os dados corretos aqui. Verifique no Adaptador README para alguns detalhes. Também em certos casos, quando a determinação automática de cookies está falhando, pode ajudar a brincar aqui, especialmente com Accept-Language e user-agent.\",\n    \"password\": \"Senha\",\n    \"proxy\": \"Configurações de proxy\",\n    \"proxyListenBind\": \"Ligação Proxy a determinados IPs\",\n    \"proxyOverrideIp\": \"Container-IP Externo (Docker)\",\n    \"proxyOwnIp\": \"IP ou nome de host próprio para usar no link do proxy\",\n    \"proxyPort\": \"Porta proxy\",\n    \"proxyPort_info\": \"(0 = porta aleatória)\",\n    \"proxy_info\": \"Se a determinação automática de um cookie não for capaz, o adaptador abre um proxy para permitir um login manual na amazon. O arquivo de registro irá dizer-lhe o URL para usar então. Abra isso no seu navegador, faça login manualmente na Amazon, resolva todos os Captchas ou Autenticações de dois fatores em potencial e o cookie será capturado em segundo plano automaticamente se for bem-sucedido.\",\n    \"push_connection\": \"Conexão Push\",\n    \"push_connection_info\": \"Com a conexão de push direta, as alterações dos dispositivos Alexa são notificadas ao adaptador e são usadas para atualizar apenas dados necessários em tempo real. Se ativado alguns intervalos de atualização de dados abaixo não são usados. Recomenda-se urgentemente usar a conexão de push para limitar os dados solicitados da nuvem de amazon!\",\n    \"resetCookie\": \"Redefinir cookies da Amazon\",\n    \"s (0 = disabled)\": \"s (0 = desativado)\",\n    \"sync_settings\": \"Configurações de sincronização\",\n    \"sync_settings_info\": \"Especifique quais dados devem ser sincronizados da Amazon Cloud para a instância do ioBroker. Por favor, apenas ative os dados que você realmente precisa!\",\n    \"synchronizeLists\": \"Sincronizar listas (por exemplo, Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Sincronizar dispositivos de casa inteligente\",\n    \"updateConfigurationInterval\": \"Intervalo de atualização da configuração do dispositivo\",\n    \"updateConfigurationInterval_info\": \"A configuração do dispositivo pode ser atualizada automaticamente. O intervalo de atualização define com que frequência isso deve acontecer. Não coloque isso muito baixo porque a Amazon pode bloquear você! O Mínimo é de 3300 ou definido para 0 para desativar a atualização.\",\n    \"updateHistoryInterval\": \"Intervalo de atualização do histórico\",\n    \"updateHistoryInterval_info\": \"O intervalo de atualização de História define com que frequência os dados de História devem ser atualizados a partir dos servidores da Amazon. O mínimo é 60s ou definido para 0 para desativar a atualização. Não coloque isso muito baixo porque a Amazon pode bloquear você! Quando o Push-Connection é estabelecido o intervalo de atualização não é usado porque a informação do push irá acioná-lo quando necessário automaticamente.\",\n    \"updateSmartHomeDevicesInterval\": \"Intervalo de atualização dos dispositivos Smart Home\",\n    \"updateSmartHomeDevicesInterval_info\": \"Por favor, ative este intervalo apenas se você realmente precisar dos valores dos dispositivos domésticos inteligentes! Apenas os dispositivos domésticos inteligentes que têm #includeInIntervalQuery ativado nos objetos e relatar ativamente seu status para sistemas Amazon são atualizados para se certificar de não criar problemas e custos no lado desenvolvedor de habilidades! Não coloque isso muito baixo porque a Amazon pode bloquear você! O Mínimo é de 900 ou definido para 0 para desativar a atualização (pode ser acionado manualmente via #query states).\",\n    \"updateStateInterval\": \"Intervalo de atualização de dados (informações do player ...)\",\n    \"updateStateInterval_info\": \"O intervalo geral de atualização de dados do dispositivo define com que frequência os dados devem ser atualizados a partir dos servidores da Amazon. O mínimo é 300s ou definido para 0 para desativar a atualização. Não coloque isso muito baixo porque a Amazon pode bloquear você! Quando o Push-Connection é estabelecido o intervalo de atualização é definido para 60 minutos quando ativado.\",\n    \"usePushConnection\": \"Use conexão push\",\n    \"userAgent\": \"Custom User-Agent\"\n}\n"
  },
  {
    "path": "admin/i18n/ru/translations.json",
    "content": "{\n    \"Email\": \"Пользователь/Email\",\n    \"Password\": \"Пароль\",\n    \"Please wait while loading Adapter-Status ...\": \"Пожалуйста, подождите, пока загружается Adapter-Status ...\",\n    \"ProxyInfo\": \"Нажмите <a href=\\\"%s\\\" target=\\\"_blank\\\">здесь, чтобы войти в свою учетную запись Amazon один раз</a>, чтобы активировать подключение к вашим устройствам Alexa для этого экземпляра.\",\n    \"Settings\": \"Настройки\",\n    \"StatusInfo\": \"Alexa-Cookies обновлены и обновлены до %s \",\n    \"acceptLanguage\": \"Пользовательский заголовок Accept-Language\",\n    \"alexaLogin\": \"Входные данные Alexa\",\n    \"alexaServiceHost\": \"Alexa base url\",\n    \"auth\": \"Детали входа для учетной записи Amazon, на которой зарегистрированы устройства Echo\",\n    \"auth_info\": \"Учетные данные будут храниться зашифрованными в вашем локальном экземпляре ioBroker. Вы также можете оставить их пустыми, но затем вам нужно получить cookie Amazon самостоятельно или использовать прокси-сервер от адаптера для входа в систему и получить файл cookie.\",\n    \"autoQueryActivityOnTrigger\": \"Автоматически запрашивать активность при взаимодействии с устройством\",\n    \"autoQueryActivityOnTrigger_info\": \"Пожалуйста, включайте эту функцию только в том случае, если вам действительно нужна автоматическая информация об активности!\",\n    \"bespoken\": \"Виртуальное устройство Bespoken\",\n    \"bespokenToken\": \"Bespoken Validation Token\",\n    \"bespoken_info\": \"Виртуальные устройства Bespoken можно использовать для отправки текстовых команд на вашу учетную запись Amazon и дает ограниченный контроль над вашими Устройствами - по возможности используя произносимые слова. <br/> <br/> Вам нужно создать учетную запись Bespoken, а затем виртуальное устройство для получить токен Bespoken, как описано на https://read.Bespoken.io/end-to-end/setup/. Затем вы вводите токен ниже, и вы увидите новые объекты для адаптера. <br/> <br/> Обратите внимание: Bespoken создает аудиофайл из вашего текста и отправляет его в Alexa-API. Таким образом, качество понимания Alexa так же хорошо, как и это поколение речи, поэтому это может быть проблематично со специальными словами.\",\n    \"cookie\": \"Cookie\",\n    \"cookieLoginUrl\": \"Пользовательский URL-адрес Cookie-Amazon\",\n    \"device_settings\": \"Настройки устройства\",\n    \"device_settings_info\": \"Настройки для устройств, которые извлекаются из Amazon\",\n    \"history\": \"Настройки истории\",\n    \"historyIgnoreEmptySummary\": \"Игнорировать записи истории, в которых не была распознана команда\",\n    \"history_info\": \"Настройки для данных истории, которые доступны в состояниях \\\"History\\\"\",\n    \"includeAppDevices\": \"Запросите и добавьте также App-Devices\",\n    \"interval\": \"Интервалы обновления данных\",\n    \"interval_info\": \"Несколько данных могут автоматически обновляться. Вы определяете здесь в секундах, как часто это должно произойти. Используя историю состояний. # Триггер, вы всегда можете обновлять данные вручную. Если вы установите 0 как интервал, автоматическое обновление отключено.\",\n    \"overwrite\": \"Переопределить специальный параметр для определения и связи файлов cookie\",\n    \"overwrite_info\": \"По умолчанию для параметров установлено соединение с немецкой учетной записью amazon. Если вы используете другие страны амазонки, вам нужно установить правильные данные здесь. За дополнительной информацией обратитесь к адаптеру README. Также в некоторых случаях, когда автоматическая дефрагментация файлов cookie не работает, она может помочь здесь играть особенно с Accept-Language и user-agent.\",\n    \"password\": \"Пароль\",\n    \"proxy\": \"Настройки прокси\",\n    \"proxyListenBind\": \"Связывание прокси-сервера с определенным IP-адресом\",\n    \"proxyOverrideIp\": \"Внешний контейнер-IP (Docker)\",\n    \"proxyOwnIp\": \"Собственный IP-адрес или имя хоста для использования в прокси-ссылке\",\n    \"proxyPort\": \"Порт прокси\",\n    \"proxyPort_info\": \"(0 = случайный порт)\",\n    \"proxy_info\": \"Если автоматическое определение файла cookie не позволяет адаптеру открыть прокси-сервер, чтобы разрешить вход в систему вручную для амазонки. Затем в файле журнала будет указан URL-адрес. Откройте это в своем браузере, вручную войдите в Amazon, разрешите все потенциальные Captchas или Two-Factor-Authentication, и cookie автоматически зацепится в фоновом режиме, если успешно.\",\n    \"push_connection\": \"Push-соединение\",\n    \"push_connection_info\": \"При прямом подключении к адаптеру оповещены изменения устройств Alexa и используются для обновления только необходимых данных в ближайшем режиме реального времени. Если включены некоторые интервалы обновления данных ниже не используются. Срочно рекомендуется использовать push-соединение для ограничения данных, запрашиваемых из облака amazon!\",\n    \"resetCookie\": \"Сброс Amazon Cookies\",\n    \"s (0 = disabled)\": \"сек (0 = отключено)\",\n    \"sync_settings\": \"Настройки синхронизации\",\n    \"sync_settings_info\": \"Укажите, какие данные должны быть синхронизированы из Amazon Cloud в экземпляр ioBroker. Пожалуйста, включите данные, которые вам действительно нужны!\",\n    \"synchronizeLists\": \"Синхронизируйте списки (например, Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Синхронизируйте устройства умного дома\",\n    \"updateConfigurationInterval\": \"Интервал обновления конфигурации устройства\",\n    \"updateConfigurationInterval_info\": \"Конфигурация устройства может обновляться автоматически. Интервал обновления определяет, как часто это должно произойти. Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Минимум 300-х или настроен на 0, чтобы отключить обновление.\",\n    \"updateHistoryInterval\": \"Интервал обновления истории\",\n    \"updateHistoryInterval_info\": \"Интервал обновления истории определяет, как часто данные истории должны быть обновлены с серверов Amazon. Минимум 60-х или установлен 0, чтобы отключить обновление. Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Когда Push-Connection установлен интервал обновления не используется, потому что нажмите информацию будет триггер при необходимости автоматически.\",\n    \"updateSmartHomeDevicesInterval\": \"Интервал обновления устройств «Умный дом»\",\n    \"updateSmartHomeDevicesInterval_info\": \"Пожалуйста, включите этот интервал только если вам действительно нужны значения умных домашних устройств! Только умные домашние устройства, которые имеют #includeInIntervalQuery активированы в объектах и активно сообщают о своем статусе в системы Amazon, обновляются, чтобы не создавать проблемы и затраты на стороне разработчика мастерства! Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Минимум 900s или установлено на 0, чтобы отключить обновление (может быть спрошен вручную через #query состояния).\",\n    \"updateStateInterval\": \"Интервал обновления данных (информация плеера ...)\",\n    \"updateStateInterval_info\": \"Интервал обновления данных общего устройства определяет, как часто данные должны быть обновлены с серверов Amazon. Минимум 300-х или установлен 0, чтобы отключить обновление. Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Когда Push-Connection установлен интервал обновления устанавливается до 60 минут при включенном.\",\n    \"usePushConnection\": \"Использовать push-соединение\",\n    \"userAgent\": \"Пользовательский User-Agent\"\n}\n"
  },
  {
    "path": "admin/i18n/uk/translations.json",
    "content": "{\n    \"Email\": \"Електронна пошта\",\n    \"Password\": \"Пароль\",\n    \"Please wait while loading Adapter-Status ...\": \"Зачекайте, поки завантажується статус адаптера…\",\n    \"ProxyInfo\": \"Клацніть <a href=\\\"%s\\\" target=\\\"_blank\\\">тут, щоб один раз увійти у свій обліковий запис Amazon</a>, щоб активувати підключення до ваших пристроїв Alexa для цього випадку.\",\n    \"Settings\": \"Налаштування\",\n    \"StatusInfo\": \"Файли cookie Alexa оновлені та останнє оновлення %s\",\n    \"acceptLanguage\": \"Спеціальний заголовок прийнятної мови\",\n    \"alexaLogin\": \"Дані для входу в Alexa\",\n    \"alexaServiceHost\": \"URL-адреса бази Alexa\",\n    \"auth\": \"Деталі входу в обліковий запис Amazon, де зареєстровані ваші пристрої Alexa\",\n    \"auth_info\": \"Облікові дані зберігатимуться в зашифрованому вигляді у вашому локальному екземплярі ioBroker. Ви також можете залишити їх порожніми, але тоді вам потрібно буде отримати файл cookie Amazon самостійно або використати проксі-сервер з адаптера, щоб увійти та отримати файл cookie.\",\n    \"autoQueryActivityOnTrigger\": \"Автоматично надсилайте запит щодо взаємодії з пристроєм\",\n    \"autoQueryActivityOnTrigger_info\": \"Увімкніть, лише якщо вам дійсно потрібна інформація про активність автоматично!\",\n    \"bespoken\": \"Віртуальний пристрій на замовлення\",\n    \"bespokenToken\": \"Індивідуальний маркер перевірки\",\n    \"bespoken_info\": \"Віртуальні пристрої Bespoken можна використовувати для надсилання текстових команд до вашого облікового запису Amazon і надають обмежений контроль над вашими пристроями, якщо це можливо, за допомогою голосових слів.<br/><br/>Вам потрібно створити обліковий запис Bespoken, а потім віртуальний пристрій, щоб отримати Bespoken Token, як описано на https://read.Bespoken.io/end-to-end/setup/. Потім ви вводите маркер нижче, і ви побачите нові об’єкти для адаптера.<br/><br/>Зверніть увагу: Bespoken створює аудіофайл із вашого тексту та надсилає його до Alexa-API. Таким чином, якість розуміння Alexa така ж хороша, як і це покоління мовлення, тому це може бути проблематично зі спеціальними словами.\",\n    \"cookie\": \"Печиво\",\n    \"cookieLoginUrl\": \"Користувацька URL-адреса файлів cookie Amazon\",\n    \"device_settings\": \"Налаштування пристрою\",\n    \"device_settings_info\": \"Налаштування для пристроїв, отриманих з Amazon\",\n    \"history\": \"Налаштування історії\",\n    \"historyIgnoreEmptySummary\": \"Ігнорувати записи історії, якщо жодна команда не була розпізнана\",\n    \"history_info\": \"Налаштування для даних історії, доступних у станах «Історія».\",\n    \"includeAppDevices\": \"Зробіть запит і додайте також App-Devices\",\n    \"interval\": \"Інтервал оновлення даних\",\n    \"interval_info\": \"Деякі дані можуть оновлюватися автоматично. Ви визначаєте тут за секунди, як часто це має відбуватися. Використовуючи історію стану.#trigger, ви завжди можете оновити дані вручну. Якщо ви встановите 0 як інтервал, автоматичне оновлення буде вимкнено.\",\n    \"overwrite\": \"Перевизначити спеціальний параметр для визначення та передачі файлів cookie\",\n    \"overwrite_info\": \"За замовчуванням параметри встановлені для підключення до німецького облікового запису Amazon. Якщо ваш обліковий запис знаходиться в іншій країні Amazon, вам потрібно ввести тут правильні дані. Перевірте файл README адаптера, щоб отримати деякі відомості. Крім того, у деяких випадках, коли автоматичне визначення файлів cookie не вдається, може допомогти пограти тут, особливо з налаштуваннями Accept-Language і user-agent.\",\n    \"password\": \"Пароль\",\n    \"proxy\": \"Налаштування проксі\",\n    \"proxyListenBind\": \"Прив'язка проксі до певного IP\",\n    \"proxyOverrideIp\": \"Зовнішній контейнер-IP (Docker)\",\n    \"proxyOwnIp\": \"Власна IP-адреса або ім’я хоста для використання в проксі-посиланні\",\n    \"proxyPort\": \"Проксі-порт\",\n    \"proxyPort_info\": \"(0 = випадковий порт)\",\n    \"proxy_info\": \"Якщо автоматичне визначення файлу cookie неможливо, адаптер відкриває проксі, щоб дозволити ручний вхід в amazon. Екран журналу та адміністратора підкаже URL-адресу для використання. Відкрийте це у своєму браузері, увійдіть в Amazon вручну, виконайте всі потенційні Captcha або двофакторну автентифікацію, і файл cookie буде перехоплено автоматично у фоновому режимі, якщо це вдасться.\",\n    \"push_connection\": \"Push-Connection\",\n    \"push_connection_info\": \"За допомогою прямого push-з’єднання про зміни з пристроїв Alexa повідомляється адаптер і використовується для оновлення лише необхідних даних майже в реальному часі. Якщо ввімкнено, деякі наведені нижче інтервали оновлення даних не використовуються. Настійно рекомендується використовувати push-з’єднання, щоб обмежити дані, які запитуються з хмари Amazon!\",\n    \"resetCookie\": \"Скинути файли cookie Amazon\",\n    \"s (0 = disabled)\": \"s (0 = вимкнено)\",\n    \"sync_settings\": \"Параметри синхронізації\",\n    \"sync_settings_info\": \"Укажіть, які дані потрібно синхронізувати з Amazon Cloud до екземпляра ioBroker. Увімкніть лише ті дані, які вам дійсно потрібні!\",\n    \"synchronizeLists\": \"Синхронізація списків (наприклад, Todo)\",\n    \"synchronizeSmartHomeDevices\": \"Синхронізація пристроїв Smart Home\",\n    \"updateConfigurationInterval\": \"Інтервал оновлення конфігурації пристрою\",\n    \"updateConfigurationInterval_info\": \"Конфігурацію пристрою можна оновлювати автоматично. Інтервал оновлення визначає, як часто це має відбуватися. Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Мінімум становить 300 с або встановіть 0, щоб вимкнути оновлення.\",\n    \"updateHistoryInterval\": \"Інтервал оновлення історії\",\n    \"updateHistoryInterval_info\": \"Інтервал оновлення історії визначає частоту оновлення даних історії із серверів Amazon. Мінімум становить 60 с або встановіть 0, щоб вимкнути оновлення. Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Коли встановлюється Push-Connection, інтервал оновлення не використовується, оскільки push-інформація активує його за потреби автоматично.\",\n    \"updateSmartHomeDevicesInterval\": \"Інтервал оновлення пристроїв Smart Home\",\n    \"updateSmartHomeDevicesInterval_info\": \"Увімкніть цей інтервал, лише якщо вам дійсно потрібні значення пристроїв розумного дому! Оновлюються лише ті розумні домашні пристрої, які мають активований #includeInIntervalQuery в об’єктах і активно повідомляють про свій статус системам Amazon, щоб переконатися, що не створюють проблем і витрат на стороні розробника навичок! Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Мінімум становить 900 с або встановіть значення 0, щоб вимкнути оновлення (можна запустити вручну за допомогою станів #query).\",\n    \"updateStateInterval\": \"Інтервал оновлення даних (інформація про гравця…)\",\n    \"updateStateInterval_info\": \"Загальний інтервал оновлення даних пристрою визначає частоту оновлення даних із серверів Amazon. Мінімум становить 300 с або встановіть 0, щоб вимкнути оновлення. Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Коли Push-Connection встановлено, інтервал оновлення встановлюється на 60 хвилин, якщо ввімкнено.\",\n    \"usePushConnection\": \"Використовуйте push-з'єднання\",\n    \"userAgent\": \"Спеціальний агент користувача\"\n}\n"
  },
  {
    "path": "admin/i18n/zh-cn/translations.json",
    "content": "{\n    \"Email\": \"电子邮件\",\n    \"Password\": \"密码\",\n    \"Please wait while loading Adapter-Status ...\": \"在等待Adapter-Status时请稍候......\",\n    \"ProxyInfo\": \"请单击<a href=\\\"%s\\\" target=\\\"_blank\\\">此处登录您的亚马逊账户一次</a>，为该实例激活与您的 Alexa 设备的连接。\",\n    \"Settings\": \"设置\",\n    \"StatusInfo\": \"Alexa-Cookies是最新的，最后更新于％s\",\n    \"acceptLanguage\": \"自定义接受语言标题\",\n    \"alexaLogin\": \"Alexa登录数据\",\n    \"alexaServiceHost\": \"Alexa基本网址\",\n    \"auth\": \"注册了Echo设备的Amazon帐户的登录详细信息\",\n    \"auth_info\": \"凭据将以加密方式存储在本地ioBroker实例中。\",\n    \"autoQueryActivityOnTrigger\": \"自动查询设备交互活动\",\n    \"autoQueryActivityOnTrigger_info\": \"请仅在您确实需要自动获取活动信息时启用！\",\n    \"bespoken\": \"预定的虚拟设备\",\n    \"bespokenToken\": \"验证令牌\",\n    \"bespoken_info\": \"Bespoken虚拟设备可用于向您的Amazon帐户发送文本命令，并在可能的情况下使用口语对您的设备进行有限的控制。<br/> <br/>您需要先创建Bespoken帐户，然后创建虚拟设备才能使用\",\n    \"cookie\": \"曲奇饼\",\n    \"cookieLoginUrl\": \"自定义Cookie-Amazon-URL\",\n    \"device_settings\": \"设备设置\",\n    \"device_settings_info\": \"从亚马逊获取的设备的设置\",\n    \"history\": \"历史记录设置\",\n    \"historyIgnoreEmptySummary\": \"忽略未识别命令的历史条目\",\n    \"history_info\": \"在“历史记录”状态下可用的历史记录数据设置\",\n    \"includeAppDevices\": \"查询并添加 App-Devices\",\n    \"interval\": \"数据更新间隔\",\n    \"interval_info\": \"几个数据可以自动更新。\",\n    \"overwrite\": \"覆盖用于cookie确定和通信的特殊参数\",\n    \"overwrite_info\": \"默认情况下，参数设置为与德国亚马逊帐户关联。\",\n    \"password\": \"密码\",\n    \"proxy\": \"代理设置\",\n    \"proxyListenBind\": \"代理绑定到某些IP\",\n    \"proxyOverrideIp\": \"外部容器-IP（Docker）\",\n    \"proxyOwnIp\": \"在代理链接中使用的自己的IP或主机名\",\n    \"proxyPort\": \"代理端口\",\n    \"proxyPort_info\": \"（0 =随机端口）\",\n    \"proxy_info\": \"如果无法自动确定Cookie，则适配器将打开代理以允许手动登录到亚马逊。\",\n    \"push_connection\": \"推式连接\",\n    \"push_connection_info\": \"由于直接推动了阿克拉装置的改变,已通知适应者,并且仅用于不久的实时更新必要的数据。 如果无法使用下文的一些数据更新间隔。 令人急切地建议利用推动联系来限制从马纳西地区的数据!\",\n    \"resetCookie\": \"重置Amazon Cookies\",\n    \"s (0 = disabled)\": \"s（0 =禁用）\",\n    \"sync_settings\": \"同步设置\",\n    \"sync_settings_info\": \"数据应从亚马逊克尔集到奥布罗克。 只请你真正需要!\",\n    \"synchronizeLists\": \"同步列表（例如 Todo）\",\n    \"synchronizeSmartHomeDevices\": \"同步智能家居设备\",\n    \"updateConfigurationInterval\": \"设备配置更新间隔\",\n    \"updateConfigurationInterval_info\": \"装置配置可以自动更新。 更新的间隔界定了如何经常发生这种情况。 由于其他亚马逊可能阻碍你! 最低限度是900个,或规定为0.至0.1%的更新。.\",\n    \"updateHistoryInterval\": \"历史记录更新间隔\",\n    \"updateHistoryInterval_info\": \"历史最新间隔界定了如何从亚马逊服务器更新历史数据。 最低限度是60个,或为0.1%的更新。 由于其他亚马逊可能阻碍你! 当Push-Connection成立时,没有使用最新间隔,因为如果需要自动启动信息就会触发。.\",\n    \"updateSmartHomeDevicesInterval\": \"智能家居设备更新间隔\",\n    \"updateSmartHomeDevicesInterval_info\": \"只有在你真正需要聪明的家用具的价值观时,才能够做到这一点。 只有在有目标的星球和积极汇报亚马逊系统状况的智能家装置得到更新,以确保不会产生技能发展方面的问题和费用! 由于其他亚马逊可能阻碍你! 最低限度是300个,或规定0.至0.至0.可区分的更新(通过#query州以手工方式进行)。.\",\n    \"updateStateInterval\": \"数据更新间隔（玩家信息...）\",\n    \"updateStateInterval_info\": \"总的装置数据更新的间隔界定了如何从亚马逊服务器更新数据。 最低限度是300个,或为0.1%的更新。 由于其他亚马逊可能阻碍你! 当Push-Connection成立时,更新的间隔时间为60分钟。.\",\n    \"usePushConnection\": \"使用推送连接\",\n    \"userAgent\": \"自定义用户代理\"\n}\n"
  },
  {
    "path": "admin/index_m.html",
    "content": "<html>\n\n<head>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/adapter.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../../lib/css/materialize.css\">\n\n    <script type=\"text/javascript\" src=\"../../lib/js/jquery-3.2.1.min.js\"></script>\n    <script type=\"text/javascript\" src=\"../../socket.io/socket.io.js\"></script>\n\n    <script type=\"text/javascript\" src=\"../../js/translate.js\"></script>\n    <script type=\"text/javascript\" src=\"../../lib/js/materialize.js\"></script>\n    <script type=\"text/javascript\" src=\"../../js/adapter-settings.js\"></script>\n    <script type=\"text/javascript\" src=\"words.js\"></script>\n\n    <!-- you have to define 2 functions in the global scope: -->\n    <script type=\"text/javascript\">\n\n        var secret;\n\n        function ownFillSelectIPs(id, actualAddr, callback) {\n            getIPs(function (ips) {\n                var str = '';\n                var existingFound = false;\n                for (var i = 0; i < ips.length; i++) {\n                    if (ips[i].family === 'ipv6') continue;\n                    if (ips[i].address === '0.0.0.0') continue;\n                    if (ips[i].address === '127.0.0.1') continue;\n                    if (ips[i].address === actualAddr) existingFound = true;\n                    str += '<option value=\"' + ips[i].address + '\" ' + ((ips[i].address === actualAddr) ? 'selected' : '') + '>' + ips[i].name + '</option>';\n                }\n                if (!existingFound && actualAddr) {\n                    str = '<option value=\"' + actualAddr + '\" selected> Please change INVALID!!' + actualAddr + '</option>' + str;\n                }\n\n                $(id).html(str);\n                $(id).select();\n                if (typeof callback === 'function') {\n                    callback();\n                }\n            });\n        }\n\n        function sprintf(format) {\n            var args = Array.prototype.slice.call(arguments, 1);\n            var i = 0;\n            return format.replace(/%s/g, function () {\n                return args[i++];\n            });\n        }\n\n        var statusUpdateTimeout = null;\n        function getStatusInfo(updateCall) {\n            if (statusUpdateTimeout) clearTimeout(statusUpdateTimeout);\n            if (!updateCall) {\n                statusUpdateTimeout = setTimeout(getStatusInfo, 5000);\n            }\n            sendTo(null, 'getStatusInfo', null, function (response) {\n                if (statusUpdateTimeout) clearTimeout(statusUpdateTimeout);\n                var text;\n                if (response.result.proxyActive) {\n                    text = '<img src=\"warning.png\" width=60 height=60 align=\"middle\">' + sprintf(systemDictionary['ProxyInfo'][systemLang], response.result.proxyUrl);\n                    statusUpdateTimeout = setTimeout(getStatusInfo, 5000, true);\n                }\n                else {\n                    text = '<img src=\"info.png\" width=60 height=60 align=\"middle\">' + sprintf(systemDictionary['StatusInfo'][systemLang], response.result.cookieTimestamp);\n                }\n                $('#statusInfo').html(text);\n            });\n        }\n\n        // the function loadSettings has to exist ...\n        function load(settings, onChange) {\n            ownFillSelectIPs('#proxyOwnIp', settings.proxyOwnIp, false, true, function () {\n                $('#proxyOwnIp').select();\n            });\n\n            fillSelectIPs('#proxyListenBind', settings.proxyListenBind, false, true, function () {\n                $('#proxyListenBind').select();\n            });\n\n            if (!settings) return;\n            settings.email = '';\n            settings.password = '';\n            if (settings.cookie === undefined) settings.cookie = '';\n            if (settings.alexaServiceHost === undefined) settings.alexaServiceHost = '';\n            if (settings.userAgent === undefined) settings.userAgent = '';\n            if (settings.acceptLanguage === undefined) settings.acceptLanguage = '';\n            if (settings.cookieLoginUrl === undefined) settings.cookieLoginUrl = '';\n            if (settings.updateStateInterval === undefined) settings.updateStateInterval = 0;\n            if (settings.updateHistoryInterval === undefined) settings.updateHistoryInterval = 0;\n            if (settings.proxyOwnIp === undefined) settings.proxyOwnIp = '';\n            if (settings.proxyOverrideIp === undefined) settings.proxyOverrideIp = '';\n            if (settings.proxyPort === undefined) settings.proxyPort = 0;\n            if (settings.proxyListenBind === undefined) settings.proxyListenBind = '';\n            if (settings.usePushConnection === undefined) settings.usePushConnection = false;\n            if (settings.resetCookies === undefined) settings.resetCookies = false;\n            if (settings.historyIgnoreEmptySummary === undefined) settings.historyIgnoreEmptySummary = false;\n            if (settings.includeAppDevices === undefined) settings.includeAppDevices = false;\n            if (settings.autoQueryActivityOnTrigger === undefined) settings.autoQueryActivityOnTrigger = false;\n\n            for (var key in settings) {\n                if (!settings.hasOwnProperty(key)) continue;\n\n                var $key = $('#' + key + '.value');\n                if ($key.attr('type') === 'checkbox') {\n                    $key.prop('checked', settings[key]).on('change', function () {\n                        if ($('#auth').prop('checked')) {\n                            $('#secure').prop('checked', true);\n                        }\n                        onChange();\n                        showHideSettings();\n                    });\n                } else {\n                    var val = settings[key];\n                    $key.val(val).on('change', function () {\n                        onChange();\n                    }).on('keyup', function () {\n                        onChange();\n                    });\n                }\n            }\n            // Set default language\n            if (!settings.language) {\n                settings.language = systemLang || 'en';\n                $('#language').val(settings.language).trigger('change');\n            }\n\n            M.updateTextFields();\n            $('select').select();\n            onChange(false);\n            getStatusInfo();\n        }\n\n        function save(callback) {\n            var obj = {};\n            $('.value').each(function () {\n                var $this = $(this);\n                switch ($this.attr('type')) {\n                    case 'checkbox':\n                        obj[$this.attr('id')] = $this.prop('checked');\n                        break;\n                    case 'number':\n                        obj[$this.attr('id')] = parseInt($this.val(), 10);\n                        break;\n                    default:\n                        obj[$this.attr('id')] = $this.val();\n                        break;\n                }\n            });\n            callback(obj);\n        }\n    </script>\n\n    <style>\n        h1 {\n            font-size: 10px;\n            color: #505050;\n            font-weight: bold;\n            width: 100%;\n            padding: 6px 5px 5px 5px;\n            background-color: #e8e8e8;\n        }\n\n        .title {\n            font-weight: bold;\n        }\n\n        .proxy-info {\n            margin-bottom: 25px;\n        }\n\n        .adapter-container>div {\n            margin-bottom: 0 !important;\n        }\n\n        .page {\n            height: calc(100% - 50px) !important;\n        }\n\n        .marg {\n            margin-top: 3em;\n        }\n\n        #statusInfo a:link {\n            color: #3366BB !important;\n            cursor: pointer !important;\n            text-decoration: underline !important;\n        }\n\n        .respTab {\n            padding: 0 !important;\n        }\n\n        /* Styling for small Screen (Responsive) */\n        @media screen and (max-width: 768px) {\n            .m .tabs .tab {\n                margin: 0 5px !important;\n            }\n\n            .m .tabs .tab a {\n                padding: 5px 5px;\n                display: flex;\n                white-space: normal;\n                word-wrap: break-word;\n                text-align: center;\n                align-items: center;\n                justify-content: center;\n                line-height: 1.2;\n            }\n\n            .m .tabs {\n                display: flex;\n                justify-content: space-around;\n            }\n\n            #statusInfo {\n                display: flex;\n                align-items: center;\n                justify-content: center;\n                flex-direction: column;\n                text-align: center;\n                gap: 10px;\n            }\n\n            .warnInfo {\n                display: flex;\n                align-items: center;\n                justify-content: center;\n                flex-direction: column;\n                text-align: center;\n                gap: 10px;\n            }\n\n            .adapter-body {\n                overflow: hidden;\n            }\n\n            .m [type=checkbox]+span:not(.lever) {\n                height: auto;\n            }\n        }\n    </style>\n</head>\n\n<body>\n    <div class=\"m adapter-container\" id=\"settings\">\n        <div class=\"row\">\n            <div class=\"col s12 respTab\">\n                <!-- Tabs definieren -->\n                <ul class=\"tabs\">\n                    <!-- Tab1 Login + Intervalle-->\n                    <li class=\"tab col s4\">\n                        <a href=\"#tab-main\" class=\"translate\">alexaLogin</a>\n                    </li>\n                    <!-- Tab2 Cookie -->\n                    <li class=\"tab col s4\">\n                        <a href=\"#tab-cookie\" class=\"translate\">cookie</a>\n                    </li>\n                    <!-- Tab3 Proxy -->\n                    <li class=\"tab col s4\">\n                        <a href=\"#tab-proxy\" class=\"translate\">proxy</a>\n                    </li>\n                    <!-- weitere Tabs hier einfügen -->\n                </ul>\n            </div>\n\n            <!-- Ab hier der Inhalt der Tabs -->\n\n\n            <!-- Tab1 Login + Intervall-->\n            <div id=\"tab-main\" class=\"col s12 page\">\n                <div class=\"row\">\n                    <div class=\"col s6 m6 l6\">\n                        <img src=\"alexa.png\" class=\"logo\">\n                    </div>\n                </div>\n\n                <input id=\"email\" type=\"hidden\" />\n                <input type=\"hidden\" id=\"password\" />\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <b><span id=\"statusInfo\" for=\"statusInfo\" class=\"translate\">Please wait while loading\n                                Adapter-Status ...</span></b>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <p class=\"translate title\">push_connection</p>\n                        <p class=\"translate\">push_connection_info</p>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"usePushConnection\" type=\"checkbox\" class=\"value\" />\n                            <span for=\"usePushConnection\" class=\"translate\">usePushConnection</span>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <p class=\"translate title\">history</p>\n                        <p class=\"translate\">history_info</p>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"historyIgnoreEmptySummary\" type=\"checkbox\" class=\"value\" />\n                            <span for=\"historyIgnoreEmptySummary\" class=\"translate\">historyIgnoreEmptySummary</span>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"autoQueryActivityOnTrigger\" type=\"checkbox\" class=\"value\" />\n                            <span for=\"autoQueryActivityOnTrigger\" class=\"translate\">autoQueryActivityOnTrigger</span>\n                        </div>\n                        <div class=\"col s12 m6 warnInfo\">\n                            <img src=\"warning.png\" width=60 height=60 align=\"left\">\n                            <span class=\"translate\">autoQueryActivityOnTrigger_info</span>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12 warnInfo\">\n                        <p class=\"translate title\">sync_settings</p>\n                        <img src=\"warning.png\" width=60 height=60 align=\"left\">\n                        <p class=\"translate\">sync_settings_info</p>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"synchronizeSmartHomeDevices\" type=\"checkbox\" class=\"value\" />\n                            <span for=\"synchronizeSmartHomeDevices\" class=\"translate\">synchronizeSmartHomeDevices</span>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"synchronizeLists\" type=\"checkbox\" class=\"value\" />\n                            <span for=\"synchronizeLists\" class=\"translate\">synchronizeLists</span>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"includeAppDevices\" type=\"checkbox\" class=\"value\" />\n                            <span for=\"includeAppDevices\" class=\"translate\">includeAppDevices</span>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <p class=\"translate title\">interval</p>\n                        <p class=\"translate\">interval_info</p>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input class=\"value validate\" placeholder=\"300\" id=\"updateStateInterval\" type=\"number\"\n                                min=\"0\" max=\"100000\">\n                            <label for=\"updateStateInterval\" class=\"translate\">updateStateInterval</label>\n                            <span class=\"helper-text translate\">s (0 = disabled)</span>\n                        </div>\n                        <div class=\"col s12 m6 warnInfo\">\n                            <img src=\"warning.png\" width=60 height=60 align=\"left\">\n                            <span class=\"translate\">updateStateInterval_info</span>\n                        </div>\n                    </div>\n                    <div class=\"col s12\">\n                        <div class=\"input-field col s12 m6\">\n                            <input class=\"value validate\" placeholder=\"300\" id=\"updateHistoryInterval\" type=\"number\"\n                                min=\"0\" max=\"100000\">\n                            <label for=\"updateHistoryInterval\" class=\"translate\">updateHistoryInterval</label>\n                            <span class=\"helper-text translate\">s (0 = disabled)</span>\n                        </div>\n                        <div class=\"col s12 m6 warnInfo\">\n                            <img src=\"warning.png\" width=60 height=60 align=\"left\">\n                            <span class=\"translate\">updateHistoryInterval_info</span>\n                        </div>\n                    </div>\n                    <div class=\"col s12\">\n                        <div class=\"input-field col s12 m6\">\n                            <input class=\"value validate\" placeholder=\"300\" id=\"updateConfigurationInterval\"\n                                type=\"number\" min=\"0\" max=\"100000\">\n                            <label for=\"updateConfigurationInterval\"\n                                class=\"translate\">updateConfigurationInterval</label>\n                            <span class=\"helper-text translate\">s (0 = disabled)</span>\n                        </div>\n                        <div class=\"input-field col s12 m6 warnInfo\">\n                            <img src=\"warning.png\" width=60 height=60 align=\"left\">\n                            <span class=\"translate\">updateConfigurationInterval_info</span>\n                        </div>\n                    </div>\n                </div>\n\n            </div> <!-- Ende Tab1 -->\n\n            <!-- Tab2 Cookie -->\n            <div id=\"tab-cookie\" class=\"col s12 page\">\n                <div class=\"row\">\n                    <div class=\"col s12 m4 l2\">\n                        <img src=\"alexa.png\" class=\"logo\">\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"resetCookies\" type=\"checkbox\" class=\"value\" />\n                            <span for=\"resetCookies\" class=\"translate\">resetCookie</span>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col s12\">\n                        <p class=\"translate title\">overwrite</p>\n                        <p class=\"translate\">overwrite_info</p>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"alexaServiceHost\" class=\"value\" type=\"text\" />\n                            <label for=\"alexaServiceHost\" class=\"translate\">alexaServiceHost</label>\n                        </div>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"userAgent\" class=\"value\" type=\"text\" />\n                            <label for=\"userAgent\" class=\"translate\">userAgent</label>\n                        </div>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"acceptLanguage\" class=\"value\" type=\"text\" />\n                            <label for=\"acceptLanguage\" class=\"translate\">acceptLanguage</label>\n                        </div>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"cookieLoginUrl\" class=\"value\" type=\"text\" />\n                            <label for=\"cookieLoginUrl\" class=\"translate\">cookieLoginUrl</label>\n                        </div>\n\n                    </div>\n                </div>\n\n            </div> <!-- Ende Tab2 -->\n\n            <!-- Tab3 Proxy -->\n            <div id=\"tab-proxy\" class=\"col s12 page\">\n                <div class=\"row\">\n                    <div class=\"col s12 m4 l2\">\n                        <img src=\"alexa.png\" class=\"logo\">\n                    </div>\n                </div>\n                <div class=\"row\">\n\n                    <div class=\"col s12\">\n                        <p class=\"translate title\">proxy</p>\n                        <p class=\"translate proxy-info\">proxy_info</p>\n\n                        <div class=\"input-field col s12\">\n                            <select id=\"proxyOwnIp\" class=\"value\"></select>\n                            <label for=\"proxyOwnIp\" class=\"translate\">proxyOwnIp</label>\n                        </div>\n\n                        <div class=\"input-field col s12\">\n                            <input id=\"proxyPort\" class=\"value\" type=\"number\" min=\"0\" max=\"65565\" />\n                            <label for=\"proxyPort\" class=\"translate\">proxyPort</label>\n                            <span class=\"helper-text translate\">proxyPort_info</span>\n                        </div>\n\n                        <div class=\"input-field col s12\">\n                            <select id=\"proxyListenBind\" class=\"value\"></select>\n                            <label for=\"proxyListenBind\" class=\"translate\">proxyListenBind</label>\n                        </div>\n\n                        <div class=\"input-field col s12 m6\">\n                            <input id=\"proxyOverrideIp\" class=\"value\" type=\"text\" />\n                            <label for=\"proxyOverrideIp\" class=\"translate\">proxyOverrideIp</label>\n                        </div>\n\n                    </div>\n\n                </div>\n            </div> <!-- Ende Tab3 -->\n\n        </div><!-- Ende row -->\n\n    </div><!-- Ende Adapter Container -->\n</body>\n\n</html>"
  },
  {
    "path": "admin/words.js",
    "content": "/*global systemDictionary:true */\n/*\n+===================== DO NOT MODIFY ======================+\n| This file was generated by translate-adapter, please use |\n| `translate-adapter adminLanguages2words` to update it.   |\n+===================== DO NOT MODIFY ======================+\n*/\n'use strict';\n\nsystemDictionary = {\n    \"Email\": {                                        \"en\": \"E-Mail\",                                           \"de\": \"E-Mail\",                                           \"ru\": \"Пользователь/Email\",                               \"pt\": \"E-mail\",                                           \"nl\": \"User/E-mail\",                                      \"fr\": \"Utilisateur/Email\",                                \"it\": \"Utente/E-mail\",                                    \"es\": \"Usuario/Correo electrónico\",                       \"pl\": \"Użytkownik/Email\",                                 \"uk\": \"Електронна пошта\",                                 \"zh-cn\": \"电子邮件\"},\n    \"Password\": {                                     \"en\": \"Password\",                                         \"de\": \"Passwort\",                                         \"ru\": \"Пароль\",                                           \"pt\": \"Senha\",                                            \"nl\": \"Wachtwoord\",                                       \"fr\": \"Mot de passe\",                                     \"it\": \"Parola d'ordine\",                                  \"es\": \"Contraseña\",                                       \"pl\": \"Hasło\",                                            \"uk\": \"Пароль\",                                           \"zh-cn\": \"密码\"},\n    \"Please wait while loading Adapter-Status ...\": { \"en\": \"Please wait while loading Adapter-Status …\",       \"de\": \"Der Adapter-Status wird geladen. Bitte warten …\",  \"ru\": \"Пожалуйста, подождите, пока загружается Adapter-Status ...\", \"pt\": \"Por favor, aguarde enquanto carrega o estado do adaptador …\", \"nl\": \"Een ogenblik geduld tijdens het laden van de adapter-status ...\", \"fr\": \"Veuillez patienter pendant le chargement de Adapter-Status ...\", \"it\": \"Si prega di attendere durante il caricamento di Adapter-Status ...\", \"es\": \"Por favor, espere mientras se carga el adaptador de estado ...\", \"pl\": \"Poczekaj na załadowanie statusu adaptera ...\",     \"uk\": \"Зачекайте, поки завантажується статус адаптера…\",  \"zh-cn\": \"在等待Adapter-Status时请稍候......\"},\n    \"ProxyInfo\": {                                    \"en\": \"Please click <a href=\\\"%s\\\" target=\\\"_blank\\\">here to log in to your Amazon Account once</a> to activate the connection to your Alexa devices for this instance.\", \"de\": \"Bitte den <a href=\\\"%s\\\" target=\\\"_blank\\\">folgenden Link zur einmaligen Anmeldung im Amazon-Konto</a> nutzen, um die Adapterverbindung zu Ihren Alexa-Geräten zu aktivieren.\", \"ru\": \"Нажмите <a href=\\\"%s\\\" target=\\\"_blank\\\">здесь, чтобы войти в свою учетную запись Amazon один раз</a>, чтобы активировать подключение к вашим устройствам Alexa для этого экземпляра.\", \"pt\": \"Clique <a href=\\\"%s\\\" target=\\\"_blank\\\">aqui para fazer login em sua conta Amazon</a> para ativar a conexão com seus dispositivos Alexa para esta instância.\", \"nl\": \"Klik <a href=\\\"%s\\\" target=\\\"_blank\\\">hier om u eenmaal aan te melden bij uw Amazon-account</a> om de verbinding met uw Alexa-apparaten voor deze instantie te activeren.\", \"fr\": \"Veuillez cliquer <a href=\\\"%s\\\" target=\\\"_blank\\\">ici pour vous connecter une fois à votre compte Amazon</a> afin d'activer la connexion à vos appareils Alexa pour cette instance.\", \"it\": \"Fai clic <a href=\\\"%s\\\" target=\\\"_blank\\\">qui per accedere al tuo account Amazon una volta</a> per attivare la connessione ai tuoi dispositivi Alexa per questa istanza.\", \"es\": \"Haga clic <a href=\\\"%s\\\" target=\\\"_blank\\\">aquí para iniciar sesión en su cuenta de Amazon una vez</a> para activar la conexión a sus dispositivos Alexa para esta instancia.\", \"pl\": \"Kliknij <a href=\\\"%s\\\" target=\\\"_blank\\\">tutaj, aby zalogować się raz na swoje konto Amazon</a>, aby aktywować połączenie z urządzeniami Alexa dla tej instancji.\", \"uk\": \"Клацніть <a href=\\\"%s\\\" target=\\\"_blank\\\">тут, щоб один раз увійти у свій обліковий запис Amazon</a>, щоб активувати підключення до ваших пристроїв Alexa для цього випадку.\", \"zh-cn\": \"请单击<a href=\\\"%s\\\" target=\\\"_blank\\\">此处登录您的亚马逊账户一次</a>，为该实例激活与您的 Alexa 设备的连接。\"},\n    \"Settings\": {                                     \"en\": \"Settings\",                                         \"de\": \"Einstellungen\",                                    \"ru\": \"Настройки\",                                        \"pt\": \"Configurações\",                                    \"nl\": \"instellingen\",                                     \"fr\": \"Paramètres\",                                       \"it\": \"impostazioni\",                                     \"es\": \"Configuraciones\",                                  \"pl\": \"Ustawienia\",                                       \"uk\": \"Налаштування\",                                     \"zh-cn\": \"设置\"},\n    \"StatusInfo\": {                                   \"en\": \"Alexa-Cookies are up to date and last updated on %s\", \"de\": \"Alexa-Cookies sind aktuell und zuletzt aktualisiert am %s\", \"ru\": \"Alexa-Cookies обновлены и обновлены до %s \",       \"pt\": \"Os cookies da Alexa são atuais e foram atualizados pela última vez em %s\", \"nl\": \"Alexa-cookies zijn up-to-date en voor het laatst bijgewerkt op %s \", \"fr\": \"Les cookies Alexa sont à jour et mis à jour pour la dernière fois le %s \", \"it\": \"Alexa-Cookies sono aggiornati e aggiornati per l'ultima volta su %s \", \"es\": \"Las cookies de Alexa están actualizadas y actualizadas por última vez en %s \", \"pl\": \"Alexa-Cookies są aktualne i ostatnio aktualizowane w %s \", \"uk\": \"Файли cookie Alexa оновлені та останнє оновлення %s\", \"zh-cn\": \"Alexa-Cookies是最新的，最后更新于％s\"},\n    \"acceptLanguage\": {                               \"en\": \"Custom Accept-Language Header\",                    \"de\": \"Eigener Accept-Language-Header\",                   \"ru\": \"Пользовательский заголовок Accept-Language\",       \"pt\": \"Próprio Accept-Language-Header\",                   \"nl\": \"Aangepaste acceptatie-taalkop\",                    \"fr\": \"En-tête personnalisé Accept-Language\",             \"it\": \"Intestazione di lingua accettabile personalizzata\", \"es\": \"Encabezado personalizado de idioma de aceptación\", \"pl\": \"Niestandardowy nagłówek Accept-Language\",          \"uk\": \"Спеціальний заголовок прийнятної мови\",            \"zh-cn\": \"自定义接受语言标题\"},\n    \"alexaLogin\": {                                   \"en\": \"Alexa login data\",                                 \"de\": \"Alexa Login Daten\",                                \"ru\": \"Входные данные Alexa\",                             \"pt\": \"Dados de login para Alexa\",                        \"nl\": \"Alexa-inloggegevens\",                              \"fr\": \"Données de connexion Alexa\",                       \"it\": \"Dati di login di Alexa\",                           \"es\": \"Datos de acceso de Alexa\",                         \"pl\": \"Dane logowania Alexa\",                             \"uk\": \"Дані для входу в Alexa\",                           \"zh-cn\": \"Alexa登录数据\"},\n    \"alexaServiceHost\": {                             \"en\": \"Alexa base url\",                                   \"de\": \"Alexa Basis URL\",                                  \"ru\": \"Alexa base url\",                                   \"pt\": \"URL básico da Alexa\",                              \"nl\": \"Alexa-basis-URL\",                                  \"fr\": \"Alexa base de l'Alexa\",                            \"it\": \"Alexa base url\",                                   \"es\": \"URL base de Alexa\",                                \"pl\": \"Adres bazowy Alexa\",                               \"uk\": \"URL-адреса бази Alexa\",                            \"zh-cn\": \"Alexa基本网址\"},\n    \"auth\": {                                         \"en\": \"Login Details for Amazon Account where your Alexa devices are registered\", \"de\": \"Login-Daten für das Amazon-Konto, in dem die Alexa Geräte registriert sind\", \"ru\": \"Детали входа для учетной записи Amazon, на которой зарегистрированы устройства Echo\", \"pt\": \"Dados de login para a conta da Amazon na qual os dispositivos da Alexa estão registrados\", \"nl\": \"Aanmeldgegevens voor Amazon-account waarop Echo-apparaten zijn geregistreerd\", \"fr\": \"Détails de connexion pour le compte Amazon où les appareils Echo sont enregistrés\", \"it\": \"Dettagli di accesso per l'account Amazon in cui sono registrati i dispositivi Echo\", \"es\": \"Detalles de inicio de sesión para Amazon Cuenta donde están registrados los dispositivos Echo\", \"pl\": \"Dane logowania do konta Amazon, w którym zarejestrowane są urządzenia Echo\", \"uk\": \"Деталі входу в обліковий запис Amazon, де зареєстровані ваші пристрої Alexa\", \"zh-cn\": \"注册了Echo设备的Amazon帐户的登录详细信息\"},\n    \"auth_info\": {                                    \"en\": \"The credentials will be stored encrypted in your local ioBroker instance. You can also leave them empty, but then you need to fetch the Amazon cookie by your own or use the proxy from the adapter to login and get the cookie.\", \"de\": \"Die Login-Daten werden verschlüsselt in der lokalen ioBroker-Instanz gespeichert. Wenn die Daten nicht angegeben werden, muss das Cookie selbst ausgelesen werden oder über den Proxy des Adapters der Login vorgenommen werden, sodass das Cookie ermittelt werden kann. Dies muss aber manuell wiederholt werden sobald der Cookie ungültig geworden ist.\", \"ru\": \"Учетные данные будут храниться зашифрованными в вашем локальном экземпляре ioBroker. Вы также можете оставить их пустыми, но затем вам нужно получить cookie Amazon самостоятельно или использовать прокси-сервер от адаптера для входа в систему и получить файл cookie.\", \"pt\": \"Os dados de login serão armazenados criptografados na instância local do ioBroker. Se os dados não forem especificados, o próprio cookie deve ser lido ou o login deve ser realizado através do proxy do adaptador para que o cookie possa ser determinado. No entanto, isso deve ser repetido manualmente assim que o cookie se tornar inválido.\", \"nl\": \"De inloggegevens worden gecodeerd opgeslagen in uw lokale instantie ioBroker. Je kunt ze ook leeg laten, maar dan moet je de Amazon-cookie zelf ophalen of de proxy van de adapter gebruiken om in te loggen en de cookie te krijgen.\", \"fr\": \"Les informations d'identification seront stockées cryptées dans votre instance ioBroker locale. Vous pouvez également les laisser vides, mais vous devez ensuite récupérer le cookie Amazon par vous-même ou utiliser le proxy de l'adaptateur pour vous connecter et obtenir le cookie.\", \"it\": \"Le credenziali verranno archiviate crittografate nell'istanza ioBroker locale. Puoi anche lasciarli vuoti, ma poi devi recuperare il cookie Amazon da solo o utilizzare il proxy dall'adattatore per accedere e ottenere il cookie.\", \"es\": \"Las credenciales se almacenarán encriptadas en su instancia local de ioBroker. También puede dejarlos vacíos, pero luego debe buscar la cookie de Amazon por su cuenta o usar el proxy del adaptador para iniciar sesión y obtener la cookie.\", \"pl\": \"Dane uwierzytelniające będą przechowywane w postaci zaszyfrowanej w lokalnej instancji ioBroker. Możesz również pozostawić je puste, ale musisz pobrać plik cookie Amazon samodzielnie lub użyć proxy z adaptera, aby się zalogować i uzyskać plik cookie.\", \"uk\": \"Облікові дані зберігатимуться в зашифрованому вигляді у вашому локальному екземплярі ioBroker. Ви також можете залишити їх порожніми, але тоді вам потрібно буде отримати файл cookie Amazon самостійно або використати проксі-сервер з адаптера, щоб увійти та отримати файл cookie.\", \"zh-cn\": \"凭据将以加密方式存储在本地ioBroker实例中。\"},\n    \"autoQueryActivityOnTrigger\": {                   \"en\": \"Automatically query activity on device interaction\", \"de\": \"Fragen Sie die Aktivität bei Geräteinteraktion automatisch ab\", \"ru\": \"Автоматически запрашивать активность при взаимодействии с устройством\", \"pt\": \"Consultar automaticamente a atividade na interação do dispositivo\", \"nl\": \"Automatisch opvragen van activiteit bij apparaatinteractie\", \"fr\": \"Interroger automatiquement l'activité sur l'interaction de l'appareil\", \"it\": \"Interroga automaticamente l'attività sull'interazione del dispositivo\", \"es\": \"Consultar automáticamente la actividad en la interacción del dispositivo\", \"pl\": \"Automatycznie sprawdzaj aktywność związaną z interakcją urządzenia\", \"uk\": \"Автоматично надсилайте запит щодо взаємодії з пристроєм\", \"zh-cn\": \"自动查询设备交互活动\"},\n    \"autoQueryActivityOnTrigger_info\": {              \"en\": \"Please only enable if you really need activity information automatically!\", \"de\": \"Bitte nur aktivieren, wenn Sie Aktivitätsinformationen wirklich automatisch benötigen!\", \"ru\": \"Пожалуйста, включайте эту функцию только в том случае, если вам действительно нужна автоматическая информация об активности!\", \"pt\": \"Ative apenas se você realmente precisar de informações de atividades automaticamente!\", \"nl\": \"Schakel dit alleen in als u activiteitsinformatie echt automatisch nodig heeft!\", \"fr\": \"Veuillez l'activer uniquement si vous avez vraiment besoin automatiquement d'informations sur votre activité !\", \"it\": \"Abilitalo solo se hai veramente bisogno delle informazioni sull'attività automaticamente!\", \"es\": \"¡Habilítelo solo si realmente necesita información de actividad automáticamente!\", \"pl\": \"Włącz tylko wtedy, gdy naprawdę potrzebujesz automatycznie informacji o aktywności!\", \"uk\": \"Увімкніть, лише якщо вам дійсно потрібна інформація про активність автоматично!\", \"zh-cn\": \"请仅在您确实需要自动获取活动信息时启用！\"},\n    \"bespoken\": {                                     \"en\": \"Bespoken Virtual Device\",                          \"de\": \"Virtuelles Bespoken Gerät\",                        \"ru\": \"Виртуальное устройство Bespoken\",                  \"pt\": \"Dispositivo virtual Bespoken\",                     \"nl\": \"Bespoken virtueel apparaat\",                       \"fr\": \"Appareil virtuel Bespoken\",                        \"it\": \"Dispositivo virtuale Bespoken\",                    \"es\": \"Dispositivo virtual Bespoken\",                     \"pl\": \"Wirtualne urządzenie Bespoken\",                    \"uk\": \"Віртуальний пристрій на замовлення\",               \"zh-cn\": \"预定的虚拟设备\"},\n    \"bespokenToken\": {                                \"en\": \"Bespoken Validation Token\",                        \"de\": \"Bespoken Validation Token\",                        \"ru\": \"Bespoken Validation Token\",                        \"pt\": \"Token de validação para Bespoken\",                 \"nl\": \"Bespoken Validation Token\",                        \"fr\": \"Bespoken Validation Token\",                        \"it\": \"Bespoken Validation Token\",                        \"es\": \"Bespoken Validation Token\",                        \"pl\": \"Bespoken Validation Token\",                        \"uk\": \"Індивідуальний маркер перевірки\",                  \"zh-cn\": \"验证令牌\"},\n    \"bespoken_info\": {                                \"en\": \"The Bespoken Virtual Devices can be used to send text commands to you Amazon Account and gives limited control over your Devices - when possible using spoken words.<br/><br/>You need to create a Bespoken Account and then a Virtual Device to get a Bespoken Token as described on https://read.Bespoken.io/end-to-end/setup/ . Then you enter the token below and you will see new objects for the adapter.<br/><br/>Please note: Bespoken creates a audio file out of your text and sends this to the Alexa-APIs. So the understanding quality of Alexa is as good as this speech generation, so it may be problematic with special words.\", \"de\": \"Die Bespoken Virtual Devices können verwendet werden, um Textbefehle an Ihr Amazon-Konto zu senden und gibt Ihnen eine eingeschränkte Kontrolle über Ihre Geräte - wenn möglich mit gesprochenen Wörtern. <br/> <br/> Sie müssen ein Bespoken-Konto und dann ein virtuelles Gerät erstellen Holen Sie sich ein Bespoken-Token wie unter https://read.Bespoken.io/end-to-end/setup/ beschrieben. Dann geben Sie unten das Token ein und Sie sehen neue Objekte für den Adapter. <br/> <br/> Hinweis: Bespoken erstellt aus Ihrem Text eine Audiodatei und sendet diese an die Alexa-APIs. Also ist die Verständnisqualität von Alexa so gut wie diese Sprachgenerierung, so dass es mit speziellen Wörtern problematisch sein kann.\", \"ru\": \"Виртуальные устройства Bespoken можно использовать для отправки текстовых команд на вашу учетную запись Amazon и дает ограниченный контроль над вашими Устройствами - по возможности используя произносимые слова. <br/> <br/> Вам нужно создать учетную запись Bespoken, а затем виртуальное устройство для получить токен Bespoken, как описано на https://read.Bespoken.io/end-to-end/setup/. Затем вы вводите токен ниже, и вы увидите новые объекты для адаптера. <br/> <br/> Обратите внимание: Bespoken создает аудиофайл из вашего текста и отправляет его в Alexa-API. Таким образом, качество понимания Alexa так же хорошо, как и это поколение речи, поэтому это может быть проблематично со специальными словами.\", \"pt\": \"Os Dispositivos Virtuais da Bespoken podem ser usados ​​para enviar comandos de texto para sua Conta da Amazon e fornecer controle limitado sobre seus Dispositivos - quando possível, usando palavras faladas. <br/> <br/> Você precisa criar uma Conta Bespoken e, em seguida, um Dispositivo Virtual para obtenha um Token Bespoken conforme descrito em https://read.Bespoken.io/end-to-end/setup/. Em seguida, você insere o token abaixo e verá novos objetos para o adaptador. <br/> <br/> Observação: Bespoken cria um arquivo de áudio com o texto e o envia para as APIs do Alexa. Portanto, a qualidade de compreensão do Alexa é tão boa quanto essa geração de fala, por isso pode ser problemático com palavras especiais.\", \"nl\": \"De virtuele BESACT-apparaten kunnen worden gebruikt om tekstopdrachten naar uw Amazon-account te verzenden en geeft beperkte controle over uw apparaten - indien mogelijk met behulp van gesproken woorden. <br/> <br/> U moet eerst een Bespoken-account en vervolgens een virtueel apparaat maken een Bespoken-token krijgen zoals beschreven op https://read.Bespoken.io/end-toend/setup/. Vervolgens voert u de token hieronder in en ziet u nieuwe objecten voor de adapter. <br/> <br/> Let op: Bespoken maakt een audiobestand van uw tekst en stuurt dit naar de Alexa-API's. Dus de kenniskwaliteit van Alexa is net zo goed als deze spraakgeneratie, dus het kan problematisch zijn met speciale woorden.\", \"fr\": \"Les périphériques virtuels Bespoken peuvent être utilisés pour envoyer des commandes de texte à votre compte Amazon et donner un contrôle limité sur vos périphériques - si possible en utilisant des mots prononcés. <br/> <br/> Vous devez créer un compte Bespoken puis un périphérique virtuel pour obtenir un jeton Bespoken comme décrit sur https://read.Bespoken.io/end-to-end/setup/. Ensuite, vous entrez le jeton ci-dessous et vous verrez de nouveaux objets pour l'adaptateur. <br/> <br/> Remarque: Bespoken crée un fichier audio à partir de votre texte et l'envoie aux API Alexa. Donc, la qualité de la compréhension d’Alex est aussi bonne que cette génération de discours, donc il peut être problématique avec des mots spéciaux.\", \"it\": \"I dispositivi virtuali Bespoken possono essere utilizzati per inviare comandi di testo al tuo account Amazon e forniscono un controllo limitato sui tuoi dispositivi - quando possibile usando parole parlate. <br/> <br/> Devi creare un account Bespoken e poi un dispositivo virtuale per ottenere un token Bespoken come descritto su https://read.Bespoken.io/end-to-end/setup/. Quindi inserisci il token qui sotto e vedrai nuovi oggetti per l'adattatore. <br/> <br/> Nota: Bespoken crea un file audio dal tuo testo e lo invia alle API Alexa. Quindi la qualità della comprensione di Alexa è buona quanto questa generazione di parlato, quindi potrebbe essere problematica con parole speciali.\", \"es\": \"Los dispositivos virtuales Bespoken se pueden utilizar para enviar comandos de texto a su cuenta de Amazon y le dan un control limitado sobre sus dispositivos, cuando sea posible utilizando palabras habladas. <br/> <br/> Usted necesita crear una cuenta de Bespoken y luego un dispositivo virtual para obtener un token Bespoken como se describe en https://read.Bespoken.io/end-to-end/setup/. A continuación, ingrese el token a continuación y verá nuevos objetos para el adaptador. <br/> <br/> Tenga en cuenta: Bespoken crea un archivo de audio de su texto y lo envía a las API de Alexa. Entonces, la calidad de comprensión de Alexa es tan buena como esta generación de habla, por lo que puede ser problemático con palabras especiales.\", \"pl\": \"Urządzenia wirtualne Bespoken mogą być używane do wysyłania poleceń tekstowych do konta Amazon i dają ograniczoną kontrolę nad urządzeniami - w miarę możliwości za pomocą wypowiadanych słów. <br/> <br/> Musisz utworzyć konto Bespoken, a następnie urządzenie wirtualne do zdobądź token Bespoken zgodnie z opisem na https://read.Bespoken.io/end-to-end/setup/. Następnie wpisz poniższy token, a zobaczysz nowe obiekty dla adaptera. <br/> <br/> Uwaga: Bespoken tworzy plik audio poza tekstem i wysyła go do Alexa-API. Zatem jakość rozumienia Alexy jest tak dobra, jak ta generacja mowy, więc może być problematyczne w przypadku specjalnych słów.\", \"uk\": \"Віртуальні пристрої Bespoken можна використовувати для надсилання текстових команд до вашого облікового запису Amazon і надають обмежений контроль над вашими пристроями, якщо це можливо, за допомогою голосових слів.<br/><br/>Вам потрібно створити обліковий запис Bespoken, а потім віртуальний пристрій, щоб отримати Bespoken Token, як описано на https://read.Bespoken.io/end-to-end/setup/. Потім ви вводите маркер нижче, і ви побачите нові об’єкти для адаптера.<br/><br/>Зверніть увагу: Bespoken створює аудіофайл із вашого тексту та надсилає його до Alexa-API. Таким чином, якість розуміння Alexa така ж хороша, як і це покоління мовлення, тому це може бути проблематично зі спеціальними словами.\", \"zh-cn\": \"Bespoken虚拟设备可用于向您的Amazon帐户发送文本命令，并在可能的情况下使用口语对您的设备进行有限的控制。<br/> <br/>您需要先创建Bespoken帐户，然后创建虚拟设备才能使用\"},\n    \"cookie\": {                                       \"en\": \"Cookie\",                                           \"de\": \"Cookie\",                                           \"ru\": \"Cookie\",                                           \"pt\": \"Biscoito\",                                         \"nl\": \"Cookie\",                                           \"fr\": \"Cookie\",                                           \"it\": \"Cookie\",                                           \"es\": \"Cookie\",                                           \"pl\": \"Cookie\",                                           \"uk\": \"Печиво\",                                           \"zh-cn\": \"曲奇饼\"},\n    \"cookieLoginUrl\": {                               \"en\": \"Custom Cookie-Amazon-URL\",                         \"de\": \"Eigene Cookie-Amazon-URL\",                         \"ru\": \"Пользовательский URL-адрес Cookie-Amazon\",         \"pt\": \"Cookie personalizado-Amazon-URL\",                  \"nl\": \"Aangepaste Cookie-Amazon-URL\",                     \"fr\": \"Cookie personnalisé-Amazon-URL\",                   \"it\": \"Cookie personalizzato-Amazon-URL\",                 \"es\": \"Cookie personalizada-Amazon-URL\",                  \"pl\": \"Niestandardowy plik cookie-Amazon-URL\",            \"uk\": \"Користувацька URL-адреса файлів cookie Amazon\",    \"zh-cn\": \"自定义Cookie-Amazon-URL\"},\n    \"device_settings\": {                              \"en\": \"Device Settings\",                                  \"de\": \"Geräteeinstellungen\",                              \"ru\": \"Настройки устройства\",                             \"pt\": \"Configurações do dispositivo\",                     \"nl\": \"Apparaat instellingen\",                            \"fr\": \"Réglages de l'appareil\",                           \"it\": \"Impostazioni del dispositivo\",                     \"es\": \"Configuración de dispositivo\",                     \"pl\": \"Ustawienia urządzenia\",                            \"uk\": \"Налаштування пристрою\",                            \"zh-cn\": \"设备设置\"},\n    \"device_settings_info\": {                         \"en\": \"Settings for the devices that are fetched from Amazon\", \"de\": \"Einstellungen für die Geräte, die von Amazon abgerufen werden\", \"ru\": \"Настройки для устройств, которые извлекаются из Amazon\", \"pt\": \"Configurações para os dispositivos que são buscados na Amazon\", \"nl\": \"Instellingen voor de apparaten die van Amazon worden opgehaald\", \"fr\": \"Paramètres des appareils récupérés sur Amazon\",    \"it\": \"Impostazioni per i dispositivi recuperati da Amazon\", \"es\": \"Configuración para los dispositivos que se obtienen de Amazon\", \"pl\": \"Ustawienia urządzeń pobieranych z Amazon\",         \"uk\": \"Налаштування для пристроїв, отриманих з Amazon\",   \"zh-cn\": \"从亚马逊获取的设备的设置\"},\n    \"history\": {                                      \"en\": \"History Settings\",                                 \"de\": \"Verlaufseinstellungen\",                            \"ru\": \"Настройки истории\",                                \"pt\": \"Configurações de histórico\",                       \"nl\": \"Geschiedenis instellingen\",                        \"fr\": \"Paramètres d'historique\",                          \"it\": \"Impostazioni cronologia\",                          \"es\": \"Configuración del historial\",                      \"pl\": \"Ustawienia historii\",                              \"uk\": \"Налаштування історії\",                             \"zh-cn\": \"历史记录设置\"},\n    \"historyIgnoreEmptySummary\": {                    \"en\": \"Ignore History-Entries when no command was recognized\", \"de\": \"Verlaufseinträge ignorieren, wenn kein Befehl erkannt wurde\", \"ru\": \"Игнорировать записи истории, в которых не была распознана команда\", \"pt\": \"Ignorar entradas de histórico onde nenhum comando foi reconhecido\", \"nl\": \"Negeer geschiedenis-items waar geen commando werd herkend\", \"fr\": \"Ignorer les entrées d'historique où aucune commande n'a été reconnue\", \"it\": \"Ignora voci di cronologia in cui non è stato riconosciuto alcun comando\", \"es\": \"Ignorar entradas de historial donde no se reconoció ningún comando\", \"pl\": \"Ignoruj wpisy historii, w których nie rozpoznano żadnego polecenia\", \"uk\": \"Ігнорувати записи історії, якщо жодна команда не була розпізнана\", \"zh-cn\": \"忽略未识别命令的历史条目\"},\n    \"history_info\": {                                 \"en\": \"Settings for History data that are available in the \\\"History\\\" states\", \"de\": \"Einstellungen für Verlaufsdaten, die in den \\\"History\\\" Zuständen verfügbar sind\", \"ru\": \"Настройки для данных истории, которые доступны в состояниях \\\"History\\\"\", \"pt\": \"Configurações para dados de histórico que estão disponíveis nos estados \\\"History\\\"\", \"nl\": \"Instellingen voor geschiedenisgegevens die beschikbaar zijn in de status \\\"History\\\"\", \"fr\": \"Paramètres des données d'historique disponibles dans les états \\\"History\\\"\", \"it\": \"Impostazioni per i dati della cronologia disponibili negli stati \\\"History\\\"\", \"es\": \"Configuración de los datos del historial que están disponibles en los estados \\\"History\\\"\", \"pl\": \"Ustawienia danych historii, które są dostępne w stanach „History”\", \"uk\": \"Налаштування для даних історії, доступних у станах «Історія».\", \"zh-cn\": \"在“历史记录”状态下可用的历史记录数据设置\"},\n    \"includeAppDevices\": {                            \"en\": \"Query and add also App-Devices\",                   \"de\": \"Auch App-Devices abfragen und hinzufügen\",         \"ru\": \"Запросите и добавьте также App-Devices\",           \"pt\": \"Consulte e adicione também App-Devices\",           \"nl\": \"Vraag en voeg ook app-apparaten toe\",              \"fr\": \"Interroger et ajouter également des appareils d'application\", \"it\": \"Interroga e aggiungi anche App-Devices\",           \"es\": \"Consultar y agregar también App-Devices\",          \"pl\": \"Zapytanie i dodawanie również App-Devices\",        \"uk\": \"Зробіть запит і додайте також App-Devices\",        \"zh-cn\": \"查询并添加 App-Devices\"},\n    \"interval\": {                                     \"en\": \"Data update interval\",                             \"de\": \"Datenaktualisierungsintervall\",                    \"ru\": \"Интервалы обновления данных\",                      \"pt\": \"Intervalos de atualização de dados\",               \"nl\": \"Gegevens update-intervallen\",                      \"fr\": \"Intervalles de mise à jour\",                       \"it\": \"Intervalli di aggiornamento dei dati\",             \"es\": \"Intervalos de actualización de datos\",             \"pl\": \"Interwały aktualizacji danych\",                    \"uk\": \"Інтервал оновлення даних\",                         \"zh-cn\": \"数据更新间隔\"},\n    \"interval_info\": {                                \"en\": \"Several data can be updated automatically. You define here in seconds how often this should happen. Using the state history.#trigger you can always update the data manually. If you set 0 as interval the automatic update is disabled. \", \"de\": \"Bestimmte Daten können automatisch aktualisiert werden. Wie oft dies passiert, wird hier in Sekunden ferstgelegt. Über den Datenunkt history.#trigger kann jederzeit manuell eine Aktualisierung angestossen werden. Die Angabe von 0 deaktiviert die automatischen Aktualisierungen. \", \"ru\": \"Несколько данных могут автоматически обновляться. Вы определяете здесь в секундах, как часто это должно произойти. Используя историю состояний. # Триггер, вы всегда можете обновлять данные вручную. Если вы установите 0 как интервал, автоматическое обновление отключено.\", \"pt\": \"Vários dados podem ser atualizados automaticamente. Você define aqui em segundos quantas vezes isso deve acontecer. Usando o histórico de estados. # Trigger, você sempre pode atualizar os dados manualmente. Se você definir 0 como intervalo, a atualização automática será desativada.\", \"nl\": \"Verschillende gegevens kunnen automatisch worden bijgewerkt. Je definieert hier in seconden hoe vaak dit moet gebeuren. Door de # trigger te gebruiken, kunt u de gegevens altijd handmatig bijwerken. Als u 0 instelt als interval, is de automatische update uitgeschakeld.\", \"fr\": \"Plusieurs données peuvent être automatiquement mises à jour. Vous définissez ici en secondes combien de fois cela devrait arriver. En utilisant l'historique d'état. # Trigger, vous pouvez toujours mettre à jour les données manuellement. Si vous définissez 0 comme intervalle, la mise à jour automatique est désactivée.\", \"it\": \"Diversi dati possono essere aggiornati automaticamente. Definisci qui in secondi quanto spesso ciò dovrebbe accadere. Utilizzando la cronologia dello stato. # Trigger puoi sempre aggiornare i dati manualmente. Se si imposta 0 come intervallo, l'aggiornamento automatico è disabilitato.\", \"es\": \"Varios datos se pueden actualizar automáticamente. Usted define aquí en segundos con qué frecuencia esto debería suceder. Con el disparador de historial de estado. #, Siempre puede actualizar los datos manualmente. Si configura 0 como intervalo, la actualización automática está deshabilitada.\", \"pl\": \"Kilka danych może być automatycznie aktualizowanych. Tutaj określasz w sekundach, jak często powinno to mieć miejsce. Korzystając z historii stanów. # Trigger zawsze możesz zaktualizować dane ręcznie. Jeśli ustawisz 0 jako interwał, aktualizacja automatyczna zostanie wyłączona.\", \"uk\": \"Деякі дані можуть оновлюватися автоматично. Ви визначаєте тут за секунди, як часто це має відбуватися. Використовуючи історію стану.#trigger, ви завжди можете оновити дані вручну. Якщо ви встановите 0 як інтервал, автоматичне оновлення буде вимкнено.\", \"zh-cn\": \"几个数据可以自动更新。\"},\n    \"overwrite\": {                                    \"en\": \"Override special parameter for cookie determination and communication\", \"de\": \"Überschreiben spezieller Parameter zur Cookie-Ermittlung und Kommunikation\", \"ru\": \"Переопределить специальный параметр для определения и связи файлов cookie\", \"pt\": \"Substituir parâmetro especial para determinação e comunicação de cookies\", \"nl\": \"Speciale parameter voor cookie-bepaling en communicatie overschrijven\", \"fr\": \"Remplacer le paramètre spécial pour la détermination des cookies et la communication\", \"it\": \"Sostituisci i parametri speciali per la determinazione e la comunicazione dei cookie\", \"es\": \"Anula el parámetro especial para la determinación y comunicación de cookies\", \"pl\": \"Zastąp specjalny parametr do określania i komunikacji plików cookie\", \"uk\": \"Перевизначити спеціальний параметр для визначення та передачі файлів cookie\", \"zh-cn\": \"覆盖用于cookie确定和通信的特殊参数\"},\n    \"overwrite_info\": {                               \"en\": \"By default the parameters are set to connect with a german Amazon account. If your account is located in an other Amazon country you need to enter the correct details here. Check in Adapter README for some details. Also in certain cases when the automatic cookie determination is failing it can help to play around here especially with the Accept-Language and user-agent settings.\", \"de\": \"Standardmäßig verbindet sich der Adapter mit einem deutschen Amazon-Konto. Bei der Nutzung von Accounts aus anderen Amazon-Ländern müssen hier die korrekten Daten eingetragen werden. Einige Hinweise dazu sind in der Adapter-README zu finden. Auch in speziellen Fällen, wenn die automatische Cookie-Ermittlung fehlschlägt, kann es helfen die Felder User-Agent und Accept-Language anzupassen.\", \"ru\": \"По умолчанию для параметров установлено соединение с немецкой учетной записью amazon. Если вы используете другие страны амазонки, вам нужно установить правильные данные здесь. За дополнительной информацией обратитесь к адаптеру README. Также в некоторых случаях, когда автоматическая дефрагментация файлов cookie не работает, она может помочь здесь играть особенно с Accept-Language и user-agent.\", \"pt\": \"Por padrão, os parâmetros são configurados para se conectar a uma conta da Amazon na Alemanha. Se você estiver usando outros países amazônicos, precisará definir os dados corretos aqui. Verifique no Adaptador README para alguns detalhes. Também em certos casos, quando a determinação automática de cookies está falhando, pode ajudar a brincar aqui, especialmente com Accept-Language e user-agent.\", \"nl\": \"Standaard zijn de parameters ingesteld om verbinding te maken met een Duits Amazon-account. Als u andere Amazon-landen gebruikt, moet u hier de juiste gegevens instellen. Raadpleeg de README van de adapter voor meer informatie. Ook in bepaalde gevallen waar de automatische cookie-bepaling faalt, kan het helpen om hier te spelen, vooral met Accept-Language en user-agent.\", \"fr\": \"Par défaut, les paramètres sont définis pour se connecter avec un compte amazon allemand. Si vous utilisez d'autres pays amazon, vous devez définir les données correctes ici. Vérifiez l'adaptateur README pour plus de détails. Dans certains cas, lorsque la détermination automatique des cookies échoue, cela peut aider à jouer ici, en particulier avec Accept-Language et user-agent.\", \"it\": \"Di default i parametri sono impostati per connettersi con un account tedesco di Amazon. Se stai utilizzando altri paesi di Amazon, devi impostare qui i dati corretti. Controllare in Adapter README per alcuni dettagli. Inoltre, in alcuni casi in cui la determinazione automatica dei cookie non funziona, può aiutare a giocare qui, specialmente con Accept-Language e user-agent.\", \"es\": \"Por defecto, los parámetros están configurados para conectarse con una cuenta amazon alemana. Si está utilizando otros países amazónicos, debe establecer los datos correctos aquí. Verifique en el adaptador README para algunos detalles. También en algunos casos, la determinación automática de cookies está fallando, puede ayudar a jugar aquí especialmente con Accept-Language y User-Agent.\", \"pl\": \"Domyślnie parametry są ustawione na połączenie z niemieckim kontem Amazon. Jeśli korzystasz z innych krajów amazon, musisz ustawić poprawne dane tutaj. Sprawdź w pliku README na temat niektórych szczegółów. Również w niektórych przypadkach, gdy automatyczne określanie ciasteczek zawodzi, może pomóc w zabawie, szczególnie w Accept-Language i user-agent.\", \"uk\": \"За замовчуванням параметри встановлені для підключення до німецького облікового запису Amazon. Якщо ваш обліковий запис знаходиться в іншій країні Amazon, вам потрібно ввести тут правильні дані. Перевірте файл README адаптера, щоб отримати деякі відомості. Крім того, у деяких випадках, коли автоматичне визначення файлів cookie не вдається, може допомогти пограти тут, особливо з налаштуваннями Accept-Language і user-agent.\", \"zh-cn\": \"默认情况下，参数设置为与德国亚马逊帐户关联。\"},\n    \"password\": {                                     \"en\": \"Password\",                                         \"de\": \"Passwort\",                                         \"ru\": \"Пароль\",                                           \"pt\": \"Senha\",                                            \"nl\": \"wachtwoord\",                                       \"fr\": \"mot de passe\",                                     \"it\": \"parola d'ordine\",                                  \"es\": \"contraseña\",                                       \"pl\": \"hasło\",                                            \"uk\": \"Пароль\",                                           \"zh-cn\": \"密码\"},\n    \"proxy\": {                                        \"en\": \"Proxy Settings\",                                   \"de\": \"Proxy-Einstellungen\",                              \"ru\": \"Настройки прокси\",                                 \"pt\": \"Configurações de proxy\",                           \"nl\": \"Proxy instellingen\",                               \"fr\": \"Paramètres du proxy\",                              \"it\": \"Impostazioni proxy\",                               \"es\": \"Configuración de proxy\",                           \"pl\": \"Ustawienia proxy\",                                 \"uk\": \"Налаштування проксі\",                              \"zh-cn\": \"代理设置\"},\n    \"proxyListenBind\": {                              \"en\": \"Proxy Binding to certain IP\",                      \"de\": \"Proxy-Beschränkung auf folgende IP (Listen Bind)\", \"ru\": \"Связывание прокси-сервера с определенным IP-адресом\", \"pt\": \"Ligação Proxy a determinados IPs\",                 \"nl\": \"Proxy-binding aan bepaalde IP\",                    \"fr\": \"Liaison proxy à certaines adresses IP\",            \"it\": \"Collegamento a determinati indirizzi IP\",          \"es\": \"Enlazado Proxy a cierta IP\",                       \"pl\": \"Wiązanie proxy do określonego adresu IP\",          \"uk\": \"Прив'язка проксі до певного IP\",                   \"zh-cn\": \"代理绑定到某些IP\"},\n    \"proxyOverrideIp\": {                              \"en\": \"External Container-IP (Docker)\",                   \"de\": \"Externe Container-IP (Docker)\",                    \"ru\": \"Внешний контейнер-IP (Docker)\",                    \"pt\": \"Container-IP Externo (Docker)\",                    \"nl\": \"External Container-IP (Docker)\",                   \"fr\": \"IP de conteneur externe (Docker)\",                 \"it\": \"External Container-IP (Docker)\",                   \"es\": \"Contenedor externo-IP (Docker)\",                   \"pl\": \"Zewnętrzny kontener-IP (Docker)\",                  \"uk\": \"Зовнішній контейнер-IP (Docker)\",                  \"zh-cn\": \"外部容器-IP（Docker）\"},\n    \"proxyOwnIp\": {                                   \"en\": \"Own IP or hostname to use in proxy link\",          \"de\": \"Eigene IP oder Hostname für den Proxy-Link\",       \"ru\": \"Собственный IP-адрес или имя хоста для использования в прокси-ссылке\", \"pt\": \"IP ou nome de host próprio para usar no link do proxy\", \"nl\": \"Eigen IP of hostnaam om te gebruiken in proxy-koppeling\", \"fr\": \"Propre IP ou nom d'hôte à utiliser dans un lien de proxy\", \"it\": \"Proprio IP o nome host da utilizzare nel collegamento proxy\", \"es\": \"IP propia o nombre de host para usar en el enlace proxy\", \"pl\": \"Własny adres IP lub nazwa hosta do użycia w łączu proxy\", \"uk\": \"Власна IP-адреса або ім’я хоста для використання в проксі-посиланні\", \"zh-cn\": \"在代理链接中使用的自己的IP或主机名\"},\n    \"proxyPort\": {                                    \"en\": \"Proxy-Port\",                                       \"de\": \"Proxy-Port\",                                       \"ru\": \"Порт прокси\",                                      \"pt\": \"Porta proxy\",                                      \"nl\": \"Proxy-Port\",                                       \"fr\": \"Port proxy\",                                       \"it\": \"Porta proxy\",                                      \"es\": \"Puerto proxy\",                                     \"pl\": \"Port proxy\",                                       \"uk\": \"Проксі-порт\",                                      \"zh-cn\": \"代理端口\"},\n    \"proxyPort_info\": {                               \"en\": \"(0 = random port)\",                                \"de\": \"(0 = zufälliger Port)\",                            \"ru\": \"(0 = случайный порт)\",                             \"pt\": \"(0 = porta aleatória)\",                            \"nl\": \"(0 = willekeurige poort)\",                         \"fr\": \"(0 = port aléatoire)\",                             \"it\": \"(0 = porta casuale)\",                              \"es\": \"(0 = puerto aleatorio)\",                           \"pl\": \"(0 = losowy port)\",                                \"uk\": \"(0 = випадковий порт)\",                            \"zh-cn\": \"（0 =随机端口）\"},\n    \"proxy_info\": {                                   \"en\": \"If the automatic determination of a cookie was not possible, the adapter opens a proxy to allow a manual login to amazon. The Logfile and Admin screen will tell you the URL to use then. Open this in your browser, manually login into Amazon, solve all potential Captchas or Two-Factor-Authentications and the cookie is catched automatically in the background if successful.\", \"de\": \"Wenn die automatische Cookie-Ermittlung nicht funktioniert öffnet der Adapter einen Proxy um einen manuellen Login zu erlauben. Das Logfile und die Admin-Seite zeigt in dem Fall den aufzurufenden Link an. Diese mit dem Browser aufrufen, bei Amazon einloggen, alle potentiell angezeigten Captchas und Zwei-Faktor-Abfragen beantworten und das Cookie wird bei Erfolg im Hintergrund automatisch ermittelt.\", \"ru\": \"Если автоматическое определение файла cookie не позволяет адаптеру открыть прокси-сервер, чтобы разрешить вход в систему вручную для амазонки. Затем в файле журнала будет указан URL-адрес. Откройте это в своем браузере, вручную войдите в Amazon, разрешите все потенциальные Captchas или Two-Factor-Authentication, и cookie автоматически зацепится в фоновом режиме, если успешно.\", \"pt\": \"Se a determinação automática de um cookie não for capaz, o adaptador abre um proxy para permitir um login manual na amazon. O arquivo de registro irá dizer-lhe o URL para usar então. Abra isso no seu navegador, faça login manualmente na Amazon, resolva todos os Captchas ou Autenticações de dois fatores em potencial e o cookie será capturado em segundo plano automaticamente se for bem-sucedido.\", \"nl\": \"Als de automatische vaststelling van een cookie niet mogelijk is, opent de adapter een proxy om handmatig inloggen bij Amazon mogelijk te maken. Het logbestand geeft u de URL die u vervolgens moet gebruiken. Open dit in uw browser, log handmatig in bij Amazon, los alle mogelijke Captchas of Two-Factor-Authenticaties op en de cookie wordt automatisch in de achtergrond gevangen als het succesvol is.\", \"fr\": \"Si la détermination automatique d'un cookie n'est pas possible, l'adaptateur ouvre un proxy pour autoriser une connexion manuelle à amazon. Le fichier journal vous indiquera l'URL à utiliser ensuite. Ouvrez-le dans votre navigateur, connectez-vous manuellement à Amazon, résolvez tous les captcha potentiels ou les authentifications à deux facteurs et le cookie est automatiquement récupéré en arrière-plan en cas de succès.\", \"it\": \"Se la determinazione automatica di un cookie non è possibile, l'adattatore apre un proxy per consentire l'accesso manuale ad Amazon. Il file di registro ti indicherà l'URL da utilizzare in quel momento. Apri questo nel tuo browser, accedi manualmente ad Amazon, risolvi tutti i potenziali Captcha o Autenticazione a due fattori e il cookie viene automaticamente catturato in background in caso di successo.\", \"es\": \"Si la determinación automática de una cookie no puede, el adaptador abre un proxy para permitir un inicio de sesión manual en Amazon. El archivo de registro le dirá la URL que debe usar. Abra esto en su navegador, inicie sesión manualmente en Amazon, resuelva todas las posibles Captchas o Autenticaciones de dos factores y la cookie queda atrapada en segundo plano automáticamente si tiene éxito.\", \"pl\": \"Jeśli automatyczne określenie pliku cookie nie jest możliwe, adapter otwiera proxy, aby umożliwić ręczne logowanie do Amazon. Logfile wskaże ci adres URL, którego będziesz używał. Otwórz to w przeglądarce, ręcznie zaloguj się do Amazon, rozwiąż wszystkie potencjalne Captchas lub uwierzytelnienia dwuetapowe, a ciasteczko zostanie automatycznie przechwycone w tle, jeśli się powiedzie.\", \"uk\": \"Якщо автоматичне визначення файлу cookie неможливо, адаптер відкриває проксі, щоб дозволити ручний вхід в amazon. Екран журналу та адміністратора підкаже URL-адресу для використання. Відкрийте це у своєму браузері, увійдіть в Amazon вручну, виконайте всі потенційні Captcha або двофакторну автентифікацію, і файл cookie буде перехоплено автоматично у фоновому режимі, якщо це вдасться.\", \"zh-cn\": \"如果无法自动确定Cookie，则适配器将打开代理以允许手动登录到亚马逊。\"},\n    \"push_connection\": {                              \"en\": \"Push-Connection\",                                  \"de\": \"Push-Verbindung\",                                  \"ru\": \"Push-соединение\",                                  \"pt\": \"Conexão Push\",                                     \"nl\": \"Push-aansluiting\",                                 \"fr\": \"Push-Connection\",                                  \"it\": \"Push-Connection\",                                  \"es\": \"Push-Connection\",                                  \"pl\": \"Push-Connection\",                                  \"uk\": \"Push-Connection\",                                  \"zh-cn\": \"推式连接\"},\n    \"push_connection_info\": {                         \"en\": \"With the direct push connection the changes from the Alexa devices are notified to the adapter and are used to update only needed data in near real time. If enabled some data update intervals below are not used. It is Urgently recommended to use the push connection to limit the data requested from the amazon cloud!\", \"de\": \"Mit der direkten Push-Verbindung werden die Änderungen von den Alexa-Geräten an den Adapter gemeldet und verwendet, um nur benötigte Daten in naher Echtzeit zu aktualisieren. Wenn einige aktivierte Daten-Update-Intervalle unten nicht verwendet werden. Es wird dringend empfohlen, die Push-Verbindung zu verwenden, um die von der Amazon-Cloud angeforderten Daten zu begrenzen!\", \"ru\": \"При прямом подключении к адаптеру оповещены изменения устройств Alexa и используются для обновления только необходимых данных в ближайшем режиме реального времени. Если включены некоторые интервалы обновления данных ниже не используются. Срочно рекомендуется использовать push-соединение для ограничения данных, запрашиваемых из облака amazon!\", \"pt\": \"Com a conexão de push direta, as alterações dos dispositivos Alexa são notificadas ao adaptador e são usadas para atualizar apenas dados necessários em tempo real. Se ativado alguns intervalos de atualização de dados abaixo não são usados. Recomenda-se urgentemente usar a conexão de push para limitar os dados solicitados da nuvem de amazon!\", \"nl\": \"Met de directe verbinding die de veranderingen van de Alexa apparatuur zijn geïnformeerd aan de adapter en worden gebruikt om gegevens te updaten in de echte tijd. Als een aantal data updates beneden niet gebruikt worden. Het is Urgently aanbevolen de persverbinding te gebruiken om de gegevens te beperken van de Amazonewolk!\", \"fr\": \"Avec la connexion push directe les changements des appareils Alexa sont notifiés à l'adaptateur et sont utilisés pour mettre à jour seulement les données nécessaires en temps réel. Si activé certains intervalles de mise à jour de données ci-dessous ne sont pas utilisés. Il est urgent d'utiliser la connexion push pour limiter les données demandées sur le nuage amazon!\", \"it\": \"Con la connessione a spinta diretta le modifiche dei dispositivi Alexa vengono notificate all'adattatore e vengono utilizzate per aggiornare solo i dati necessari in tempo reale. Se abilitato alcuni intervalli di aggiornamento dati di seguito non vengono utilizzati. È urgentemente consigliato utilizzare la connessione push per limitare i dati richiesti dal cloud amazon!\", \"es\": \"Con la conexión de empuje directo los cambios de los dispositivos Alexa se notifican al adaptador y se utilizan para actualizar sólo los datos necesarios en tiempo real cercano. Si se activan algunos intervalos de actualización de datos a continuación no se utilizan. Se recomienda urgentemente utilizar la conexión push para limitar los datos solicitados desde la nube amazon!\", \"pl\": \"Dzięki bezpośredniemu połączeniu zmian w urządzeniach Aleksy są powiadomione do adaptera i są wykorzystywane do aktualizacji potrzebnych danych w czasie rzeczywistym. Jeśli uaktualnione dane nie są używane. Zaleca się użycie połączenia naciskowego w celu ograniczenia danych żądanych przez niebieską chmurę!\", \"uk\": \"За допомогою прямого push-з’єднання про зміни з пристроїв Alexa повідомляється адаптер і використовується для оновлення лише необхідних даних майже в реальному часі. Якщо ввімкнено, деякі наведені нижче інтервали оновлення даних не використовуються. Настійно рекомендується використовувати push-з’єднання, щоб обмежити дані, які запитуються з хмари Amazon!\", \"zh-cn\": \"由于直接推动了阿克拉装置的改变,已通知适应者,并且仅用于不久的实时更新必要的数据。 如果无法使用下文的一些数据更新间隔。 令人急切地建议利用推动联系来限制从马纳西地区的数据!\"},\n    \"resetCookie\": {                                  \"en\": \"Reset Amazon Cookies\",                             \"de\": \"Amazon-Cookies zurücksetzen\",                      \"ru\": \"Сброс Amazon Cookies\",                             \"pt\": \"Redefinir cookies da Amazon\",                      \"nl\": \"Reset Amazon-cookies\",                             \"fr\": \"Réinitialiser les cookies Amazon\",                 \"it\": \"Reimposta i cookie Amazon\",                        \"es\": \"Restablecer las cookies de Amazon\",                \"pl\": \"Zresetuj ciasteczka Amazon\",                       \"uk\": \"Скинути файли cookie Amazon\",                      \"zh-cn\": \"重置Amazon Cookies\"},\n    \"s (0 = disabled)\": {                             \"en\": \"s (0 = disabled)\",                                 \"de\": \"s (0 = deaktiviert)\",                              \"ru\": \"сек (0 = отключено)\",                              \"pt\": \"s (0 = desativado)\",                               \"nl\": \"s (0 = uitgeschakeld)\",                            \"fr\": \"s (0 = désactivé)\",                                \"it\": \"s (0 = disabilitato)\",                             \"es\": \"s (0 = deshabilitado)\",                            \"pl\": \"s (0 = wyłączone)\",                                \"uk\": \"s (0 = вимкнено)\",                                 \"zh-cn\": \"s（0 =禁用）\"},\n    \"sync_settings\": {                                \"en\": \"Synchronization Settings\",                         \"de\": \"Synchronisierungseinstellungen\",                   \"ru\": \"Настройки синхронизации\",                          \"pt\": \"Configurações de sincronização\",                   \"nl\": \"Synchronisatie-instellingen\",                      \"fr\": \"Paramètres de synchronisation\",                    \"it\": \"Impostazioni di sincronizzazione\",                 \"es\": \"Configuración de sincronización\",                  \"pl\": \"Ustawienia synchronizacji\",                        \"uk\": \"Параметри синхронізації\",                          \"zh-cn\": \"同步设置\"},\n    \"sync_settings_info\": {                           \"en\": \"Specify which data should be synced from the Amazon Cloud to the ioBroker instance. Please only enable the data you really need!\", \"de\": \"Definition welche Daten von der Amazon Cloud zur ioBroker-Instanz synchronisiert werden sollen. Bitte nur die Daten aktivieren, die wirklich benötigt werden!\", \"ru\": \"Укажите, какие данные должны быть синхронизированы из Amazon Cloud в экземпляр ioBroker. Пожалуйста, включите данные, которые вам действительно нужны!\", \"pt\": \"Especifique quais dados devem ser sincronizados da Amazon Cloud para a instância do ioBroker. Por favor, apenas ative os dados que você realmente precisa!\", \"nl\": \"Vermeld welke gegevens moeten worden gesynchroniseerd van de Amazone Cloud naar het ioBroker instance. Bevestig de gegevens die je echt nodig hebt!\", \"fr\": \"Préciser quelles données doivent être synchronisées depuis Amazon Cloud jusqu'à l'instance ioBroker. Veuillez uniquement activer les données dont vous avez vraiment besoin !\", \"it\": \"Specificare quali dati dovrebbero essere sincronizzati da Amazon Cloud all'istanza ioBroker. Si prega di abilitare solo i dati di cui hai veramente bisogno!\", \"es\": \"Especifique qué datos deben ser sincronizados desde la nube amazónica a la instancia ioBroker. Por favor, sólo active los datos que realmente necesita!\", \"pl\": \"Wskazuje się, że dane powinny być syndykowane z Amazon Cloud na przykład ioBrokera. Pozwoliło na to tylko dane, które potrzebują!\", \"uk\": \"Укажіть, які дані потрібно синхронізувати з Amazon Cloud до екземпляра ioBroker. Увімкніть лише ті дані, які вам дійсно потрібні!\", \"zh-cn\": \"数据应从亚马逊克尔集到奥布罗克。 只请你真正需要!\"},\n    \"synchronizeLists\": {                             \"en\": \"Synchronize Lists (e.g. Todo)\",                    \"de\": \"Listen synchronisieren (z. B. Todo)\",              \"ru\": \"Синхронизируйте списки (например, Todo)\",          \"pt\": \"Sincronizar listas (por exemplo, Todo)\",           \"nl\": \"Lijsten synchroniseren (bijv. Todo)\",              \"fr\": \"Synchroniser les listes (par exemple Todo)\",       \"it\": \"Sincronizza elenchi (ad es. Todo)\",                \"es\": \"Sincronizar listas (por ejemplo, Todo)\",           \"pl\": \"Synchronizuj listy (np. Todo)\",                    \"uk\": \"Синхронізація списків (наприклад, Todo)\",          \"zh-cn\": \"同步列表（例如 Todo）\"},\n    \"synchronizeSmartHomeDevices\": {                  \"en\": \"Synchronize Smart Home Devices\",                   \"de\": \"Smart-Home-Geräte synchronisieren\",                \"ru\": \"Синхронизируйте устройства умного дома\",           \"pt\": \"Sincronizar dispositivos de casa inteligente\",     \"nl\": \"Synchroniseer Smart Home-apparaten\",               \"fr\": \"Synchroniser les appareils domestiques intelligents\", \"it\": \"Sincronizza i dispositivi Smart Home\",             \"es\": \"Sincronizar dispositivos domésticos inteligentes\", \"pl\": \"Synchronizuj inteligentne urządzenia domowe\",      \"uk\": \"Синхронізація пристроїв Smart Home\",               \"zh-cn\": \"同步智能家居设备\"},\n    \"updateConfigurationInterval\": {                  \"en\": \"Device Configuration update interval\",             \"de\": \"Aktualisierungsintervall der Gerätekonfiguration\", \"ru\": \"Интервал обновления конфигурации устройства\",      \"pt\": \"Intervalo de atualização da configuração do dispositivo\", \"nl\": \"Update-interval apparaatconfiguratie\",             \"fr\": \"Intervalle de mise à jour de la configuration de l'appareil\", \"it\": \"Intervallo di aggiornamento della configurazione del dispositivo\", \"es\": \"Intervalo de actualización de la configuración del dispositivo\", \"pl\": \"Interwał aktualizacji konfiguracji urządzenia\",    \"uk\": \"Інтервал оновлення конфігурації пристрою\",         \"zh-cn\": \"设备配置更新间隔\"},\n    \"updateConfigurationInterval_info\": {             \"en\": \"The device configuration can be updated automatically. The update interval defines how often this should happen. Do not set this too low because else Amazon might block you! The Minimum is 300s or set to 0 to disable the update.\", \"de\": \"Die Gerätekonfiguration kann automatisch aktualisiert werden. Das Update-Intervall definiert, wie oft dies geschehen sollte. Stellen Sie das nicht zu niedrig, weil sonst Amazon Sie blockieren könnte! Das Minimum ist 300s oder auf 0 gesetzt, um das Update zu deaktivieren.\", \"ru\": \"Конфигурация устройства может обновляться автоматически. Интервал обновления определяет, как часто это должно произойти. Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Минимум 300-х или настроен на 0, чтобы отключить обновление.\", \"pt\": \"A configuração do dispositivo pode ser atualizada automaticamente. O intervalo de atualização define com que frequência isso deve acontecer. Não coloque isso muito baixo porque a Amazon pode bloquear você! O Mínimo é de 3300 ou definido para 0 para desativar a atualização.\", \"nl\": \"Het apparaat kan automatisch bijgewerkt worden. De update interval definieert hoe vaak dit zou moeten gebeuren. Zet dit niet te laag, want anders blokkeert Amazone je. Het Minimum is 300 of klaar om de update uit te schakelen.\", \"fr\": \"La configuration de l'appareil peut être mise à jour automatiquement. L'intervalle de mise à jour définit combien de fois cela devrait se produire. Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Le minimum est de 300 ou de 0 pour désactiver la mise à jour.\", \"it\": \"La configurazione del dispositivo può essere aggiornata automaticamente. L'intervallo di aggiornamento definisce quanto spesso questo dovrebbe accadere. Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Il Minimo è 300 o impostato a 0 per disabilitare l'aggiornamento.\", \"es\": \"La configuración del dispositivo se puede actualizar automáticamente. El intervalo de actualización define con qué frecuencia debería ocurrir esto. No pongas esto demasiado bajo porque si no Amazon podría bloquearte! El Mínimo es de 300 o se establece a 0 para desactivar la actualización.\", \"pl\": \"Konfiguracja urządzenia może być automatycznie aktualizowana. Przedział aktualizacji definiuje sposób, w jaki często się to dzieje. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Minimum jest 300 lub ustawiony do 0, aby złagodzić aktualizację.\", \"uk\": \"Конфігурацію пристрою можна оновлювати автоматично. Інтервал оновлення визначає, як часто це має відбуватися. Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Мінімум становить 300 с або встановіть 0, щоб вимкнути оновлення.\", \"zh-cn\": \"装置配置可以自动更新。 更新的间隔界定了如何经常发生这种情况。 由于其他亚马逊可能阻碍你! 最低限度是900个,或规定为0.至0.1%的更新。.\"},\n    \"updateHistoryInterval\": {                        \"en\": \"History update interval\",                          \"de\": \"History-Aktualisierungsintervall\",                 \"ru\": \"Интервал обновления истории\",                      \"pt\": \"Intervalo de atualização do histórico\",            \"nl\": \"Historisch update-interval\",                       \"fr\": \"Intervalle de mise à\",                             \"it\": \"Intervallo di aggiornamento della cronologia\",     \"es\": \"Intervalo de actualización de historial\",          \"pl\": \"Interwał aktualizacji historii\",                   \"uk\": \"Інтервал оновлення історії\",                       \"zh-cn\": \"历史记录更新间隔\"},\n    \"updateHistoryInterval_info\": {                   \"en\": \"The History update interval defines how often the History data should be updated from the Amazon servers. The minimum is 60s or set to 0 to disable the update. Do not set this too low because else Amazon might block you! When the Push-Connection is established the update interval is not used because push information will trigger it when needed automatically.\", \"de\": \"Das History Update-Intervall definiert, wie oft die History-Daten von den Amazon-Servern aktualisiert werden sollen. Das Minimum ist 60s oder auf 0 gesetzt, um das Update zu deaktivieren. Setzen Sie das nicht zu niedrig, weil sonst Amazon Sie blockieren könnte! Wenn die Push-Connection festgestellt wird, wird das Update-Intervall nicht verwendet, da Push-Informationen es bei Bedarf automatisch auslösen.\", \"ru\": \"Интервал обновления истории определяет, как часто данные истории должны быть обновлены с серверов Amazon. Минимум 60-х или установлен 0, чтобы отключить обновление. Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Когда Push-Connection установлен интервал обновления не используется, потому что нажмите информацию будет триггер при необходимости автоматически.\", \"pt\": \"O intervalo de atualização de História define com que frequência os dados de História devem ser atualizados a partir dos servidores da Amazon. O mínimo é 60s ou definido para 0 para desativar a atualização. Não coloque isso muito baixo porque a Amazon pode bloquear você! Quando o Push-Connection é estabelecido o intervalo de atualização não é usado porque a informação do push irá acioná-lo quando necessário automaticamente.\", \"nl\": \"De geschiedenis interval definieert hoe vaak de geschiedenisgegevens moeten worden geüpdateerd van de Amazone servers. Het minimum is 60 of klaar om de update uit te schakelen. Zet dit niet te laag, want anders blokkeert Amazone je. Als de Push-Connectie is vastgesteld dat de update niet gebruikt wordt, omdat informatie het zal activeren als het automatisch nodig is.\", \"fr\": \"L'intervalle de mise à jour d'Histoire définit la fréquence de mise à jour des données d'Histoire des serveurs Amazon. Le minimum est de 60 ou de 0 pour désactiver la mise à jour. Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Lorsque le Push-Connection est établi, l'intervalle de mise à jour n'est pas utilisé parce que l'information de poussée le déclenchera au besoin automatiquement.\", \"it\": \"L'intervallo di aggiornamento di Storia definisce quanto spesso i dati di Storia dovrebbero essere aggiornati dai server Amazon. Il minimo è di 300 o impostato a 0 per disabilitare l'aggiornamento. Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Quando la Push-Connection viene stabilita l'intervallo di aggiornamento non viene utilizzato perché le informazioni push lo innescheranno quando necessario automaticamente.\", \"es\": \"El intervalo de actualización de la historia define con qué frecuencia los datos de la historia deben actualizarse desde los servidores de Amazon. El mínimo es de 60 o se establece a 0 para desactivar la actualización. No pongas esto demasiado bajo porque si no Amazon podría bloquearte! Cuando el Push-Connection se establece el intervalo de actualización no se utiliza porque la información de empuje lo activará cuando sea necesario automáticamente.\", \"pl\": \"Przedział historii określa, jak często dane historyczne powinny być aktualizowane z serwerów Amazon. Minimalna liczba wynosi 60 lub jest ustawiona do 0, aby złagodzić aktualizację. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Kiedy Push-Connection jest ugruntowany przedział uaktualnienia nie jest używany, ponieważ informacje naciśnięte będą uruchamiane automatycznie.\", \"uk\": \"Інтервал оновлення історії визначає частоту оновлення даних історії із серверів Amazon. Мінімум становить 60 с або встановіть 0, щоб вимкнути оновлення. Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Коли встановлюється Push-Connection, інтервал оновлення не використовується, оскільки push-інформація активує його за потреби автоматично.\", \"zh-cn\": \"历史最新间隔界定了如何从亚马逊服务器更新历史数据。 最低限度是60个,或为0.1%的更新。 由于其他亚马逊可能阻碍你! 当Push-Connection成立时,没有使用最新间隔,因为如果需要自动启动信息就会触发。.\"},\n    \"updateSmartHomeDevicesInterval\": {               \"en\": \"Smart Home devices update interval\",               \"de\": \"Aktualisierungsintervall für Smart-Home-Geräte\",   \"ru\": \"Интервал обновления устройств «Умный дом»\",        \"pt\": \"Intervalo de atualização dos dispositivos Smart Home\", \"nl\": \"Update-interval voor Smart Home-apparaten\",        \"fr\": \"Intervalle de mise à jour des appareils Smart Home\", \"it\": \"Intervallo di aggiornamento dei dispositivi Smart Home\", \"es\": \"Intervalo de actualización de dispositivos domésticos inteligentes\", \"pl\": \"Interwał aktualizacji urządzeń Smart Home\",        \"uk\": \"Інтервал оновлення пристроїв Smart Home\",          \"zh-cn\": \"智能家居设备更新间隔\"},\n    \"updateSmartHomeDevicesInterval_info\": {          \"en\": \"Please enable this interval only if you really need the values of the smart home devices! Only the smart home devices that have #includeInIntervalQuery activated in the objects and actively report their status to Amazon systems are updated to make sure to not create issues and costs on the skill developer side! Do not set this too low because else Amazon might block you! The Minimum is 900s or set to 0 to disable the update (can be triggered manually via #query states).\", \"de\": \"Bitte dieses Intervall nur aktivieren, wenn die Werte der Smart Home-Geräte wirklich benötigt werden! Nur die intelligenten Home-Geräte, die #includeInIntervalQuery in den Objekten aktiviert haben und aktiv ihren Status auf Amazon-Systeme melden, werden aktualisiert, um sicherzustellen, dass keine Probleme und Kosten auf der Entwicklerseite des Skills entstehen! Bitte das Intervall nicht zu niedrig, setzen, weil sonst Amazon Sie blockieren könnte! Das Minimum ist 900s oder kann auf 0 gesetzt werden, um das Update zu deaktivieren (Update kann manuell über #query Zustände ausgelöst werden).\", \"ru\": \"Пожалуйста, включите этот интервал только если вам действительно нужны значения умных домашних устройств! Только умные домашние устройства, которые имеют #includeInIntervalQuery активированы в объектах и активно сообщают о своем статусе в системы Amazon, обновляются, чтобы не создавать проблемы и затраты на стороне разработчика мастерства! Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Минимум 900s или установлено на 0, чтобы отключить обновление (может быть спрошен вручную через #query состояния).\", \"pt\": \"Por favor, ative este intervalo apenas se você realmente precisar dos valores dos dispositivos domésticos inteligentes! Apenas os dispositivos domésticos inteligentes que têm #includeInIntervalQuery ativado nos objetos e relatar ativamente seu status para sistemas Amazon são atualizados para se certificar de não criar problemas e custos no lado desenvolvedor de habilidades! Não coloque isso muito baixo porque a Amazon pode bloquear você! O Mínimo é de 900 ou definido para 0 para desativar a atualização (pode ser acionado manualmente via #query states).\", \"nl\": \"Beveel deze interval alleen als je echt de waarden van de slimme thuis apparaten nodig hebt! Alleen de slimme thuis apparaten die includeïnfiltervalQuery geactiveerd in de objecten en actief hun status aan Amazon systemen rapporteren om geen problemen te creëren en kosten aan de ontwikkelaar. Zet dit niet te laag, want anders blokkeert Amazon je. Het Minimum is 900 of set to 0 om de update te ontmantelen.\", \"fr\": \"Veuillez activer cet intervalle seulement si vous avez vraiment besoin des valeurs des appareils à domicile intelligents! Seuls les appareils smart home qui ont #includeInIntervalQuery activés dans les objets et rapportent activement leur statut aux systèmes Amazon sont mis à jour pour s'assurer de ne pas créer de problèmes et de coûts sur le côté développeur de compétences! Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Le minimum est de 900 ou défini à 0 pour désactiver la mise à jour (peut être déclenché manuellement via les états #query).\", \"it\": \"Si prega di attivare questo intervallo solo se hai davvero bisogno dei valori dei dispositivi smart home! Solo i dispositivi smart home che hanno #includeInIntervalQuery attivato negli oggetti e riportano attivamente il loro stato ai sistemi Amazon sono aggiornati per assicurarsi di non creare problemi e costi sul lato sviluppatore di abilità! Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Il Minimo è 900 o impostato a 0 per disabilitare l'aggiornamento (può essere attivato manualmente tramite #query stati).\", \"es\": \"Por favor, active este intervalo sólo si realmente necesita los valores de los dispositivos caseros inteligentes! Sólo los dispositivos inteligentes caseros que tienen #includeInIntervalQuery activados en los objetos y reportan activamente su estado en los sistemas de Amazon se actualizan para asegurarse de no crear problemas y costos en el lado desarrollador de habilidades! No pongas esto demasiado bajo porque si no Amazon podría bloquearte! El Mínimo es de 900 o se establece a 0 para desactivar la actualización (puede activarse manualmente a través de estados #query).\", \"pl\": \"Pozwólcie to tylko wtedy, gdy potrzebujesz wartości inteligentnych urządzeń. Jedynie inteligentne urządzenia domowe, które #include inIntervalQuery aktywowane w obiektach i aktywnie informują o ich statusie w systemach Amazon, aby nie uaktualnić problemów i kosztów po stronie wytwórcy. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Minimum jest 900 lub ustawiony do 0, aby zdyskwalifikować aktualizację (może być uruchamiany ręcznie przez stany numeryczne).\", \"uk\": \"Увімкніть цей інтервал, лише якщо вам дійсно потрібні значення пристроїв розумного дому! Оновлюються лише ті розумні домашні пристрої, які мають активований #includeInIntervalQuery в об’єктах і активно повідомляють про свій статус системам Amazon, щоб переконатися, що не створюють проблем і витрат на стороні розробника навичок! Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Мінімум становить 900 с або встановіть значення 0, щоб вимкнути оновлення (можна запустити вручну за допомогою станів #query).\", \"zh-cn\": \"只有在你真正需要聪明的家用具的价值观时,才能够做到这一点。 只有在有目标的星球和积极汇报亚马逊系统状况的智能家装置得到更新,以确保不会产生技能发展方面的问题和费用! 由于其他亚马逊可能阻碍你! 最低限度是300个,或规定0.至0.至0.可区分的更新(通过#query州以手工方式进行)。.\"},\n    \"updateStateInterval\": {                          \"en\": \"Data update interval (player info …)\",             \"de\": \"Datenaktualisierungsinterval (Player-Info ...)\",   \"ru\": \"Интервал обновления данных (информация плеера ...)\", \"pt\": \"Intervalo de atualização de dados (informações do player ...)\", \"nl\": \"Gegevensbijwerkingsinterval (Player-Info ...)\",    \"fr\": \"Intervalle de mise à jour des données (Player-Info ...)\", \"it\": \"Intervallo di aggiornamento dei dati (Player-Info ...)\", \"es\": \"Intervalo de actualización de datos (Player-Info ...)\", \"pl\": \"Interwał aktualizacji danych (Player-Info ...)\",   \"uk\": \"Інтервал оновлення даних (інформація про гравця…)\", \"zh-cn\": \"数据更新间隔（玩家信息...）\"},\n    \"updateStateInterval_info\": {                     \"en\": \"The general device data update interval defines how often the data should be updated from the Amazon servers. The minimum is 300s or set to 0 to disable the update. Do not set this too low because else Amazon might block you! When the Push-Connection is established the update interval is set to 60 minutes when enabled.\", \"de\": \"Das allgemeine Datenupdate-Intervall des Geräts definiert, wie oft die Daten von den Amazon-Servern aktualisiert werden sollen. Das Minimum ist 300s oder wird auf 0 gesetzt um das Update zu deaktivieren. Setzen Sie das nicht zu niedrig, weil Amazon Sie sonst blockieren könnte! Wenn die Push-Connection hergestellt ist wird das Update-Intervall auf 60 Minuten gestellt, falls aktiviert.\", \"ru\": \"Интервал обновления данных общего устройства определяет, как часто данные должны быть обновлены с серверов Amazon. Минимум 300-х или установлен 0, чтобы отключить обновление. Не устанавливайте это слишком низко, потому что другой Amazon может заблокировать вас! Когда Push-Connection установлен интервал обновления устанавливается до 60 минут при включенном.\", \"pt\": \"O intervalo geral de atualização de dados do dispositivo define com que frequência os dados devem ser atualizados a partir dos servidores da Amazon. O mínimo é 300s ou definido para 0 para desativar a atualização. Não coloque isso muito baixo porque a Amazon pode bloquear você! Quando o Push-Connection é estabelecido o intervalo de atualização é definido para 60 minutos quando ativado.\", \"nl\": \"De algemene apparatuur update bepaalt hoe vaak de gegevens moeten worden geüpload van de Amazone servers. Het minimum is 300 of klaar om de update uit te schakelen. Zet dit niet te laag, want anders blokkeert Amazone je. Als de Push-Connectie is vastgesteld dat de update interval is gestart tot 60 minuten wanneer het in staat is.\", \"fr\": \"L'intervalle général de mise à jour des données de périphérique définit la fréquence de mise à jour des données des serveurs Amazon. Le minimum est de 300 ou de 0 pour désactiver la mise à jour. Ne le mettez pas trop bas parce que d'autres Amazon pourraient vous bloquer! Lorsque le Push-Connection est établi, l'intervalle de mise à jour est fixé à 60 minutes lorsque activé.\", \"it\": \"L'intervallo di aggiornamento dei dati del dispositivo generale definisce quanto spesso i dati dovrebbero essere aggiornati dai server Amazon. Il minimo è di 300 o impostato a 0 per disabilitare l'aggiornamento. Non impostare questo troppo basso perché altrimenti Amazon potrebbe bloccarti! Quando viene stabilita la Push-Connection, l'intervallo di aggiornamento viene impostato a 60 minuti quando abilitato.\", \"es\": \"El intervalo de actualización de datos del dispositivo general define con qué frecuencia los datos deben actualizarse desde los servidores de Amazon. El mínimo es de 300 o se establece a 0 para desactivar la actualización. No pongas esto demasiado bajo porque si no Amazon podría bloquearte! Cuando el Push-Connection se establece el intervalo de actualización se establece a 60 minutos cuando está habilitado.\", \"pl\": \"Ogólna aktualizacja danych definiuje, jak często dane powinny być aktualizowane z serwerów Amazon. Minimalna liczba wynosi 300 lub jest ustawiona do 0, aby złagodzić aktualizację. Nie ustalono tego zbyt niskiego, ponieważ inna Amazonka może blokować Ciebie. Po ustanowieniu przez Push-Connection przedziału aktualizacji następuje do 60 minut.\", \"uk\": \"Загальний інтервал оновлення даних пристрою визначає частоту оновлення даних із серверів Amazon. Мінімум становить 300 с або встановіть 0, щоб вимкнути оновлення. Не встановлюйте це значення занадто низько, інакше Amazon може вас заблокувати! Коли Push-Connection встановлено, інтервал оновлення встановлюється на 60 хвилин, якщо ввімкнено.\", \"zh-cn\": \"总的装置数据更新的间隔界定了如何从亚马逊服务器更新数据。 最低限度是300个,或为0.1%的更新。 由于其他亚马逊可能阻碍你! 当Push-Connection成立时,更新的间隔时间为60分钟。.\"},\n    \"usePushConnection\": {                            \"en\": \"Use push connection\",                              \"de\": \"Push-Verbindung verwenden\",                        \"ru\": \"Использовать push-соединение\",                     \"pt\": \"Use conexão push\",                                 \"nl\": \"Gebruik een push-verbinding\",                      \"fr\": \"Utiliser la connexion push\",                       \"it\": \"Utilizzare la connessione push\",                   \"es\": \"Use la conexión push\",                             \"pl\": \"Użyj połączenia push\",                             \"uk\": \"Використовуйте push-з'єднання\",                    \"zh-cn\": \"使用推送连接\"},\n    \"userAgent\": {                                    \"en\": \"Custom User-Agent\",                                \"de\": \"Eigener User-Agent\",                               \"ru\": \"Пользовательский User-Agent\",                      \"pt\": \"Custom User-Agent\",                                \"nl\": \"Aangepaste User-Agent\",                            \"fr\": \"User-Agent personnalisé\",                          \"it\": \"User-Agent personalizzato\",                        \"es\": \"User-Agent personalizado\",                         \"pl\": \"Niestandardowy User-Agent\",                        \"uk\": \"Спеціальний агент користувача\",                    \"zh-cn\": \"自定义用户代理\"},\n};"
  },
  {
    "path": "io-package.json",
    "content": "{\n  \"common\": {\n    \"name\": \"alexa2\",\n    \"version\": \"3.27.4\",\n    \"blockedVersions\": [\n      \"~3.14.0\",\n      \"~3.15.0\",\n      \"~3.16.0\",\n      \"3.17.0\",\n      \"3.17.1\",\n      \"3.17.2\",\n      \"3.17.3\"\n    ],\n    \"title\": \"Alexa2 (Amazon Echo)\",\n    \"titleLang\": {\n      \"en\": \"Alexa2 (Amazon Echo)\",\n      \"de\": \"Alexa2 (Amazon Echo)\",\n      \"ru\": \"Alexa2 (Amazon Echo)\",\n      \"pt\": \"Alexa2 (Amazon Echo)\",\n      \"nl\": \"Alexa2 (Amazon Echo)\",\n      \"fr\": \"Alexa2 (Amazon Echo)\",\n      \"it\": \"Alexa2 (Amazon Echo)\",\n      \"es\": \"Alexa2 (eco de Amazon)\",\n      \"pl\": \"Alexa2 (Amazon Echo)\",\n      \"zh-cn\": \"Alexa2（Amazon回声）\",\n      \"uk\": \"Alexa2 (Amazon Echo)\"\n    },\n    \"desc\": {\n      \"en\": \"Remote control for Alexa (Amazon Echo)\",\n      \"de\": \"Steuerung von Alexa (Amazon Echo)\",\n      \"ru\": \"Управление Alexa (Amazon Echo)\",\n      \"pt\": \"Controle remoto para Alexa (Amazon Echo)\",\n      \"nl\": \"Afstandsbediening voor Alexa (Amazon Echo)\",\n      \"fr\": \"Télécommande pour Alexa (Amazon Echo)\",\n      \"it\": \"Telecomando per Alexa (Amazon Echo)\",\n      \"es\": \"Control remoto para Alexa (Amazon Echo)\",\n      \"pl\": \"Pilot do Alexa (Amazon Echo)\",\n      \"zh-cn\": \"Alexa（Amazon Echo）的遥控器\",\n      \"uk\": \"Пульт дистанційного керування для Alexa (Amazon Echo)\"\n    },\n    \"platform\": \"Javascript/Node.js\",\n    \"news\": {\n      \"3.27.4\": {\n        \"en\": \"Adjusts authentication check to recent Amazon changes\",\n        \"de\": \"Anpassung der Authentifizierungsprüfung an aktuelle Amazon-Änderungen\",\n        \"ru\": \"Настройка проверки подлинности на последние изменения Amazon\",\n        \"pt\": \"Ajusta a verificação de autenticação às alterações recentes do Amazon\",\n        \"nl\": \"Past authenticatiecontrole aan aan recente Amazon-wijzigingen\",\n        \"fr\": \"Ajuste la vérification d'authentification aux changements récents d'Amazon\",\n        \"it\": \"Regola il controllo di autenticazione alle recenti modifiche Amazon\",\n        \"es\": \"Ajuste la verificación de autenticación a los cambios recientes de Amazon\",\n        \"pl\": \"Dostosuje sprawdzanie autentyczności do ostatnich zmian Amazon\",\n        \"uk\": \"Налаштування перевірки автентичності на останні зміни Amazon\",\n        \"zh-cn\": \"根据最近的亚马逊变化调整认证检查\"\n      },\n      \"3.27.3\": {\n        \"en\": \"Adjust Lists to recent Amazon changes\\nAdjust retrieving the Smart home device list to recent Amazon changes\\nDelete all ioBroker originated Smart Home devices because not controllable anyway\\nAdjust usage of some deprecated js-controller methods\",\n        \"de\": \"Anpassung der Listen an aktuelle Amazon-Änderungen\\nBestätigen Sie das Abrufen der Smart Home-Gerät-Liste auf aktuelle Amazon-Änderungen\\nLöschen Sie alle ioBroker entstanden Smart Home Geräte, weil nicht kontrollierbar sowieso\\nPassen Sie die Verwendung einiger deprecated js-Controller Methoden an\",\n        \"ru\": \"Адаптация списков к последним изменениям Amazon\\nНастройка восстановления списка устройств Smart Home до последних изменений Amazon\\nУдалить все ioBroker Устройства умного дома, потому что они не контролируются\\nНастройка использования некоторых устаревших методов js-контроллера\",\n        \"pt\": \"Ajustar listas para alterações recentes na Amazon\\nAjustar a recuperação da lista de dispositivos domésticos Smart para alterações recentes na Amazon\\nApagar todas as origens do ioBroker Smart Home dispositivos porque não controlável de qualquer maneira\\nAjuste o uso de alguns métodos desatualizados de controle js\",\n        \"nl\": \"Lijsten aanpassen aan recente wijzigingen in Amazon\\nPas het ophalen van de Smart home apparaat lijst aan recente Amazon wijzigingen\\nVerwijder alle ioBroker-afkomstig Smart Home apparaten omdat niet te controleren toch\\nHet gebruik van sommige verouderde js-controller methoden aanpassen\",\n        \"fr\": \"Ajuster les listes aux changements récents d'Amazon\\nAjuster la récupération de la liste des appareils à domicile intelligents aux changements récents d'Amazon\\nSupprimer tout ioBroker originaire Appareils Smart Home parce qu'ils ne sont pas contrôlables de toute façon\\nRégler l'utilisation de certaines méthodes de contrôle de js dépréciées\",\n        \"it\": \"Regolare le liste alle recenti modifiche Amazon\\nRegolare il recupero dell'elenco dei dispositivi Smart home alle recenti modifiche Amazon\\nCancella tutto ioBroker originato Dispositivi Smart Home perché non controllabili comunque\\nRegolare l'uso di alcuni metodi js-controller deprecati\",\n        \"es\": \"Ajuste listas de cambios recientes en Amazon\\nAjuste la recuperación de la lista de dispositivos inteligentes para el hogar a los cambios recientes de Amazon\\nEliminar todo el origen de ioBroker Smart Home dispositivos porque no controlables de todos modos\\nAjuste el uso de algunos métodos de control js deprecated\",\n        \"pl\": \"Dostosuj listy do ostatnich zmian Amazon\\nDostosowanie listy urządzeń Smart home do ostatnich zmian Amazon\\nUsuń wszystkie jOBroker pochodzi Inteligentne urządzenia domowe, ponieważ i tak nie do kontrolowania\\nDostosuj użycie niektórych zdeprecjonowanych metod sterownika js- controla\",\n        \"uk\": \"Налаштування списку останніх змін Amazon\\nНалаштування перерозподілу списку побутових пристроїв Smart до останніх змін Amazon\\nВидалити всі ioBroker Розумні побутові пристрої, тому що не керовані\\nРегулювання використання деяких депресованих методів js-controller\",\n        \"zh-cn\": \"根据最近的亚马逊变化调整列表\\n将 Smart 主设备列表调整为最近的 Amazon 更改\\n删除所有 ioBroker 来源 智能家用设备, 因为不能控制\\n调整一些已贬值的js控制器方法的使用\"\n      },\n      \"3.26.7\": {\n        \"en\": \"Fix Sentry integration\",\n        \"de\": \"Fix Sentry Integration\",\n        \"ru\": \"Интеграция с отправкой\",\n        \"pt\": \"Integração da Sentry\",\n        \"nl\": \"Fix Sentry-integratie\",\n        \"fr\": \"Correction de l'intégration Sentry\",\n        \"it\": \"Fissa integrazione Sentry\",\n        \"es\": \"Integración de Sentry\",\n        \"pl\": \"Napraw integrację zapisu\",\n        \"uk\": \"Фіксована інтеграція Sentry\",\n        \"zh-cn\": \"哨兵一体化\"\n      },\n      \"3.26.6\": {\n        \"en\": \"Responsive Design added\",\n        \"de\": \"Responsive Design hinzugefügt\",\n        \"ru\": \"Responsive Design\",\n        \"pt\": \"Design responsivo adicionado\",\n        \"nl\": \"Responsive Design toegevoegd\",\n        \"fr\": \"Design sensible ajouté\",\n        \"it\": \"Responsive Design aggiunto\",\n        \"es\": \"Diseño responsable añadido\",\n        \"pl\": \"Dodano projekt odpowiedzialny\",\n        \"uk\": \"Додано адаптивний дизайн\",\n        \"zh-cn\": \"添加响应设计\"\n      },\n      \"3.26.5\": {\n        \"en\": \"Adjust History query to recent Amazon changes\",\n        \"de\": \"Begleichen Sie die Geschichte Anfrage an aktuelle Amazon-Änderungen\",\n        \"ru\": \"Настройте исторический запрос на недавние изменения в Amazon\",\n        \"pt\": \"Ajustar a consulta de história às mudanças recentes da Amazônia\",\n        \"nl\": \"Historische zoekopdracht aanpassen aan recente Amazon-wijzigingen\",\n        \"fr\": \"Régler la requête d'historique aux changements récents d'Amazon\",\n        \"it\": \"Regolare la query di storia alle recenti modifiche Amazon\",\n        \"es\": \"Adjust Historia consulta a cambios recientes de Amazon\",\n        \"pl\": \"Dostosuj zapytanie Historia do ostatnich zmian Amazon\",\n        \"uk\": \"Налаштування історичного запиту на останні зміни Amazon\",\n        \"zh-cn\": \"调整历史查询以适应最近的亚马逊更改\"\n      },\n      \"3.26.4\": {\n        \"en\": \"Removed Weblink\\nAdjust History query to recent Amazon changes\",\n        \"de\": \"Entfernen von Weblink\\nBegleichen Sie die Geschichte Anfrage an aktuelle Amazon-Änderungen\",\n        \"ru\": \"Удаленная веб-ссылка\\nНастройте исторический запрос на недавние изменения в Amazon\",\n        \"pt\": \"Weblink removido\\nAjustar a consulta de história às mudanças recentes da Amazônia\",\n        \"nl\": \"Weblink verwijderd\\nHistorische zoekopdracht aanpassen aan recente Amazon-wijzigingen\",\n        \"fr\": \"Supprimé le lien Web\\nRégler la requête d'historique aux changements récents d'Amazon\",\n        \"it\": \"Rimozione di Weblink\\nRegolare la query di storia alle recenti modifiche Amazon\",\n        \"es\": \"Redlink eliminado\\nAdjust Historia consulta a cambios recientes de Amazon\",\n        \"pl\": \"Usunięto mrugnięcie\\nDostosuj zapytanie Historia do ostatnich zmian Amazon\",\n        \"uk\": \"Видалити Weblink\\nНалаштування історичного запиту на останні зміни Amazon\",\n        \"zh-cn\": \"删除了网络链接\\n调整历史查询以适应最近的亚马逊更改\"\n      },\n      \"3.26.3\": {\n        \"en\": \"Fixed the proxy login process\",\n        \"de\": \"Behoben des Proxy-Anmeldevorgangs\",\n        \"ru\": \"Исправлен процесс авторизации прокси\",\n        \"pt\": \"Corrigido o processo de login proxy\",\n        \"nl\": \"Het proxy loginproces\",\n        \"fr\": \"Correction du processus de connexion proxy\",\n        \"it\": \"Risolto il processo di login proxy\",\n        \"es\": \"Arreglado el proceso de inicio de sesión proxy\",\n        \"pl\": \"Fixed proxy login process\",\n        \"uk\": \"Виправлено процес проксі\",\n        \"zh-cn\": \"A. 确定代伐木过程\"\n      }\n    },\n    \"keywords\": [\n      \"alexa\",\n      \"amazon\",\n      \"amazon echo\",\n      \"echo dot\",\n      \"echo show\"\n    ],\n    \"authors\": [\n      \"Apollon77 <iobroker@fischer-ka.de>\"\n    ],\n    \"mode\": \"daemon\",\n    \"icon\": \"alexa.png\",\n    \"materialize\": true,\n    \"extIcon\": \"https://raw.githubusercontent.com/Apollon77/ioBroker.alexa2/master/admin/alexa.png\",\n    \"readme\": \"https://github.com/Apollon77/ioBroker.alexa2/blob/master/README.md\",\n    \"loglevel\": \"info\",\n    \"type\": \"iot-systems\",\n    \"enabled\": true,\n    \"messagebox\": true,\n    \"licenseInformation\": {\n      \"license\": \"MIT\",\n      \"type\": \"free\"\n    },\n    \"connectionType\": \"cloud\",\n    \"dataSource\": \"push\",\n    \"tier\": 2,\n    \"compact\": true,\n    \"dependencies\": [\n      {\n        \"js-controller\": \">=4.0.24\"\n      }\n    ],\n    \"plugins\": {\n      \"sentry\": {\n        \"dsn\": \"https://e9ac55f23fe149e78b1eab9ed4bf26ad@sentry.iobroker.net/8\",\n        \"pathWhitelist\": [\n          \"@apollon\",\n          \"alexa-cookie2\",\n          \"alexa-remote2\"\n        ],\n        \"errorBlacklist\": [\n          \"SyntaxError\"\n        ]\n      }\n    },\n    \"messages\": [\n      {\n        \"condition\": {\n          \"operand\": \"and\",\n          \"rules\": [\n            \"oldVersion<3.13.0\",\n            \"newVersion>=3.13.0\"\n          ]\n        },\n        \"title\": {\n          \"en\": \"Important notice!\",\n          \"de\": \"Wichtiger Hinweis!\",\n          \"ru\": \"Важное замечание!\",\n          \"pt\": \"Notícia importante!\",\n          \"nl\": \"Belangrijke mededeling!\",\n          \"fr\": \"Avis important!\",\n          \"it\": \"Avviso IMPORTANTE!\",\n          \"es\": \"Noticia importante!\",\n          \"pl\": \"Ważna uwaga!\",\n          \"zh-cn\": \"重要通知!\",\n          \"uk\": \"Важливе повідомлення!\"\n        },\n        \"text\": {\n          \"en\": \"The new version 1.2.x will potentially change the Names of List Objects when special characters (including \\\".\\\") are used! Please delete the old objects manually if needed.\",\n          \"de\": \"Die neue Version 1.2.x wird die Namen der Listenobjekte möglicherweise ändern, wenn Sonderzeichen (einschließlich \\\".\\\") verwendet werden! Bitte löschen Sie die alten Objekte bei Bedarf manuell.\",\n          \"ru\": \"Новая версия 1.2.x потенциально изменит имена объектов списка, когда используются специальные символы (включая \\\".\\\")! Пожалуйста, удалите старые объекты вручную, если это необходимо.\",\n          \"pt\": \"A nova versão 1.2.x irá potencialmente alterar os nomes dos objetos de lista quando caracteres especiais (incluindo \\\".\\\") são usados! Por favor, exclua os objetos antigos manualmente, se necessário.\",\n          \"nl\": \"De nieuwe versie 1.2.x zal mogelijk de Namen van List Objects veranderen als speciale personages (includeren) worden gebruikt. Verwijder de oude objecten handmatig als het nodig is.\",\n          \"fr\": \"La nouvelle version 1.2.x modifiera potentiellement les Noms des objets de la liste lorsque des caractères spéciaux (y compris \\\"). Veuillez supprimer les anciens objets manuellement si nécessaire.\",\n          \"it\": \"La nuova versione 1.2.x cambierà potenzialmente i nomi degli oggetti di elenco quando vengono utilizzati caratteri speciali (inclusi \\\".\\\")! Si prega di eliminare i vecchi oggetti manualmente se necessario.\",\n          \"es\": \"La nueva versión 1.2.x potencialmente cambiará los Nombres de Objetos de Lista cuando se utilizan caracteres especiales (incluyendo \\\"). Por favor, borre los viejos objetos manualmente si es necesario.\",\n          \"pl\": \"Nowa wersja 1.2.x będzie potencjalnie zmieniać nazwę nazw obiektów listowych, kiedy używa się specjalnych postaci (w tym „.”). Okazuje się, że stare obiekty ręcznie będą potrzebne.\",\n          \"zh-cn\": \"新版本1.2.x将有可能改变名单所列物品的名称(包括“......”)。 请在必要时删除旧物体。.\",\n          \"uk\": \"Нова версія 1.2.x потенційно змінить імена об’єктів списку, коли використовуються спеціальні символи (включаючи «.»)! За потреби видаліть старі об’єкти вручну.\"\n        },\n        \"level\": \"warn\",\n        \"buttons\": [\n          \"agree\",\n          \"cancel\"\n        ]\n      },\n      {\n        \"condition\": {\n          \"operand\": \"and\",\n          \"rules\": [\n            \"oldVersion<3.15.0\",\n            \"newVersion>=3.15.0\"\n          ]\n        },\n        \"title\": {\n          \"en\": \"Format to create Reminder/Alarms changed!\",\n          \"de\": \"Format zum Erstellen von Reminder/Alarms geändert!\",\n          \"ru\": \"Формат для создания Reminder/Alarms изменился!\",\n          \"pt\": \"Formato para criar lembrete / alarmes alterado!\",\n          \"nl\": \"Format om Reminder/Alarms te creëren!\",\n          \"fr\": \"Format pour créer Reminder/Alarms changé!\",\n          \"it\": \"Formato per creare Reminder/Allarma cambiate!\",\n          \"es\": \"Formato para crear Reminder/Alarms cambiado!\",\n          \"pl\": \"W celu stworzenia zmian Reminder/Alarms!\",\n          \"zh-cn\": \"创建Reminder/Alarms的格局改变了!\",\n          \"uk\": \"Змінено формат створення нагадувань/будильників!\"\n        },\n        \"text\": {\n          \"en\": \"The format used in the \\\"New\\\" states for Alarms or Reminders has changed and allows now to provide more Details. Please see the Readme for the details!\",\n          \"de\": \"Das Format, das in den neuen Zuständen für Alarme oder Reminders verwendet wird, hat sich geändert und ermöglicht nun mehr Details. Die Readme enthält alle Details!\",\n          \"ru\": \"Изменен формат, используемый в новых состояниях для тревоги или напоминаний и позволяет теперь предоставить более подробную информацию. Пожалуйста, посмотрите Readme для деталей!\",\n          \"pt\": \"O formato usado nos Novos estados para Alarmes ou Lembretes mudou e agora permite fornecer mais detalhes. Por favor, veja o Readme para os detalhes!\",\n          \"nl\": \"De formaat gebruikt in de Nieuwe staten voor Alarms of Reminders is veranderd en staat nu toe om meer Details te leveren. Zie de Readme voor de details!\",\n          \"fr\": \"Le format utilisé dans les nouveaux états pour les armes ou les rappels a changé et permet maintenant de fournir plus de détails. Veuillez voir le Readme pour les détails!\",\n          \"it\": \"Il formato utilizzato nei nuovi stati per gli allarmi o i ripetitori è cambiato e consente ora di fornire ulteriori dettagli. Si prega di vedere il Readme per i dettagli!\",\n          \"es\": \"El formato utilizado en los Nuevos estados para Alarmas o Recordatorios ha cambiado y permite ahora proporcionar más detalles. Por favor, vea el Readme para los detalles!\",\n          \"pl\": \"Format używany w nowych stanach dla Alarmsa lub Remindersa został zmieniony i pozwala obecnie na dostarczenie większej liczby dekatów. Wyglądaj na szczegóły!\",\n          \"zh-cn\": \"新州用于申报或递解剂的格式已经改变,现在可以提供更多的细节。 请见详细!\",\n          \"uk\": \"Формат, який використовується в станах «Новий» для будильників або нагадувань, змінився і тепер дозволяє надавати більше деталей. Будь ласка, перегляньте файл Readme, щоб дізнатися більше!\"\n        },\n        \"level\": \"warn\",\n        \"buttons\": [\n          \"agree\",\n          \"cancel\"\n        ],\n        \"link\": \"https://github.com/Apollon77/ioBroker.alexa2#alexa20echo-devicesserialnumberalarm\",\n        \"linkText\": {\n          \"en\": \"Readme\",\n          \"de\": \"Reader\",\n          \"ru\": \"Читать\",\n          \"pt\": \"Readme\",\n          \"nl\": \"Lees\",\n          \"fr\": \"Readme\",\n          \"it\": \"Readme\",\n          \"es\": \"Readme\",\n          \"pl\": \"Ready\",\n          \"zh-cn\": \"阅读摘要\",\n          \"uk\": \"Readme\"\n        }\n      },\n      {\n        \"condition\": {\n          \"operand\": \"and\",\n          \"rules\": [\n            \"oldVersion<3.18.0\",\n            \"newVersion>=3.18.0\"\n          ]\n        },\n        \"title\": {\n          \"en\": \"Changes in Smart Home device syncing\",\n          \"de\": \"Änderungen der Smart Home Gerätesynchronisation\",\n          \"ru\": \"Изменения в синхронизации устройства Smart Home\",\n          \"pt\": \"Alterações na sincronização de dispositivos Smart Home\",\n          \"nl\": \"Veranderingen in Smart Home apparaat\",\n          \"fr\": \"Changements dans la synchronisation de l'appareil Smart Home\",\n          \"it\": \"Modifiche nella sincronizzazione dei dispositivi Smart Home\",\n          \"es\": \"Cambios en la sincronización de dispositivos Smart Home\",\n          \"pl\": \"Zmiany w Smart Home urządzenie synchronizujące\",\n          \"zh-cn\": \"Smart家装置的合并变化\",\n          \"uk\": \"Зміни в синхронізації пристрою Smart Home\"\n        },\n        \"text\": {\n          \"en\": \"Starting with version 3.18.0 smart home device values are no longer synced by default. You need to enable the new state #includeInIntervalQuery existing for each syncable device in the objects in order for the device to be synchronized. Please enable only those that you really need to reduce the requests to Amazon and the Skills and prevent blocking or issues.\",\n          \"de\": \"Ab Version 3.18.0 werden Smart Home-Gerät-Werte nicht mehr standardmäßig synchronisiert. Der neuen Zustand #includeInInterQuery, der für jedes synchronisierbare Gerät in den Objekten vorhanden ist, muss aktiviert werden um das Gerät zu synchronisieren. Bitte nur diejenigen Geräte aktivieren, die wirklich benötigt werden, um die Anfragen an Amazon und die Skills zu reduzieren und Blockierung oder Probleme zu verhindern.\",\n          \"ru\": \"Начиная с версии 3.18.0 интеллектуальные ценности домашнего устройства больше не синхронизированы по умолчанию. Вам необходимо включить новое состояние #includeInIntervalQuery существующее для каждого синхронного устройства в объектах, чтобы устройство было синхронизировано. Пожалуйста, включите только те, что вам действительно нужно уменьшить запросы на Amazon и навыки и предотвратить блокировку или проблемы.\",\n          \"pt\": \"Começando com a versão 3.18.0 os valores de dispositivo doméstico inteligente não são mais sincronizados por padrão. Você precisa ativar o novo estado #includeInIntervalQuery existente para cada dispositivo sincronizável nos objetos para que o dispositivo seja sincronizado. Por favor, ative apenas aqueles que você realmente precisa para reduzir os pedidos para Amazon e as habilidades e evitar bloqueio ou problemas.\",\n          \"nl\": \"Beginnend met versie 3.18.0 slimme thuis apparatuur is niet meer gesynchroniseerd door defect. Je moet de nieuwe staat in staat stellen includeïntervalQuery die bestaat voor elk syncable apparaat in de objecten om het apparaat te synchroniseren. Betrek alleen degenen die je echt nodig hebt om de verzoeken naar Amazone en de Skills te verminderen en blokkeren of problemen te voorkomen.\",\n          \"fr\": \"À partir de la version 3.18.0, les valeurs de périphérique à domicile intelligent ne sont plus synchronisées par défaut. Vous devez activer le nouvel état #includeInIntervalQuery existant pour chaque périphérique synchronisé dans les objets afin que le dispositif soit synchronisé. S'il vous plaît activer seulement ceux que vous avez vraiment besoin de réduire les demandes à Amazon et les Compétences et prévenir le blocage ou les problèmes.\",\n          \"it\": \"A partire dalla versione 3.18.0 i valori dei dispositivi smart home non sono più sincronizzati per impostazione predefinita. È necessario abilitare il nuovo stato #includeInIntervalQuery esistente per ogni dispositivo sincronizzabile negli oggetti in modo che il dispositivo sia sincronizzato. Si prega di attivare solo quelli che è davvero necessario ridurre le richieste a Amazon e le competenze e prevenire il blocco o problemi.\",\n          \"es\": \"Empezando con la versión 3.18.0, los valores de dispositivo hogar inteligente ya no se sincronizan por defecto. Necesita habilitar el nuevo estado #includeInIntervalQuery existente para cada dispositivo sincronizable en los objetos con el fin de sincronizar el dispositivo. Por favor, active sólo aquellos que realmente necesita para reducir las solicitudes a Amazon y las Habilidades y evitar el bloqueo o problemas.\",\n          \"pl\": \"Począwszy od wersji 3.18.0, inteligentne urządzenia domowe nie są już zsynchronizowane. Musisz umożliwić nowy stan #include InIntervalQuery istniejące dla każdego urządzenia synchronizującego się w obiektach. Pozwólcie to tylko tym, że musisz zredukować żądania do Amazona i Umiejętności i zapobiegać blokowaniu lub problemom.\",\n          \"zh-cn\": \"从第3.18.0版的家里装置价值开始,不再因违约而形成。 你们需要使新的国家能够为各物体的每个可核对装置建立新的国家行动目标。 仅请你真正需要减少对亚马逊和技能的要求,并防止阻断或问题。.\",\n          \"uk\": \"Починаючи з версії 3.18.0, значення пристрою розумного дому більше не синхронізуються за умовчанням. Вам потрібно ввімкнути новий стан #includeInIntervalQuery, який існує для кожного синхронізованого пристрою в об’єктах, щоб пристрій міг синхронізуватися. Увімкніть лише ті, які вам дійсно потрібні, щоб зменшити кількість запитів до Amazon і Skills і запобігти блокуванню чи проблемам.\"\n        },\n        \"level\": \"warn\",\n        \"buttons\": [\n          \"agree\",\n          \"cancel\"\n        ],\n        \"link\": \"https://github.com/Apollon77/ioBroker.alexa2#alexa20echo-devicesserialnumberalarm\",\n        \"linkText\": {\n          \"en\": \"Readme\",\n          \"de\": \"Reader\",\n          \"ru\": \"Читать\",\n          \"pt\": \"Readme\",\n          \"nl\": \"Lees\",\n          \"fr\": \"Readme\",\n          \"it\": \"Readme\",\n          \"es\": \"Readme\",\n          \"pl\": \"Ready\",\n          \"zh-cn\": \"阅读摘要\",\n          \"uk\": \"Readme\"\n        }\n      },\n      {\n        \"condition\": {\n          \"operand\": \"and\",\n          \"rules\": [\n            \"oldVersion<3.26.0\",\n            \"newVersion>=3.26.0\"\n          ]\n        },\n        \"title\": {\n          \"en\": \"Changes in activity synchronisation\",\n          \"de\": \"Änderungen der Aktivitätssynchronisation\",\n          \"ru\": \"Изменения в синхронизации активности\",\n          \"pt\": \"Alterações na sincronização de atividades\",\n          \"nl\": \"Verandering in actuele synchronisatie\",\n          \"fr\": \"Changements de synchronisation d'activité\",\n          \"it\": \"Modifiche della sincronizzazione delle attività\",\n          \"es\": \"Cambios en la sincronización de actividad\",\n          \"pl\": \"Zmiany w synchronizacji aktywności\",\n          \"uk\": \"Зміна синхронізації активності\",\n          \"zh-cn\": \"B. 活动统一时间的变化\"\n        },\n        \"text\": {\n          \"en\": \"Starting with Version 3.26.0 The activity/history is no longer queried automatically. If you need this automatic query please enable it in the settings - but only if you really need it! It generates load on the amazon systems and we should try to reduce this as much as possible please!\",\n          \"de\": \"Beginnend mit Version 3.26.0 Die Aktivität/History wird nicht mehr automatisch abgefragt. Wenn Sie diese automatische Abfrage benötigen, aktivieren Sie es bitte in den Einstellungen - aber nur, wenn Sie es wirklich brauchen! Es erzeugt Belastung der Amazonsysteme und wir sollten versuchen, dies so viel wie möglich zu reduzieren, bitte!\",\n          \"ru\": \"Начиная с версии 3.26.0 Деятельность/история больше не запрашивается автоматически. Если вам нужен этот автоматический запрос, пожалуйста, включите его в настройках - но только если вам это действительно нужно! Он генерирует нагрузку на амазоночные системы, и мы должны попытаться уменьшить это как можно больше, пожалуйста!\",\n          \"pt\": \"Começando com a versão 3.26.0 A atividade/história não é mais consultada automaticamente. Se você precisar desta consulta automática, por favor ative-a nas configurações - mas só se você realmente precisar dele! Ele gera carga nos sistemas de amazon e devemos tentar reduzir isso tanto quanto possível, por favor!\",\n          \"nl\": \"Begin met Version 3.26.0 De activiteit/hestory is niet meer automatisch. Als je deze automatische zoektocht nodig hebt, zet het dan in de settings, maar alleen als je het echt nodig hebt! Het genereert lading op de Amazone-systemen en we moeten proberen dit zoveel mogelijk te verminderen!\",\n          \"fr\": \"À partir de la version 3.26.0 L'activité/l'histoire n'est plus demandée automatiquement. Si vous avez besoin de cette requête automatique, veuillez l'activer dans les paramètres - mais seulement si vous en avez vraiment besoin! Il génère de la charge sur les systèmes amazon et nous devrions essayer de réduire cela autant que possible s'il vous plaît!\",\n          \"it\": \"A partire dalla versione 3.26.0 L'attività/storia non è più richiesta automaticamente. Se hai bisogno di questa query automatica, abilitalo nelle impostazioni - ma solo se ne hai davvero bisogno! Esso genera carico sui sistemi amazon e dovremmo cercare di ridurre questo il più possibile per favore!\",\n          \"es\": \"Empezando con la versión 3.26.0 La actividad/historia ya no se pregunta automáticamente. Si necesita esta consulta automática, por favor, activela en la configuración - pero sólo si realmente lo necesita! Genera carga en los sistemas amazon y debemos tratar de reducir todo lo posible por favor!\",\n          \"pl\": \"Od wersji 3.26.0 Aktywność/historia nie jest już automatycznie kwestionowana. Jeśli potrzebujesz automatycznego zapytania, co pozwalasz w ustawieniach - ale tylko wtedy, jeśli tak naprawdę potrzebujesz! Wykorzystuje on ładunek na systemach amazonek i powinniśmy starać się zredukować to, co jest możliwe!\",\n          \"uk\": \"Починаючи з версії 3.26.0 Діяльність/історія не передається автоматично. Якщо вам потрібен цей автоматичний запит, будь ласка, ввімкніть його в налаштуваннях - але тільки якщо вам дійсно потрібно! Ми можемо самі зателефонувати одержувачу!\",\n          \"zh-cn\": \"A. 导 言 活动/法令不再自动提出。 如果你需要这种自动问询,请在环境中这样做,但只有你真正需要! 它给马纳区系统带来了沉重的负担,我们应尽量减少这一点!\"\n        },\n        \"level\": \"warn\",\n        \"buttons\": [\n          \"agree\",\n          \"cancel\"\n        ]\n      }\n    ]\n  },\n  \"native\": {\n    \"cookie\": \"\",\n    \"csrf\": \"\",\n    \"cookieData\": null,\n    \"alexaServiceHost\": \"\",\n    \"email\": \"\",\n    \"password\": \"\",\n    \"userAgent\": \"\",\n    \"acceptLanguage\": \"\",\n    \"cookieLoginUrl\": \"\",\n    \"proxyOwnIp\": \"\",\n    \"proxyPort\": 0,\n    \"proxyListenBind\": \"\",\n    \"updateConfigurationInterval\": 3600,\n    \"updateStateInterval\": 300,\n    \"updateHistoryInterval\": 300,\n    \"usePushConnection\": true,\n    \"proxyOverrideIp\": \"\",\n    \"resetCookies\": false,\n    \"historyIgnoreEmptySummary\": false,\n    \"macDms\": null,\n    \"includeAppDevices\": false,\n    \"synchronizeLists\": true,\n    \"synchronizeSmartHomeDevices\": false,\n    \"autoQueryActivityOnTrigger\": false\n  },\n  \"objects\": [],\n  \"instanceObjects\": [\n    {\n      \"_id\": \"info\",\n      \"type\": \"channel\",\n      \"common\": {\n        \"name\": \"Information\"\n      },\n      \"native\": {}\n    },\n    {\n      \"_id\": \"info.connection\",\n      \"type\": \"state\",\n      \"common\": {\n        \"role\": \"indicator.connected\",\n        \"name\": \"If communication with alexa works\",\n        \"type\": \"boolean\",\n        \"read\": true,\n        \"write\": false,\n        \"def\": false\n      },\n      \"native\": {}\n    },\n    {\n      \"_id\": \"info.cookie\",\n      \"type\": \"state\",\n      \"common\": {\n        \"role\": \"text\",\n        \"name\": \"Alexa Cookie\",\n        \"type\": \"string\",\n        \"read\": true,\n        \"write\": false,\n        \"def\": \"\"\n      },\n      \"native\": {}\n    },\n    {\n      \"_id\": \"info.csrf\",\n      \"type\": \"state\",\n      \"common\": {\n        \"role\": \"text\",\n        \"name\": \"Alexa CSRF\",\n        \"type\": \"string\",\n        \"read\": true,\n        \"write\": false,\n        \"def\": \"\"\n      },\n      \"native\": {}\n    }\n  ]\n}\n"
  },
  {
    "path": "lib/smarthomedevices.js",
    "content": "/* jshint -W097 */\n/* jshint -W030 */\n/* jshint strict: false */\n/* jslint node: true */\n/* jslint esversion: 6 */\n\n// https://developer.amazon.com/de/docs/smarthome/smart-home-skill-api-message-reference.html\n// https://developer.amazon.com/de/docs/smarthome/smart-home-skill-migration-guide.html\nconst capabilityObjects = {\n    'Alexa.PowerController': {\n        'powerState' : [\n            // state: powerState\n            // actions: turnOn, turnOff\n            // values: ON, OFF\n            {\n                common: {\n                    name: 'powerState',\n                    type: 'boolean',\n                    read: true,\n                    write: true,\n                    role: 'switch'\n                },\n                native: {\n                    valueTrue: 'ON',\n                    valueFalse: 'OFF',\n                    actionTrue: 'turnOn',\n                    actionFalse: 'turnOff',\n                    supportedActions: ['turnOn', 'turnOff']\n                }\n            }\n        ]\n    },\n    'Alexa.PowerLevelController': {\n        'powerLevel': [\n            // state: powerLevel\n            // actions: setPowerLevel\n            // values: 0..100\n            {\n                common: {\n                    name: 'powerLevel',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'value'\n                },\n                native: {\n                    valueSubKey: 'value',\n                    action: 'setPowerLevel',\n                    factor: 0.01,\n                    supportedActions: ['setPowerLevel']\n                },\n                experimental: true\n            },\n            {\n                common: {\n                    name: 'powerLevelType',\n                    type: 'string',\n                    read: true,\n                    write: true,\n                    role: 'value'\n                },\n                native: {\n                    valueSubKey: '@type',\n                    action: 'setPowerLevel',\n                    noFallbackStringifiedValue: true,\n                },\n                experimental: true\n            }\n        ]\n    },\n    'Alexa.LockController': {\n        'lockState': [\n            // state: lockState\n            // actions: lock, unlock ??\n            // values: 'LOCKED', 'UNLOCKED', 'JAMMED' (??)\n            {\n                common: {\n                    name: 'lockState',\n                    type: 'boolean',\n                    read: true,\n                    write: true,\n                    role: 'switch.lock'\n                },\n                native: {\n                    valueTrue: 'LOCKED',\n                    valueFalse: 'UNLOCKED',\n                    action: 'setLockState',\n                    supportedActions: ['setLockState', 'getLockState', 'lockAction']\n                },\n                experimental: true\n            }\n        ]\n    },\n    'Alexa.ThermostatController': {\n        // state: targetSetpoint\n        // actions: setTargetTemperature ??\n        // values: value=number, 'scale': 'CELSIUS'/'FAHRENHEIT'\n        // {\"action\":\"setTargetTemperature\",\"targetTemperature.value\":\"20.0\",\"targetTemperature.scale\":\"celsius\"}}\n        'targetSetpoint': [\n            {\n                common: {\n                    name: 'targetSetpoint',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'level.temperature'\n                },\n                native: {\n                    valueSubKey: 'value',\n                    action: 'setTargetTemperature',\n                    paramName: 'targetTemperature.value',\n                    supportedActions: ['setTargetTemperature', 'getTargetTemperature', 'incrementTargetTemperature', 'decrementTargetTemperature']\n                }\n            },\n            {\n                common: {\n                    name: 'targetSetpoint-scale',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text',\n                    def: 'CELSIUS'\n                },\n                native: {\n                    valueSubKey: 'scale',\n                    action: 'setTargetTemperature',\n                    paramName: 'targetTemperature.scale',\n                    noFallbackStringifiedValue: true,\n                    defaultValue: 'CELSIUS'\n                }\n            }\n        ],\n        // state: lowerSetpoint\n        // actions: setTargetTemperature ??\n        // values: value=number, 'scale': 'CELSIUS'/'FAHRENHEIT' ??\n        'lowerSetpoint': [\n            {\n                common: {\n                    name: 'lowerSetpoint',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'level.temperature'\n                },\n                native: {\n                    valueSubKey: 'value',\n                    action: 'setTargetTemperature',\n                    paramName: 'lowerSetTemperature.value',\n                    supportedActions: ['setTargetTemperature', 'getTargetTemperature', 'incrementTargetTemperature', 'decrementTargetTemperature'],\n                    sendAdditional: ['upperSetpoint']\n                },\n                experimental: true\n            },\n            {\n                common: {\n                    name: 'lowerSetpoint-scale',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text',\n                    def: 'CELSIUS'\n                },\n                native: {\n                    valueSubKey: 'scale',\n                    action: 'setTargetTemperature',\n                    paramName: 'lowerSetTemperature.scale',\n                    noFallbackStringifiedValue: true,\n                    defaultValue: 'CELSIUS'\n                },\n                experimental: true\n            }\n        ],\n        // state: thermostatMode\n        // actions: setThermostatMode ??\n        // values: e.g. 'AUTO', 'COOL', 'ECO', 'HEAT' and 'OFF'\n        'upperSetpoint': [\n            {\n                common: {\n                    name: 'upperSetpoint',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'level.temperature'\n                },\n                native: {\n                    valueSubKey: 'value',\n                    action: 'setTargetTemperature',\n                    paramName: 'upperSetTemperature.value',\n                    supportedActions: ['setTargetTemperature', 'getTargetTemperature', 'incrementTargetTemperature', 'decrementTargetTemperature'],\n                    sendAdditional: ['lowerSetpoint']\n                },\n                experimental: true\n            },\n            {\n                common: {\n                    name: 'upperSetpoint-scale',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text',\n                    def: 'CELSIUS'\n                },\n                native: {\n                    valueSubKey: 'scale',\n                    action: 'setTargetTemperature',\n                    paramName: 'upperSetTemperature.scale',\n                    noFallbackStringifiedValue: true,\n                    defaultValue: 'CELSIUS'\n                },\n                experimental: true\n            }\n        ],\n        'thermostatMode': [\n            // state: thermostatMode\n            // actions: setThermostatMode ??\n            // values: e.g. 'AUTO', 'COOL', 'ECO', 'HEAT' and 'OFF'\n            // parameters={'action': 'setThermostatMode',\"thermostatMode.value\":req.query.thermostatMode};\n            {\n                common: {\n                    name: 'thermostatMode',\n                    type: 'string', // allowed  auto, cool, heat or off\n                    read: true,\n                    write: true,\n                    role: 'text'\n                },\n                native: {\n                    valueSubKey: 'value',\n                    action: 'setThermostatMode',\n                    paramName: 'thermostatMode.value',\n                    supportedActions: ['setTargetTemperature', 'getTargetTemperature', 'incrementTargetTemperature', 'decrementTargetTemperature']\n                },\n                experimental: true\n            }\n        ]\n    },\n    'Alexa.TemperatureSensor': {\n        'temperature': [\n            // state: temperature\n            // actions: -\n            // values: value=number, 'scale': 'CELSIUS'/'FAHRENHEIT' ??\n            {\n                common: {\n                    name: 'temperature',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    role: 'level.temperature'\n                },\n                native: {\n                    supportedActions: ['getTemperatureReading']\n                }\n            },\n            {\n                common: {\n                    name: 'temperature-scale',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text',\n                    def: 'CELSIUS' // \"CELSIUS\", \"FAHRENHEIT\" or \"KELVIN\"\n                },\n                native: {\n                    noFallbackStringifiedValue: true,\n                    valueSubKey: 'scale',\n                }\n            }\n        ]\n    },\n    'Alexa.ContactSensor': {\n        'detectionState': [\n            {\n                common: {\n                    name: 'detectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'sensor.window'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                },\n                experimental: true\n            }\n        ]\n    },\n    'Alexa.MotionSensor': {\n        'detectionState': [\n            {\n                common: {\n                    name: 'detectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'sensor.motion'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                },\n                experimental: true\n            }\n        ],/*\n        'detectionSensitivity': [\n            {\n                common: {\n                    name: 'detectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'sensor.motion'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                },\n                experimental: true\n            }\n        ],\n        'enablement': [\n            {\n                common: {\n                    name: 'detectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'sensor.motion'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                },\n                experimental: true\n            }\n        ],\n        'detectionRange': [\n            {\n                common: {\n                    name: 'detectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'sensor.motion'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                },\n                experimental: true\n            }\n        ]*/\n    },\n    'Alexa.PercentageController': {\n        'percentage': [\n            // state: percentage\n            // actions: setPercentage\n            // values: 0..100\n            {\n                common: {\n                    name: 'percentage',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'level',\n                    min: 0,\n                    max: 100\n                },\n                native: {\n                    action: 'setPercentage',\n                    factor: 0.01,\n                    supportedActions: ['setPercentage', 'decrementPercentage', 'incrementPercentage']\n                }\n            }\n        ]\n    },\n    'Alexa.BrightnessController': {\n        'brightness': [\n            // state: brightness\n            // actions: setBrightness\n            // values: 0..100\n            {\n                common: {\n                    name: 'brightness',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'level.dimmer',\n                    min: 0,\n                    max: 100\n                },\n                native: {\n                    action: 'setBrightness',\n                    factor: 0.01,\n                    supportedActions: ['setBrightness', 'rampBrightness']\n                }\n            }\n        ]\n    },\n    'Alexa.ColorController': {\n        'color': [\n            // state: 'color': {\n            //    'hue': 350.5, // 0.0 to 360.0\n            //    'saturation': 0.7138, // 0.0 to 1.0\n            //    'brightness': 0.6524 // 0.0 to 1.0\n            // }\n            // actions: setColor\n            // values: see above\n            {\n                common: {\n                    name: 'colorName',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'state',\n                    states: {}\n                },\n                native: {\n                    action: 'setColor',\n                    valueSubKey: 'name', // ColorPropertiesController!!\n                    valueMap: [],\n                    noFallbackStringifiedValue: true,\n                    supportedActions: ['setColor']\n                }\n            },\n            {\n                common: {\n                    name: 'color-hue',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    min: 0,\n                    max: 360,\n                    unit: '°',\n                    role: 'level.color.hue'\n                },\n                native: {\n                    donotsend: true,\n                    valueSubKey: 'hue',\n                    //action: 'setColor',\n                    //paramName: 'color.hue'\n                }\n            },\n            {\n                common: {\n                    name: 'color-saturation',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    min: 0,\n                    max: 1,\n                    role: 'level.color.saturation'\n                },\n                native: {\n                    donotsend: true,\n                    valueSubKey: 'saturation',\n                    //action: 'setColor',\n                    //paramName: 'color.saturation'\n                }\n            },\n            {\n                common: {\n                    name: 'color-brightness',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    min: 0,\n                    max: 1,\n                    role: 'level.dimmer'\n                },\n                native: {\n                    donotsend: true,\n                    valueSubKey: 'brightness',\n                    //action: 'setColor',\n                    //paramName: 'color.brightness',\n                    //factor: 0.01\n                }\n            },\n            {\n                common: {\n                    name: 'colorRgb',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'level.color.rgb'\n                },\n                native: {\n                    donotsend: true,\n                    valueSubKey: 'rgb', // not existing\n                    noFallbackStringifiedValue: true,\n                }\n            }\n        ]\n    },\n    'Alexa.ColorTemperatureController': {\n        'colorTemperatureInKelvin': [\n            // state: 'colorTemperatureInKelvin'\n            // actions: setColorTemperature\n            // values: 1000 to 10000\n            {\n                common: {\n                    name: 'colorTemperatureName',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'state',\n                    states: {}\n                },\n                native: {\n                    action: 'setColorTemperature',\n                    valueSubKey: 'name', // ColorPropertiesController!!\n                    valueMap: [],\n                    paramName: 'colorTemperatureName',\n                    supportedActions: ['setColorTemperature', 'incrementColorTemperature', 'decrementColorTemperature']\n                }\n            },\n            {\n                common: {\n                    name: 'colorTemperatureInKelvin',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    role: 'level.color.temperature',\n                    min: 1000,\n                    max: 10000\n                },\n                native: {\n                    donotsend: true\n                }\n            }\n        ]\n    },\n    'Alexa.ColorPropertiesController': {\n        'colorProperties': [ // Redirect!!\n            'colorName',\n            'colorTemperatureName'\n        ]\n    },\n    /*\n    'Alexa.ChannelController': {\n        'channel': {\n            // state: channel {\n            //      'number': '1234',\n            //      'callSign': 'KSTATION1',\n            //      'affiliateCallSign': 'KSTATION2',\n            //      'uri': 'someUrl'\n            // }\n            // actions: changeChannel\n            // values: 0..100\n            common: {\n                name: 'channel',\n                type: 'number',\n                read: true,\n                write: true,\n                role: 'level',\n                min: 0,\n                max: 100\n            },\n            native: {\n                action: 'changeChannel',\n                factor: 0.01\n            },\n            experimental: true\n            // skipChannels -> 'channelCount' : 5 ??\n        }\n    },*/\n    'Alexa.InputController': {\n        'input': [\n            // state: 'input'\n            // actions: selectInput\n            // values: string\n            {\n                common: {\n                    name: 'input',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'media.input'\n                },\n                native: {\n                    action: 'selectInput',\n                    supportedActions: ['selectInput']\n                }\n            }\n        ]\n    },\n    'Alexa.PlaybackController': {\n        'playbackState': [\n            {\n                // state: playbackState\n                // actions: ??\n                // 'supportedOperations' : ['Play', 'Pause', 'Stop', 'StartOver', 'Previous', 'Next', 'Rewind', 'FastForward'...]\n                common: {\n                    name: 'playbackState',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text'\n                },\n                native: {\n                    supportedActions: []\n                },\n                experimental: true\n            }\n        ]\n    },\n    /*\n    'Alexa.StepSpeaker': {\n        'volumeSteps': {\n            // state: 'volumeSteps'\n            // actions: adjustVolume ??\n            // values: number -100..100\n            common: {\n                name: 'volumeSteps',\n                type: 'number',\n                read: true,\n                write: true,\n                role: 'level.color.temperature'\n            },\n            native: {\n                action: 'setColorTemperature'\n            },\n            experimental: true\n        }\n    },*/\n    /*\n    'Alexa.Speaker': {\n        'volume': {\n            // state: 'volume'\n            // actions: setVolume ??\n            // values: number 0..100\n\n            // state: 'volume'\n            // actions: adjustVolume ??\n            // values: 'volume': number -100..100, 'volumeDefault': false\n\n            // state: 'mute'\n            // actions: setMute ??\n            // values: boolean\n            common: {\n                name: 'volume',\n                type: 'number',\n                read: true,\n                write: true,\n                role: 'level.color.temperature'\n            },\n            native: {\n                action: 'setColorTemperature'\n            },\n            experimental: true\n        }\n    },*/\n    'Alexa.SceneController': {\n        'active' : [ // Do not really exist\n            {\n                common: {\n                    name: 'active',\n                    type: 'boolean',\n                    read: true,\n                    write: true,\n                    role: 'switch'\n                },\n                native: {\n                    valueTrue: 'ACTIVE',\n                    valueFalse: 'INACTIVE',\n                    actionTrue: 'sceneActivate',\n                    actionFalse: 'sceneDeactivate',\n                    supportedActions: ['sceneActivate', 'sceneDeactivate']\n                }\n            }\n        ]\n    },\n    'Alexa.EndpointConnectivity': null, /* { // Makes no sense\n        'networkInterfaces': [\n            {\n                common: {\n                    name: 'networkInterfaces',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text'\n                },\n                native: {\n                    supportedActions: []\n                }\n            }\n        ]\n    },*/\n    'Alexa.EndpointHealth': {\n        'connectivity': [\n            {\n                common: {\n                    name: 'connectivity',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator.connected'\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'value', // value.value = OK ???\n                    valueTrue: 'OK'\n                }\n            },\n            {\n                common: {\n                    name: 'connectivity-reason',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text'\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'reason', // value.reason = ???\n                    noFallbackStringifiedValue: true,\n                }\n            }\n        ],\n        'battery': [\n            {\n                common: {\n                    name: 'lowBattery',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator.lowbat'\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'health', // value.health = ???\n                    valueTrue: 'OK'\n                }\n            },\n            {\n                common: {\n                    name: 'batteryLevel',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    role: 'value.battery',\n                    unit: '%'\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'levelPercentage', // value.levelPercentage = ???\n                    noFallbackStringifiedValue: true,\n                }\n            },\n            {\n                common: {\n                    name: 'batteryHealthState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator',\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'health',\n                    valueTrue: '{\"state\":\"OK\"}'\n                }\n            },\n            {\n                common: {\n                    name: 'batteryChargingHealthState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator',\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'chargingHealth',\n                    valueTrue: '{\"state\":\"OK\"}'\n                }\n            }\n        ]\n    },\n    'Alexa.ToggleController': {\n        'toggleState': [\n            // state: toggleState\n            // actions: turnOnToggle, turnOffToggle\n            // values: ON, OFF\n            {\n                common: {\n                    name: 'toggleState',\n                    type: 'boolean',\n                    read: true,\n                    write: true,\n                    role: 'switch'\n                },\n                native: {\n                    valueTrue: 'ON',\n                    valueFalse: 'OFF',\n                    actionTrue: 'turnOnToggle',\n                    actionFalse: 'turnOffToggle',\n                    supportedActions: ['turnOnToggle', 'turnOffToggle']\n                }\n            }\n\n        ]\n    },\n    'Alexa.ModeController': {\n        'mode': [\n            // state: mode\n            // actions: setModeValue\n            {\n                common: {\n                    name: 'mode',\n                    type: 'string',\n                    read: true,\n                    write: true,\n                    role: 'state'\n                },\n                native: {\n                    supportedActions: ['setModeValue'],\n                    action: 'setModeValue'\n                }\n            }\n\n        ]\n    },\n    'Alexa.RangeController': {\n        'rangeValue': [\n            // state: rangeValue\n            // actions: turnOnToggle, turnOffToggle\n            // values: ON, OFF\n            {\n                common: {\n                    name: 'rangeValue',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'level'\n                },\n                native: {\n                    supportedActions: ['setRangeValue'],\n                    action: 'setRangeValue',\n                    paramName: 'value',\n                    sendInSubStructureOf: 'rangeValue'\n                }\n            }\n\n        ]\n    },\n    'Alexa.InventoryLevelSensor': {\n        'level': [\n            // state: level\n            {\n                common: {\n                    name: 'level',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    role: 'value'\n                },\n                native: {\n                    supportedActions: []\n                }\n            }\n\n        ]\n    },\n    'Alexa.AcousticEventSensor': null, /*{ // Will never be triggered\n        'overallMode': [\n            {\n                common: {\n                    name: 'overallMode',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'text'\n                },\n                native: {\n                    supportedActions: []\n                }\n            }\n        ],\n        'babyCryDetectionState': [\n            {\n                common: {\n                    name: 'babyCryDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'carbonMonoxideSirenDetectionState': [\n            {\n                common: {\n                    name: 'carbonMonoxideSirenDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'snoreDetectionState': [\n            {\n                common: {\n                    name: 'snoreDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'waterSoundsDetectionState': [\n            {\n                common: {\n                    name: 'waterSoundsDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'smokeAlarmDetectionState': [\n            {\n                common: {\n                    name: 'smokeAlarmDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'glassBreakDetectionState': [\n            {\n                common: {\n                    name: 'glassBreakDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'detectionModes': [\n            {\n                common: {\n                    name: 'detectionModes',\n                    type: 'string',\n                    read: true,\n                    write: false,\n                    role: 'json'\n                },\n                native: {\n                    supportedActions: []\n                }\n            }\n        ],\n        'runningWaterDetectionState': [\n            {\n                common: {\n                    name: 'runningWaterDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'coughDetectionState': [\n            {\n                common: {\n                    name: 'coughDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'dogBarkDetectionState': [\n            {\n                common: {\n                    name: 'dogBarkDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'humanPresenceDetectionState': [\n            {\n                common: {\n                    name: 'humanPresenceDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'smokeSirenDetectionState': [\n            {\n                common: {\n                    name: 'smokeSirenDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ],\n        'beepingApplianceDetectionState': [\n            {\n                common: {\n                    name: 'beepingApplianceDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ]\n    },*/\n    'Alexa.Location': {\n        'geoLocation': [\n            {\n                common: {\n                    name: 'latitudeInDegrees',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    role: 'value',\n                    unit: '%'\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'coordinate.latitudeInDegrees'\n                }\n            },\n            {\n                common: {\n                    name: 'longitudeInDegrees',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    role: 'value',\n                    unit: '%'\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'coordinate.longitudeInDegrees'\n                }\n            },\n            {\n                common: {\n                    name: 'accuracyInMeters',\n                    type: 'number',\n                    read: true,\n                    write: false,\n                    role: 'value'\n                },\n                native: {\n                    supportedActions: [],\n                    valueSubKey: 'coordinate.accuracyInMeters'\n                }\n            }\n            // Potentially more data?\n            // https://developer.amazon.com/en-US/docs/alexa/custom-skills/location-services-for-alexa-skills.html#geolocation-interface\n        ]\n    },\n    'Alexa.SecurityPanelController': {\n        'armState': [\n            {\n                common: {\n                    name: 'armState',\n                    type: 'string',\n                    read: true,\n                    write: true,\n                    role: 'text'\n                },\n                native: {\n                    supportedActions: ['controlSecurityPanel'],\n                    action: 'controlSecurityPanel'\n                }\n            }\n        ],\n        'burglaryAlarm': [\n            {\n                common: {\n                    name: 'burglaryAlarm',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator.alarm'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'ALARM',\n                    valueFalse: 'OK'\n                }\n            }\n        ],\n        'carbonMonoxideAlarm': [\n            {\n                common: {\n                    name: 'carbonMonoxideAlarm',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator.alarm'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'ALARM',\n                    valueFalse: 'OK'\n                }\n            }\n        ],\n        'fireAlarm': [\n            {\n                common: {\n                    name: 'fireAlarm',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator.alarm.fire'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'ALARM',\n                    valueFalse: 'OK'\n                }\n            }\n        ],\n        'waterAlarm': [\n            {\n                common: {\n                    name: 'waterAlarm',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'indicator.alarm.flood'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'ALARM',\n                    valueFalse: 'OK'\n                }\n            }\n        ]\n    },\n    'Alexa.Speaker': {\n        'volume': [\n            {\n                common: {\n                    name: 'volume',\n                    type: 'number',\n                    read: true,\n                    write: true,\n                    role: 'level.volume'\n                },\n                native: {\n                    supportedActions: [],\n                    action: 'setVolume'\n                }\n            }\n        ],\n        'muted': [\n            {\n                common: {\n                    name: 'muted',\n                    type: 'boolean',\n                    read: true,\n                    write: true,\n                    role: 'media.mute'\n                },\n                native: {\n                    supportedActions: [],\n                    action: 'setMute'\n                }\n            }\n        ]\n    },\n    'Alexa.EventDetectionSensor': {\n        'humanPresenceDetectionState': [\n            {\n                common: {\n                    name: 'humanPresenceDetectionState',\n                    type: 'boolean',\n                    read: true,\n                    write: false,\n                    role: 'sensor.motion'\n                },\n                native: {\n                    supportedActions: [],\n                    valueTrue: 'DETECTED',\n                    valueFalse: 'NOT_DETECTED'\n                }\n            }\n        ]\n    },\n    'Alexa.ModeIndicator': null,\n    // {\"namespace\":\"Alexa.ModeIndicator\",\"name\":\"securityMode\",\"value\":\"DISABLED\",\"timeOfSample\":\"1970-01-01T00:00:00Z\",\"uncertaintyInMilliseconds\":0,\"deepQuery\":true,\"timeOfStateChange\":\"\"}\n\n    'Alexa.LocalExecutionFlow': null,\n    // {\"namespace\":\"Alexa.LocalExecutionFlow\",\"name\":\"LocalEngineState\",\"value\":{\"engineState\":\"DISABLED\",\"interactiveFocus\":{}},\"timeOfSample\":\"2022-07-13T07:42:24.155Z\",\"uncertaintyInMilliseconds\":0,\"deepQuery\":true,\"timeOfStateChange\":\"\"}\n\n    'Alexa.PlaybackStateReporter': null\n    // {\"namespace\":\"Alexa.PlaybackStateReporter\",\"name\":\"playbackState\",\"value\":{\"state\":\"IDLE\",\"supportedOperations\":[],\"shuffle\":\"NOT_SHUFFLED\",\"repeat\":\"NOT_REPEATED\",\"favorite\":\"NOT_RATED\",\"positionMilliseconds\":0,\"players\":[{\"playerId\":\"Spotify:ESDK\",\"state\":\"IDLE\",\"supportedOperations\":[\"Play\",\"Pause\",\"Stop\",\"Next\",\"Previous\",\"EnableRepeat\",\"EnableRepeatOne\",\"EnableShuffle\"],\"positionMilliseconds\":0,\"shuffle\":\"NOT_SHUFFLED\",\"repeat\":\"NOT_REPEATED\",\"favorite\":\"NOT_RATED\",\"media\":{\"type\":\"\",\"value\":{\"playbackSource\":\"\",\"playbackSourceId\":\"\",\"playbackId\":\"\",\"trackName\":\"\",\"trackId\":\"\",\"trackNumber\":\"\",\"artist\":\"\",\"artistId\":\"\",\"album\":\"\",\"albumId\":\"\",\"coverUrls\":{\"tiny\":\"\",\"small\":\"\",\"medium\":\"\",\"large\":\"\"},\"coverId\":\"\",\"mediaProvider\":\"\",\"mediaType\":\"TRACK\",\"durationInMilliseconds\":0}}}]},\"timeOfSample\":\"2022-07-13T07:42:24.156Z\",\"uncertaintyInMilliseconds\":0,\"deepQuery\":true,\"timeOfStateChange\":\"\"}\n\n    // Alexa.CameraStreamController\n    // Alexa.Cooking und Alexa.Cooking.*\n    // Alexa.TimeHoldController\n    // Alexa.EqualizerController https://developer.amazon.com/de/docs/device-apis/alexa-equalizercontroller.html\n    // Seen in data but never declared on a device\n};\n\n// https://developer.amazon.com/de/docs/archive/shv2-smart-home-skill-api-reference.html\nconst actionObjects =  {\n    'decrementColorTemperature': [],\n    'decrementPercentage': [],\n    'decrementTargetTemperature': [],\n    'getLockState': [],\n    'getTargetTemperature': [],\n    'getTemperatureReading':\n        capabilityObjects['Alexa.TemperatureSensor'].temperature,\n    'incrementColorTemperature': [],\n    'incrementPercentage': [],\n    'incrementTargetTemperature': [],\n    'retrieveCameraStreamUri': [],\n    'setColor':\n        capabilityObjects['Alexa.ColorController'].color,\n    'setColorTemperature':\n        capabilityObjects['Alexa.ColorTemperatureController'].colorTemperatureInKelvin,\n    'setLockState': [\n        // state: lockState\n        // actions: lock, unlock ??\n        // values: 'LOCKED', 'UNLOCKED', 'JAMMED' (??)\n        {\n            common: {\n                name: 'lockState',\n                type: 'boolean',\n                read: true,\n                write: true,\n                role: 'switch.lock'\n            },\n            native: {\n                valueTrue: 'LOCKED',\n                valueFalse: 'UNLOCKED',\n                action: 'lockAction',\n                paramName: 'targetLockState.value',\n                supportedActions: ['setLockState', 'getLockState', 'lockAction']\n            }\n        }\n    ],\n    'lockAction': [\n        // state: lockState\n        // actions: lock, unlock ??\n        // values: 'LOCKED', 'UNLOCKED', 'JAMMED' (??)\n        {\n            common: {\n                name: 'lockState',\n                type: 'boolean',\n                read: true,\n                write: true,\n                role: 'switch.lock'\n            },\n            native: {\n                valueTrue: 'LOCKED',\n                valueFalse: 'UNLOCKED',\n                action: 'lockAction',\n                paramName: 'targetLockState.value',\n                supportedActions: ['setLockState', 'getLockState', 'lockAction']\n            }\n        }\n    ],\n    'setPercentage':\n        capabilityObjects['Alexa.PercentageController'].percentage, //verified!\n    'setBrightness':\n        capabilityObjects['Alexa.BrightnessController'].brightness, //verified!\n    'rampBrightness':\n        capabilityObjects['Alexa.BrightnessController'].brightness,\n    'setTargetTemperature': [\n        {\n            common: {\n                name: 'targetSetpoint',\n                type: 'number',\n                read: true,\n                write: true,\n                role: 'level.temperature'\n            },\n            native: {\n                action: 'setTargetTemperature',\n                paramName: 'targetTemperature.value',\n                supportedActions: ['setTargetTemperature', 'getTargetTemperature', 'incrementTargetTemperature', 'decrementTargetTemperature']\n            }\n        },\n        {\n            common: {\n                name: 'targetSetpoint-scale',\n                type: 'string',\n                read: true,\n                write: false,\n                role: 'text'\n            },\n            native: {\n                valueSubKey: 'scale',\n                action: 'setTargetTemperature',\n                paramName: 'targetTemperature.scale',\n                defaultValue: 'CELSIUS'\n            }\n        }\n    ],\n    'turnOn':\n        capabilityObjects['Alexa.PowerController'].powerState,\n    'turnOff':\n        capabilityObjects['Alexa.PowerController'].powerState,\n    'turnOnOff':\n        capabilityObjects['Alexa.PowerController'].powerState,\n    'sceneActivate':\n        capabilityObjects['Alexa.SceneController'].active,\n    'sceneDeactivate':\n        capabilityObjects['Alexa.SceneController'].active,\n    'turnOnToggle':\n        capabilityObjects['Alexa.ToggleController'].powerState,\n    'turnOffToggle':\n        capabilityObjects['Alexa.ToggleController'].powerState,\n    'selectInput':\n        capabilityObjects['Alexa.InputController'].input,\n    'setPowerLevel':\n        capabilityObjects['Alexa.PowerLevelController'].powerLevel,\n    'setModeValue':\n        capabilityObjects['Alexa.ModeController'].mode,\n    'setRangeValue':\n        capabilityObjects['Alexa.RangeController'].rangeValue,\n    'controlSecurityPanel':\n        capabilityObjects['Alexa.SecurityPanelController'].armState,\n};\n\nconst knownColorValues = {\n    'colorTemperatureName': {\n        'incandescent': 2700,\n        'daytime': 5500,\n        'soft': 2700,\n        'warm_white': 2200,\n        'cool_white': 7000,\n        'reading_white': 2700,\n        'white': 4000,\n        'daytime_white': 5500,\n        'daylight': 5500,\n        'sunset': 2200,\n        'daylight_white': 5500,\n        'warm': 2200,\n        'soft_white':2700,\n        'candlelight': 2200,\n        'cool': 7000,\n        'evening': 2200,\n        'bright_white': 7000,\n        'relax': 2200,\n        'reading': 2700,\n    },\n    'colorName': {\n        'medium_sea_green': '#57ffa0',\n        'dark_turquoise': '#01fbff',\n        'sky_blue': '#93e0ff',\n        'old_lace': '#fff7e8',\n        'light_salmon': '#ffa07a',\n        'ghost_white': '#f7f7ff',\n        'orange_red': '#ff4400',\n        'lime_green': '#40ff40',\n        'deep_pink': '#ff1491',\n        'hot_pink': '#ff68b6',\n        'sea_green': '#52ff9d',\n        'dodger_blue': '#1e8fff',\n        'goldenrod': '#ffc227',\n        'red': '#ff0000',\n        'blue': '#4100ff',\n        'fuchsia': '#ff00ff',\n        'green_yellow': '#afff2d',\n        'pale_goldenrod': '#fffab7',\n        'light_green': '#99ff99',\n        'light_sea_green': '#2ffff5',\n        'saddle_brown': '#ff7c1f',\n        'cornsilk': '#fff7db',\n        'dark_slate_gray': '#91ffff',\n        'gainsboro': '#ffffff',\n        'cadet_blue': '#96fbff',\n        'medium_blue': '#0000ff',\n        'wheat': '#ffe7ba',\n        'indian_red': '#ff7272',\n        'antique_white': '#fff0db',\n        'plum': '#ffb9ff',\n        'papaya_whip': '#ffefd6',\n        'web_maroon': '#ff0000',\n        'lavender_blush': '#ffeff4',\n        'cyan': '#00ffff',\n        'burlywood': '#ffd29c',\n        'floral_white': '#fff9ef',\n        'navajo_white': '#ffddad',\n        'medium_turquoise': '#57fff9',\n        'royal_blue': '#4876ff',\n        'light_goldenrod': '#ffffd6',\n        'navy_blue': '#0000ff',\n        'light_sky_blue': '#8ad2ff',\n        'medium_aquamarine': '#7fffd5',\n        'orchid': '#ff84fd',\n        'seashell': '#fff4ed',\n        'pale_turquoise': '#bcffff',\n        'yellow_green': '#bfff46',\n        'brown': '#ff3d3e',\n        'dark_khaki': '#fff891',\n        'spring_green': '#00ff7f',\n        'dark_violet': '#b300ff',\n        'purple': '#ab24ff',\n        'turquoise': '#48ffed',\n        'dim_gray': '#ffffff',\n        'dark_cyan': '#00ffff',\n        'tan': '#ffddab',\n        'pink': '#ffbfcc',\n        'dark_blue': '#0000ff',\n        'light_steel_blue': '#cae2ff',\n        'rebecca_purple': '#aa55ff',\n        'light_yellow': '#ffffe0',\n        'aqua': '#34feff',\n        'yellow': '#ffff00',\n        'dark_orchid': '#bf40ff',\n        'light_cyan': '#e0ffff',\n        'blue_violet': '#9b30ff',\n        'dark_salmon': '#ffa486',\n        'web_green': '#00ff3d',\n        'moccasin': '#ffe1b5',\n        'forest_green': '#3cff3c',\n        'gold': '#ffd400',\n        'lime': '#c7ff1e',\n        'olive': '#fffc4b',\n        'medium_orchid': '#e066ff',\n        'slate_blue': '#856fff',\n        'dark_green': '#00ff00',\n        'bisque': '#ffe2c4',\n        'coral': '#ff7e4f',\n        'salmon': '#ffa07a',\n        'powder_blue': '#c3f9ff',\n        'steel_blue': '#60b7ff',\n        'lawn_green': '#79ff41',\n        'firebrick': '#ff2f2f',\n        'olive_drab': '#bfff3f',\n        'white_smoke': '#ffffff',\n        'linen': '#fff5eb',\n        'alice_blue': '#eff7ff',\n        'medium_spring_green': '#1aff9d',\n        'violet': '#ff8bff',\n        'light_pink': '#ffb5c1',\n        'dark_magenta': '#ff00ff',\n        'web_gray': '#ffffff',\n        'maroon': '#ff468d',\n        'medium_violet_red': '#ff1aab',\n        'crimson': '#ff2545',\n        'tomato': '#ff6347',\n        'pale_green': '#9dff9d',\n        'white': '#ffffff',\n        'lavender': '#9f7fff',\n        'light_blue': '#c1f0ff',\n        'mint_cream': '#f4fff9',\n        'chocolate': '#ff8025',\n        'dark_red': '#ff0000',\n        'medium_slate_blue': '#8370ff',\n        'light_slate_gray': '#c6e1ff',\n        'magenta': '#ff00ff',\n        'dark_olive_green': '#a1ff6e',\n        'medium_purple': '#ac82ff',\n        'gray': '#ffffff',\n        'silver': '#ffffff',\n        'green': '#00ff00',\n        'chartreuse': '#7fff00',\n        'sienna': '#ff8248',\n        'peach_puff': '#ffd8ba',\n        'midnight_blue': '#3939ff',\n        'thistle': '#ffe2ff',\n        'indigo': '#9000ff',\n        'light_coral': '#ff8888',\n        'blanched_almond': '#ffeacc',\n        'web_purple': '#ff00ff',\n        'slate_gray': '#c9e4ff',\n        'rosy_brown': '#ffc1c1',\n        'sandy_brown': '#ffaa64',\n        'teal': '#34feff',\n        'misty_rose': '#ffe2e0',\n        'pale_violet_red': '#ff82ac',\n        'beige': '#ffffe5',\n        'dark_orange': '#ff8a25',\n        'dark_gray': '#ffffff',\n        'peru': '#ffa44f',\n        'deep_sky_blue': '#38bdff',\n        'dark_goldenrod': '#ffbb0e',\n        'ivory': '#ffffef',\n        'honeydew': '#efffef',\n        'dark_slate_blue': '#826fff',\n        'dark_sea_green': '#c1ffc1',\n        'light_gray': '#ffffff',\n        'cornflower': '#6b9eff',\n        'orange': '#ffa600',\n        'lemon_chiffon': '#fff9cc',\n        'azure': '#efffff',\n        'snow': '#fff9f9',\n        'aquamarine': '#7fffd2',\n        'khaki': '#fff495',\n        'black': '#ffffff'\n    }\n};\n\nconst nearestColor = require('nearest-color').from(knownColorValues.colorName);\n\nfunction patchProperties(props) {\n    if (!Array.isArray(props)) return;\n    for (const prop of props) {\n        switch(prop.id) {\n            case 'setColor':\n                if (prop.parameters) {\n                    for (const param of prop.parameters) {\n                        if (param.name === 'colorName' && param.constraint && param.constraint.options) {\n                            const displayValues = {};\n                            const sendValues =  [];\n                            for (let i = 0; i < param.constraint.options.length; i++) {\n                                let displayName = param.constraint.options[i].displayName;\n                                if (knownColorValues.colorName[param.constraint.options[i].data]) {\n                                    displayName += ' (' + knownColorValues.colorName[param.constraint.options[i].data] + ')';\n                                }\n                                displayValues[i] = displayName;\n                                sendValues[i] = param.constraint.options[i].data;\n                            }\n                            capabilityObjects['Alexa.ColorController'].color[0].common.states = displayValues;\n                            capabilityObjects['Alexa.ColorController'].color[0].native.valueMap = sendValues;\n                        }\n                    }\n                }\n                break;\n            case 'setColorTemperature':\n                if (prop.parameters) {\n                    for (const param of prop.parameters) {\n                        if (param.name === 'colorTemperatureName' && param.constraint && param.constraint.options) {\n                            const displayValues = {};\n                            const sendValues =  [];\n                            for (let i = 0; i < param.constraint.options.length; i++) {\n                                let displayName = param.constraint.options[i].displayName;\n                                if (knownColorValues.colorTemperatureName[param.constraint.options[i].data]) {\n                                    displayName += ' (' + knownColorValues.colorTemperatureName[param.constraint.options[i].data] + 'K)';\n                                }\n                                displayValues[i] = displayName;\n                                sendValues[i] = param.constraint.options[i].data;\n                            }\n                            capabilityObjects['Alexa.ColorTemperatureController'].colorTemperatureInKelvin[0].common.states = displayValues;\n                            capabilityObjects['Alexa.ColorTemperatureController'].colorTemperatureInKelvin[0].native.valueMap = sendValues;\n                        }\n                    }\n                }\n                break;\n        }\n    }\n}\n\nfunction configureCapabilityObject(cap, obj) {\n    if (!cap.configuration) {\n        return obj;\n    }\n    if (cap.configuration.measurement && cap.configuration.measurement['@type']) {\n        switch (cap.configuration.measurement['@type']) {\n            case 'Percentage':\n                obj.common.unit = '%';\n                break;\n        }\n    }\n    if (cap.interfaceName === 'Alexa.ModeController') {\n        if (cap.configuration.supportedModes) {\n            obj.common.states = {};\n            cap.configuration.supportedModes.forEach((mode) => {\n                obj.common.states[mode.value] = mode.value;\n            });\n        }\n    } else if (cap.interfaceName === 'Alexa.RangeController') {\n        if (cap.configuration.supportedRange) {\n            if (cap.configuration.supportedRange.minimumValue !== undefined) {\n                obj.common.min = cap.configuration.supportedRange.minimumValue;\n            }\n            if (cap.configuration.supportedRange.maximumValue !== undefined) {\n                obj.common.max = cap.configuration.supportedRange.maximumValue;\n            }\n            if (cap.configuration.supportedRange.minimumValue !== undefined) {\n                obj.common.step = cap.configuration.supportedRange.precision;\n            }\n        }\n        switch (cap.configuration.unitOfMeasure) {\n            case 'Alexa.Unit.Percent':\n                obj.common.unit = '%';\n                break;\n            case 'Alexa.Unit.Density.MicroGramsPerCubicMeter':\n                obj.common.unit = 'µg/m³';\n                break;\n            case 'Alexa.Unit.PartsPerMillion':\n                obj.common.unit = 'ppm';\n                break;\n        }\n        obj.native.unitOfMeasure = cap.configuration.unitOfMeasure;\n    } else if (cap.interfaceName === 'Alexa.PlaybackController') {\n        if (cap.supportedOperations) {\n            obj.common.states = {};\n            cap.supportedOperations.forEach((operation) => {\n                obj.common.states[operation] = operation;\n            });\n        }\n    } else if (cap.interfaceName === 'Alexa.InputController') {\n        if (cap.inputs) {\n            obj.common.states = {};\n            cap.inputs.forEach((input) => {\n                obj.common.states[input.name] = input.name;\n            });\n        }\n    } else if (cap.interfaceName === 'Alexa.SecurityPanelController') {\n        if (cap.configuration.supportedArmStates) {\n            obj.common.states = {};\n            cap.configuration.supportedArmStates.forEach((state) => {\n                obj.common.states[state.value] = state.value;\n            });\n        } else {\n            obj.common.states = {\n                'ARMED_AWAY': 'ARMED_AWAY',\n                'ARMED_STAY': 'ARMED_STAY',\n                'DISARMED': 'DISARMED',\n                'ARMED_NIGHT': 'ARMED_NIGHT'\n            };\n        }\n    }\n\n    return obj;\n}\n\nmodule.exports = {\n    capabilityObjects,\n    actionObjects,\n    patchProperties,\n    nearestColor,\n    configureCapabilityObject\n};\n"
  },
  {
    "path": "lib/tools.js",
    "content": "const axios = require('axios').default;\n\n/**\n * Tests whether the given variable is a real object and not an Array\n * @param {any} it The variable to test\n * @returns {it is Record<string, any>}\n */\nfunction isObject(it) {\n    // This is necessary because:\n    // typeof null === 'object'\n    // typeof [] === 'object'\n    // [] instanceof Object === true\n    return Object.prototype.toString.call(it) === '[object Object]';\n}\n\n/**\n * Tests whether the given variable is really an Array\n * @param {any} it The variable to test\n * @returns {it is any[]}\n */\nfunction isArray(it) {\n    if (typeof Array.isArray === 'function') return Array.isArray(it);\n    return Object.prototype.toString.call(it) === '[object Array]';\n}\n\n/**\n * Translates text to the target language. Automatically chooses the right translation API.\n * @param {string} text The text to translate\n * @param {string} targetLang The target languate\n * @param {string} [yandexApiKey] The yandex API key. You can create one for free at https://translate.yandex.com/developers\n * @returns {Promise<string>}\n */\nasync function translateText(text, targetLang, yandexApiKey) {\n    if (targetLang === 'en') {\n        return text;\n    } else if (!text) {\n        return '';\n    }\n    if (yandexApiKey) {\n        return translateYandex(text, targetLang, yandexApiKey);\n    } else {\n        return translateGoogle(text, targetLang);\n    }\n}\n\n/**\n * Translates text with Yandex API\n * @param {string} text The text to translate\n * @param {string} targetLang The target languate\n * @param {string} apiKey The yandex API key. You can create one for free at https://translate.yandex.com/developers\n * @returns {Promise<string>}\n */\nasync function translateYandex(text, targetLang, apiKey) {\n    if (targetLang === 'zh-cn') {\n        targetLang = 'zh';\n    }\n    try {\n        const url = `https://translate.yandex.net/api/v1.5/tr.json/translate?key=${apiKey}&text=${encodeURIComponent(text)}&lang=en-${targetLang}`;\n        const response = await axios({url, timeout: 15000});\n        if (response.data && response.data.text && isArray(response.data.text)) {\n            return response.data.text[0];\n        }\n        throw new Error('Invalid response for translate request');\n    } catch (e) {\n        throw new Error(`Could not translate to \"${targetLang}\": ${e}`);\n    }\n}\n\n/**\n * Translates text with Google API\n * @param {string} text The text to translate\n * @param {string} targetLang The target languate\n * @returns {Promise<string>}\n */\nasync function translateGoogle(text, targetLang) {\n    try {\n        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`;\n        const response = await axios({url, timeout: 15000});\n        if (isArray(response.data)) {\n            // we got a valid response\n            return response.data[0][0][0];\n        }\n        throw new Error('Invalid response for translate request');\n    } catch (e) {\n        if (e.response && e.response.status === 429) {\n            throw new Error(\n                `Could not translate to \"${targetLang}\": Rate-limited by Google Translate`\n            );\n        } else {\n            throw new Error(`Could not translate to \"${targetLang}\": ${e}`);\n        }\n    }\n}\n\nmodule.exports = {\n    isArray,\n    isObject,\n    translateText\n};\n"
  },
  {
    "path": "main.js",
    "content": "﻿/* eslint-disable no-unused-vars */\nconst Alexa = require('alexa-remote2');\nconst path = require('path');\nconst fs = require('fs');\nconst os = require('os');\nconst utils = require('@iobroker/adapter-core'); // Get common adapter utils\nconst shObjects = require('./lib/smarthomedevices.js');\nconst { rrulestr } = require('rrule');\n\nlet Sentry;\n\nlet alexa;\nlet adapter;\nlet stopped = false;\n\nconst playerControls = {\n    controlPlay: { command: 'play', val: false, common: { type: 'boolean', read: false, write: true, role: 'button.play'}},\n    controlPause:{ command: 'pause', val: false, common: { type: 'boolean', read: false, write: true, role: 'button.pause'}}\n};\n\nconst musicControls = {\n    controlNext: { command: 'next', val: false, common: { type: 'boolean', read: false, write: true, role: 'button.next'}},\n    controlPrevious: { command: 'previous', val: false, common: { type: 'boolean', read: false, write: true, role: 'button.prev'}},\n    controlForward: { command: 'forward', val: false, common: { type: 'boolean', read: false, write: true, role: 'button.forward'}},\n    controlRewind: { command: 'rewind', val: false, common: { type: 'boolean', read: false, write: true, role: 'button.reverse'}},\n    controlShuffle: { command: 'shuffle', val: false, common: { type: 'boolean', read: false, write: true, role: 'media.mode.shuffle'}},\n    controlRepeat: { command: 'repeat', val: false, common: { type: 'boolean', read: false, write: true, role: 'media.mode.repeat'}},\n};\n\nconst listObjects = {\n    'archived': { type: 'boolean', role: 'indicator' },\n    'createdDate': { type: 'number', role: 'date' },\n    'customerId': { type: 'string', role: 'text' },\n    'defaultList': { type: 'boolean', role: 'indicator' },\n    'itemId': { type: 'string', role: 'text' },\n    'listReorderVersion': { type: 'number', role: 'value' },\n    'name': { type: 'string', role: 'text' },\n    'nbestItems': { type: 'string', role: 'text' },\n    'originalAudioId': { type: 'string', role: 'text' },\n    'type': { type: 'string', role: 'text' },\n    'updatedDate': { type: 'number', role: 'date' },\n    'version': { type: 'number', role: 'value' }\n};\n\nconst listItemsObjects = {\n    '#delete': { type: 'boolean', role: 'button' },\n    'completed': { type: 'boolean', role: 'indicator', write: true },\n    'createdDateTime': { type: 'number', role: 'date' },\n    'customerId': { type: 'string', role: 'text' },\n    'id': { type: 'string', role: 'text' },\n    'listId': { type: 'string', role: 'text' },\n    'shoppingListItem': { type: 'boolean', role: 'indicator' },\n    'updatedDateTime': { type: 'number', role: 'date' },\n    'value': { type: 'string', role: 'text', write: true },\n    'version': { type: 'number', role: 'value' }\n};\n\nconst commands = {\n    'weather': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'traffic': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'flashbriefing': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'goodmorning': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'singasong': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'tellstory': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'deviceStop': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'calendarToday': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'calendarTomorrow': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'calendarNext': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'notification': { val: '', common: { type: 'string', read: false, write: true, role: 'text'}},\n    'funfact': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'joke': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'cleanup': { val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'curatedtts': { val: '', common: { type: 'string', read: false, write: true, role: 'text'}},\n    'textCommand': { val: '', common: { type: 'string', read: false, write: true, role: 'text'}},\n    'sound': { val: '', common: { type: 'string', read: false, write: true, role: 'text'}},\n    'skill': { val: '', common: { type: 'string', read: false, write: true, role: 'text'}},\n    'skillYours': { command: 'skill', val: '', common: { type: 'string', read: false, write: true, role: 'text'}}\n};\n\nconst fireTVCommands = {\n    'turnOn': { command: 'fireTVTurnOn', val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'turnOff': { command: 'fireTVTurnOff', val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'videoPause': { command: 'fireTVPauseVideo', val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'videoResume': { command: 'fireTVResumeVideo', val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'navigateHome': { command: 'fireTVNavigateHome', val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}}\n};\n\nconst allDevicesCommands = {\n    'deviceStop': { command: 'deviceStopAll', val: false, common: { type: 'boolean', read: false, write: true, role: 'button'}},\n    'deviceDoNotDisturb': { command: 'deviceDoNotDisturbAll', val: false, common: { type: 'mixed', read: false, write: true, role: 'state'}}\n};\n\nconst knownDeviceType = {\n    'A10A33FOX2NUBK':   {name: 'Echo Spot', commandSupport: true, icon: 'icons/spot.png'},\n    'A10L5JEZTKKCZ8':   {name: 'Vobot-Clock', commandSupport: true}, // REMINDERS,VOLUME_SETTING,TUNE_IN,MUSIC_SKILL,TIMERS_AND_ALARMS,I_HEART_RADIO,PEONY,AUDIO_PLAYER,DEREGISTER_DEVICE,SLEEP,CHANGE_NAME,GOLDFISH,AUDIBLE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,KINDLE_BOOKS,PERSISTENT_CONNECTION,MICROPHONE,DREAM_TRAINING,AMAZON_MUSIC\n    'A112LJ20W14H95':   {name: 'webOS TV', commandSupport: true}, // HANDS_FREE,DREAM_TRAINING,SUPPORTS_LOCALE_SWITCH,ASCENDING_ALARM_VOLUME,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AUDIO_PLAYER,CUSTOM_ALARM_TONE,SET_TIME_ZONE,AMAZON_MUSIC,TIMERS_AND_ALARMS,GOLDFISH,REMINDERS,TUNE_IN,TOUCH_INITIATED,TIDAL,DEEZER,MICROPHONE,MULTI_WAKEWORDS_SUPPORTED,KINDLE_BOOKS,PERSISTENT_CONNECTION,FAR_FIELD,MUSIC_SKILL,SOUND_SETTINGS,SUPPORTS_LOCALE,APPLE_MUSIC,SET_LOCALE,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,VOLUME_SETTING,DIALOG_INTERFACE_VERSION,ADAPTIVE_LISTENING,CHANGE_NAME,DEREGISTER_DEVICE,AUDIBLE,SIRIUSXM,SPEECH_RECOGNIZER_USS,SLEEP,EARCONS,I_HEART_RADIO\n    'A11QM4H9HGV71H':   {name: 'Echo Show 5 3rd Gen', commandSupport: true, icon: 'icons/echo_show5.png'}, // SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SUPPORTS_SOFTWARE_VERSION,DREAM_TRAINING,SIRIUSXM,TUNE_IN,CUSTOM_ALARM_TONE,TUPLE,ADAPTIVE_LISTENING,SUPPORTS_LOCALE_SWITCH,EQUALIZER_CONTROLLER_TREBLE,FLASH_BRIEFING,ALEXA_PRESENCE,PAIR_BT_SINK,AUDIO_PLAYER,LEMUR_ALPHA,TUPLE_CATEGORY_C,EQUALIZER_CONTROLLER_BASS,DISPLAY_BRIGHTNESS_ADJUST,MICROPHONE,LIVE_VIEW,AUDIO_CONTROLS,KINDLE_BOOKS,SALMON,EARCONS,CLOCK_FORMAT_24_HR,TAP_GESTURES_SINGLE_TAP,SHARKNADO,TIDAL,DISPLAY_ADAPTIVE_BRIGHTNESS,EQUALIZER_CONTROLLER_MIDRANGE,PANDORA,DIALOG_INTERFACE_VERSION,EFDCARDS,REMINDERS,SOUND_SETTINGS,SUPPORT_CALENDAR_ALERT,TIMERS_AND_ALARMS,CHANGE_NAME,DEEZER,AUDIBLE,ASCENDING_ALARM_VOLUME,VOLUME_SETTING,ALEXA_VOICE,DS_VOLUME_SETTING,DISPLAY_POWER_TOGGLE,APPLE_MUSIC,SET_LOCALE,VOICE_TRAINING,BT_PAIRING_FLOW_V2,AMAZON_MUSIC,I_HEART_RADIO,GUARD_EARCON,TAP_GESTURES,MUSIC_SKILL,GOLDFISH,FAR_FIELD_WAKE_WORD,DEREGISTER_DEVICE,SLEEP,SET_TIME_ZONE,SUPPORTS_LOCALE,SPEECH_RECOGNIZER_USS,MULTI_WAKEWORDS_SUPPORTED,ALEXA_GESTURES,PERSISTENT_CONNECTION,TIMERS_ALARMS_NOTIFICATIONS_VOLUME\n    'A12GXV8XMS007S':   {name: 'FireTV', commandSupport: true, icon: 'icons/firetv.png'}, // ARTHUR_TARGET,BT_PAIRING_FLOW_V2,REMINDERS,CHANGE_NAME,ACTIVE_AFTER_FRO,SUPPORTS_LOCALE,FLASH_BRIEFING,ASCENDING_ALARM_VOLUME,TIMERS_AND_ALARMS,PAIR_BT_SINK,PERSISTENT_CONNECTION,SOUND_SETTINGS,SUPPORTS_SOFTWARE_VERSION,MULTI_WAKEWORDS_SUPPORTED,SHARKNADO,SPEECH_RECOGNIZER_USS,CUSTOM_ALARM_TONE,EARCONS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SUPPORTS_LOCALE_SWITCH,VOLUME_SETTING,MICROPHONE,DIALOG_INTERFACE_VERSION\n    'A15996VY63BQ2D':   {name: 'Echo Show 8 2nd Gen', commandSupport: true, icon: 'icons/echo_show.png'}, // TIMERS_ALARMS_NOTIFICATIONS_VOLUME,SOUND_SETTINGS,DEREGISTER_DEVICE,DS_VOLUME_SETTING,BT_PAIRING_FLOW_V2,ADAPTIVE_LISTENING,SET_LOCALE,GUARD_EARCON,PERSISTENT_CONNECTION,LOCALIZATION,VOICE_TRAINING,ALEXA_VOICE,SUPPORT_CALENDAR_ALERT,DEEZER,MUSIC_SKILL,AUDIO_CONTROLS,TUNE_IN,EFDCARDS,SPEECH_RECOGNIZER_USS,EQUALIZER_CONTROLLER_TREBLE,DIALOG_INTERFACE_VERSION,TUPLE_CATEGORY_B,APPLE_MUSIC,SLEEP,PAIR_BT_SINK,SIRIUSXM,VISUAL_GESTURE,SUPPORTS_SOFTWARE_VERSION,TIMERS_AND_ALARMS,FAR_FIELD_WAKE_WORD,FLASH_BRIEFING,AUDIBLE,EQUALIZER_CONTROLLER_BASS,SUPPORTS_LOCALE,I_HEART_RADIO,SUPPORTS_LOCALE_SWITCH,KINDLE_BOOKS,CUSTOM_ALARM_TONE,SALMON,ALEXA_PRESENCE,MULTI_WAKEWORDS_SUPPORTED,CHANGE_NAME,TUPLE,MICROPHONE,EARCONS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AMAZON_MUSIC,VOLUME_SETTING,LEMUR_ALPHA,PANDORA,GOLDFISH,SHARKNADO,CLOCK_FORMAT_24_HR,LIVE_VIEW,POPTART,FACE_TO_TALK,TIDAL,REMINDERS,DREAM_TRAINING,AUDIO_PLAYER,ASCENDING_ALARM_VOLUME,EQUALIZER_CONTROLLER_MIDRANGE\n    'A15ERDAKK5HQQG':   {name: 'Sonos', commandSupport: false, icon: 'icons/sonos.png'}, //? AUDIO_PLAYER,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AMAZON_MUSIC,TUNE_IN,PANDORA,REMINDERS,I_HEART_RADIO,CHANGE_NAME,VOLUME_SETTING,PEONY\n    'A17LGWINFBUTZZ':   {name: 'Anker Roav Viva Alexa', commandSupport: false}, // PERSISTENT_CONNECTION,PEONY,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,VOLUME_SETTING,MICROPHONE,AUDIO_PLAYER,AMAZON_MUSIC,TUNE_IN,I_HEART_RADIO,KINDLE_BOOKS,GOLDFISH,TIMERS_AND_ALARMS,DREAM_TRAINING,DEREGISTER_DEVICE,REMINDERS,SLEEP,AUDIBLE,CHANGE_NAME\n    'A18O6U1UQFJ0XK':   {name: 'Echo Plus 2.Gen', commandSupport: true, icon: 'icons/echo_plus2.png'}, // PERSISTENT_CONNECTION,ACTIVE_AFTER_FRO,PAIR_BT_SINK,GADGETS,ASCENDING_ALARM_VOLUME,SET_LOCALE,MICROPHONE,VOLUME_SETTING,AUDIBLE,PAIR_BT_SOURCE,AUDIO_PLAYER,DREAM_TRAINING,FAR_FIELD_WAKE_WORD,UPDATE_WIFI,TUPLE,CUSTOM_ALARM_TONE,REQUIRES_OOBE_FOR_SETUP,EARCONS,KINDLE_BOOKS,SUPPORTS_SOFTWARE_VERSION,ALLOW_LOG_UPLOAD,POPTART,GOLDFISH,DEREGISTER_DEVICE,SLEEP,TAHOE_BYOD,VOICE_TRAINING,SOUND_SETTINGS,CHANGE_NAME,FLASH_BRIEFING,AUX_SETTINGS,REMINDERS,LEMUR_ALPHA,PAIR_REMOTE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TUPLE_CATEGORY_A,TIMERS_AND_ALARMS\n    'A1C66CX2XD756O':   {name: 'Fire HD 8', commandSupport: true, icon: 'icons/firetab.png'}, // MICROPHONE,ASX_TIME_ZONE,VOLUME_SETTING,SUPPORTS_SOFTWARE_VERSION,REMINDERS,PEONY,TIMERS_AND_ALARMS,PERSISTENT_CONNECTION\n    'A1DL2DVDQVK3Q':    {name: 'Apps', commandSupport: true, icon: 'icons/apps.png'}, // (PEONY,VOLUME_SETTING)\n    'A1EIANJ7PNB0Q7':   {name: 'Echo Show 15', commandSupport: true, icon: 'icons/echo_show.png'}, // KINDLE_BOOKS,ASCENDING_ALARM_VOLUME,FLASH_BRIEFING,SUPPORTS_SOFTWARE_VERSION,ALEXA_PRESENCE,EFDCARDS,POPTART,PANDORA,DIALOG_INTERFACE_VERSION,SPEECH_RECOGNIZER_USS,VOICE_TRAINING,ADAPTIVE_LISTENING,VOLUME_SETTING,FAR_FIELD_WAKE_WORD,CUSTOM_ALARM_TONE,BT_PAIRING_FLOW_V2,MULTI_WAKEWORDS_SUPPORTED,ALEXA_VOICE,GOLDFISH,SOUND_SETTINGS,SIRIUSXM,EARCONS,EQUALIZER_CONTROLLER_TREBLE,AUDIBLE,EQUALIZER_CONTROLLER_BASS,LEMUR_ALPHA,TIMERS_AND_ALARMS,SUPPORTS_LOCALE,PERSISTENT_CONNECTION,SET_LOCALE,EQUALIZER_CONTROLLER_MIDRANGE,TUNE_IN,APPLE_MUSIC,DREAM_TRAINING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SLEEP,AUDIO_PLAYER,SALMON,TUPLE,CHANGE_NAME,GUARD_EARCON,DEEZER,TUPLE_CATEGORY_B,SUPPORTS_LOCALE_SWITCH,MUSIC_SKILL,AUDIO_CONTROLS,FACTORY_RESET_DEVICE,I_HEART_RADIO,REMINDERS,MICROPHONE,SHARKNADO,PAIR_BT_SINK,TIDAL,SUPPORT_CALENDAR_ALERT,DS_VOLUME_SETTING,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,LIVE_VIEW,AMAZON_MUSIC\n    'A1ETW4IXK2PYBP':   {name: 'Echo Auto', commandSupport: false}, // CHANGE_NAME,AUDIBLE,TIMERS_AND_ALARMS,REMINDERS,AUDIO_PLAYER,SUPPORTS_LOCALE,DEREGISTER_DEVICE,AMAZON_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,MUSIC_SKILL,I_HEART_RADIO,SET_LOCALE,CUSTOM_ALARM_TONE,SET_TIME_ZONE,TUNE_IN,MICROPHONE,PERSISTENT_CONNECTION,KINDLE_BOOKS,DREAM_TRAINING,SOUND_SETTINGS,GOLDFISH,VOLUME_SETTING,SUPPORTS_LOCALE_SWITCH,SLEEP\n    'A1GIZO9LR81BL5':   {name: 'Yamaha ATS-1090', commandSupport: true}, // DREAM_TRAINING,SIRIUSXM,I_HEART_RADIO,KINDLE_BOOKS,AMAZON_MUSIC,VOLUME_SETTING,SOUND_SETTINGS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,MUSIC_SKILL,DEEZER,AUDIBLE,DIALOG_INTERFACE_VERSION,APPLE_MUSIC,SLEEP,PERSISTENT_CONNECTION,CUSTOM_ALARM_TONE,TIMERS_AND_ALARMS,CHANGE_NAME,MICROPHONE,ADAPTIVE_LISTENING,FAR_FIELD,AUDIO_PLAYER,TIDAL,REMINDERS,GOLDFISH,TOUCH_INITIATED,TUNE_IN,DEREGISTER_DEVICE\n    'A1H0CMF1XM0ZP4':   {name: 'Echo Dot/Bose', commandSupport: false}, // ??? // CHANGE_NAME,AUDIO_PLAYER,AMAZON_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,VOLUME_SETTING,LAMBDA // Bose: LAMBDA_DOWNCHANNEL,AUDIO_PLAYER,CHANGE_NAME,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AMAZON_MUSIC,PANDORA,PEONY,I_HEART_RADIO,TUNE_IN,REMINDERS,VOLUME_SETTING\n    'A1J16TEDOYCZTN':   {name: 'Fire tab', commandSupport: true, icon: 'icons/firetab.png'}, // (PEONY,MICROPHONE,SUPPORTS_SOFTWARE_VERSION,VOLUME_SETTING,ASX_TIME_ZONE,REMINDERS)\n    'A1JJ0KFC4ZPNJ3':   {name: 'Echo Input', commandSupport: true, icon: 'icons/echo-input.png'}, // (ACTIVE_AFTER_FRO,TIMERS_AND_ALARMS,SET_LOCALE,TAHOE_BYOD,AUDIBLE,FLASH_BRIEFING,I_HEART_RADIO,GOLDFISH,DREAM_TRAINING,KINDLE_BOOKS,REMINDERS,GADGETS,ALLOW_LOG_UPLOAD,SOUND_SETTINGS,FAR_FIELD_WAKE_WORD,PAIR_BT_SINK,DEREGISTER_DEVICE,AMAZON_MUSIC,LEMUR_ALPHA,VOICE_TRAINING,MICROPHONE,CHANGE_NAME,SUPPORTS_SOFTWARE_VERSION,SALMON,PAIR_BT_SOURCE,CUSTOM_ALARM_TONE,SLEEP,PANDORA,AUDIO_PLAYER,ASCENDING_ALARM_VOLUME,DS_VOLUME_SETTING,POPTART,PERSISTENT_CONNECTION,REQUIRES_OOBE_FOR_SETUP,VOLUME_SETTING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,MUSIC_SKILL,UPDATE_WIFI,EARCONS,TUNE_IN,SUPPORT_CALENDAR_ALERT)\n    'A1JJTI4FXQ7JCR':   {name: 'Amazfit GTS 4 mini', commandSupport: true}, // SET_LOCALE,SPEECH_RECOGNIZER_USS,MULTI_WAKEWORDS_SUPPORTED,KINDLE_BOOKS,MUSIC_SKILL,CUSTOM_ALARM_TONE,SET_TIME_ZONE,EARCONS,APPLE_MUSIC,DEREGISTER_DEVICE,PERSISTENT_CONNECTION,DEEZER,MICROPHONE,SIRIUSXM,AMAZON_MUSIC,REMINDERS,ADAPTIVE_LISTENING,SUPPORTS_LOCALE,SOUND_SETTINGS,SLEEP,AUDIBLE,DREAM_TRAINING,I_HEART_RADIO,CHANGE_NAME,TOUCH_INITIATED,GOLDFISH,TIDAL,AUDIO_PLAYER,VOLUME_SETTING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TUNE_IN,TIMERS_AND_ALARMS,TIMERS_ALARMS_NOTIFICATIONS_VOLUME\n    'A1LOQ8ZHF4G510':   {name: 'Samsung Soundbar Q990B', commandSupport: true}, // KINDLE_BOOKS,CHANGE_NAME,AUDIO_CONTROLS,I_HEART_RADIO,SUPPORTS_LOCALE,SLEEP,TUNE_IN,TOUCH_INITIATED,AUDIBLE,EARCONS,SET_LOCALE,APPLE_MUSIC,TIDAL,CUSTOM_ALARM_TONE,REMINDERS,DIALOG_INTERFACE_VERSION,ADAPTIVE_LISTENING,DEREGISTER_DEVICE,EQUALIZER_CONTROLLER_MIDRANGE,AUDIO_PLAYER,FAR_FIELD,SOUND_SETTINGS,DREAM_TRAINING,SET_TIME_ZONE,AMAZON_MUSIC,TIMERS_AND_ALARMS,ASCENDING_ALARM_VOLUME,EQUALIZER_CONTROLLER_BASS,DEEZER,VOLUME_SETTING,BT_PAIRING_FLOW_V2,EQUALIZER_CONTROLLER_TREBLE,SIRIUSXM,PERSISTENT_CONNECTION,MICROPHONE,GOLDFISH,SUPPORTS_LOCALE_SWITCH,MUSIC_SKILL,PAIR_BT_SINK,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,MULTI_WAKEWORDS_SUPPORTED,SPEECH_RECOGNIZER_USS\n    'A1MKGHX5VQBDWX':   {name: 'Denon Home 150', commandSupport: true}, // TIMERS_AND_ALARMS,AMAZON_MUSIC,FAR_FIELD,SUPPORTS_LOCALE_SWITCH,SET_LOCALE,DIALOG_INTERFACE_VERSION,EQUALIZER_CONTROLLER_TREBLE,SPEECH_RECOGNIZER_USS,MICROPHONE,REMINDERS,VOLUME_SETTING,EQUALIZER_CONTROLLER_MIDRANGE,AUDIBLE,TUNE_IN,SIRIUSXM,KINDLE_BOOKS,EQUALIZER_CONTROLLER_BASS,GOLDFISH,ASCENDING_ALARM_VOLUME,APPLE_MUSIC,SUPPORTS_LOCALE,DEEZER,SET_TIME_ZONE,CHANGE_NAME,SOUND_SETTINGS,AUDIO_CONTROLS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CUSTOM_ALARM_TONE,EARCONS,TIDAL,MULTI_WAKEWORDS_SUPPORTED,PERSISTENT_CONNECTION,MUSIC_SKILL,DEREGISTER_DEVICE,I_HEART_RADIO,SLEEP,DREAM_TRAINING,ADAPTIVE_LISTENING,AUDIO_PLAYER\n    'A1NL4BVLQ4L3N3':   {name: 'Echo Show', commandSupport: true, icon: 'icons/echo_show.png'}, // PAIR_BT_SINK,CUSTOM_ALARM_TONE,TIMERS_AND_ALARMS,TUNE_IN,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SOUND_SETTINGS,SET_LOCALE,SLEEP,EARCONS,SHARKNADO,SUPPORTS_SOFTWARE_VERSION,TUPLE_CATEGORY_B,MICROPHONE,SALMON,ALLOW_LOG_UPLOAD,CHANGE_NAME,FAR_FIELD_WAKE_WORD,VOLUME_SETTING,AUDIO_PLAYER,I_HEART_RADIO,REMINDERS,ASCENDING_ALARM_VOLUME,PERSISTENT_CONNECTION,AUDIBLE,GADGETS,AMAZON_MUSIC,VOICE_TRAINING,FLASH_BRIEFING,GOLDFISH,FACTORY_RESET_DEVICE,TUPLE,PANDORA,DREAM_TRAINING,LEMUR_ALPHA,POPTART,KINDLE_BOOKS\n    'AMCZ48H33RCDF':    {name: 'Samsung Soundbar Q930B', commandSupport: true}, // SET_TIME_ZONE,EQUALIZER_CONTROLLER_BASS,TUNE_IN,EQUALIZER_CONTROLLER_TREBLE,AUDIO_PLAYER,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,ADAPTIVE_LISTENING,SIRIUSXM,ASCENDING_ALARM_VOLUME,SUPPORTS_LOCALE_SWITCH,MICROPHONE,SET_LOCALE,APPLE_MUSIC,CUSTOM_ALARM_TONE,CHANGE_NAME,FAR_FIELD,GOLDFISH,BT_PAIRING_FLOW_V2,REMINDERS,VOLUME_SETTING,SLEEP,TIMERS_AND_ALARMS,SOUND_SETTINGS,SPEECH_RECOGNIZER_USS,DEEZER,MUSIC_SKILL,DIALOG_INTERFACE_VERSION,EQUALIZER_CONTROLLER_MIDRANGE,AMAZON_MUSIC,TIDAL,MULTI_WAKEWORDS_SUPPORTED,TOUCH_INITIATED,SUPPORTS_LOCALE,AUDIO_CONTROLS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AUDIBLE,DEREGISTER_DEVICE,PERSISTENT_CONNECTION,KINDLE_BOOKS,I_HEART_RADIO,DREAM_TRAINING,PAIR_BT_SINK,EARCONS\n    'A1NQ0LXWBGVQS9':   {name: 'Samsung The Frame ', commandSupport: true}, // DEREGISTER_DEVICE,AUDIO_PLAYER,SLEEP,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,GOLDFISH,MUSIC_SKILL,AUDIBLE,SPEECH_RECOGNIZER_USS,SOUND_SETTINGS,ASCENDING_ALARM_VOLUME,TIMERS_AND_ALARMS,SUPPORTS_LOCALE,CHANGE_NAME,DEEZER,CUSTOM_ALARM_TONE,SET_TIME_ZONE,TUNE_IN,AMAZON_MUSIC,SIRIUSXM,EARCONS,PERSISTENT_CONNECTION,TIDAL,APPLE_MUSIC,SET_LOCALE,DIALOG_INTERFACE_VERSION,REMINDERS,MICROPHONE,TOUCH_INITIATED,MULTI_WAKEWORDS_SUPPORTED,I_HEART_RADIO,VOLUME_SETTING,DREAM_TRAINING,HANDS_FREE,ADAPTIVE_LISTENING,SUPPORTS_LOCALE_SWITCH,KINDLE_BOOKS\n    'A1P31Q3MOWSHOD':   {name: 'Anker Zalo Halo Speaker', commandSupport: false}, // (PEONY,PERSISTENT_CONNECTION,AMAZON_MUSIC,TUNE_IN,MICROPHONE,DREAM_TRAINING,DEREGISTER_DEVICE,KINDLE_BOOKS,MUSIC_SKILL,REMINDERS,AUDIO_PLAYER,GOLDFISH,TIMERS_AND_ALARMS,...\n    'A1Q7QCGNMXAKYW':   {name: 'Fire tab 7 (Partial command support)', commandSupport: true, icon: 'icons/firetab.png'}, // (MICROPHONE,CUSTOM_ALARM_TONE,REMINDERS,SOUND_SETTINGS,DIALOG_INTERFACE_VERSION,ASX_TIME_ZONE,SUPPORTS_LOCALE,PERSISTENT_CONNECTION,VOLUME_SETTING,SUPPORTS_SOFTWARE_VERSION,TIMERS_AND_ALARMS,ASCENDING_ALARM_VOLUME,SUPPORTS_LOCALE_SWITCH,NO_TIME_ZONE_SETTING)\n    'A1QKZ9D0IJY332':   {name: 'Samsung QLED (Partial command support)', commandSupport: true}, // (AUDIO_PLAYER,GOLDFISH,AMAZON_MUSIC,APPLE_MUSIC,SIRIUSXM,MICROPHONE,SPEECH_RECOGNIZER_USS,AUDIBLE,SET_TIME_ZONE,ASCENDING_ALARM_VOLUME,PERSISTENT_CONNECTION,VOLUME_SETTING,DEEZER,EARCONS,TUNE_IN,CUSTOM_ALARM_TONE,SUPPORTS_LOCALE,TIMERS_AND_ALARMS,MUSIC_SKILL,MULTI_WAKEWORDS_SUPPORTED,SUPPORTS_LOCALE_SWITCH,KINDLE_BOOKS,DEREGISTER_DEVICE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SOUND_SETTINGS,CHANGE_NAME,DREAM_TRAINING,SLEEP,REMINDERS,I_HEART_RADIO,SET_LOCALE,TIDAL)\n    'A1RABVCI4QCIKC':   {name: 'Echo Dot 3.Gen', commandSupport: true, icon: 'icons/echo_dot3.png'}, // (SUPPORT_CALENDAR_ALERT,DEREGISTER_DEVICE,CUSTOM_ALARM_TONE,TUPLE_CATEGORY_A,AUDIBLE,ASCENDING_ALARM_VOLUME,EARCONS,SALMON,ACTIVE_AFTER_FRO,SLEEP,REQUIRES_OOBE_FOR_SETUP,FAR_FIELD_WAKE_WORD,FLASH_BRIEFING,PERSISTENT_CONNECTION,PANDORA,SUPPORTS_SOFTWARE_VERSION,I_HEART_RADIO,BT_PAIRING_FLOW_V2,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AMAZON_MUSIC,PAIR_BT_SINK,ALEXA_PRESENCE,ALLOW_LOG_UPLOAD,SET_LOCALE,PAIR_REMOTE,UPDATE_WIFI,KINDLE_BOOKS,DREAM_TRAINING,MUSIC_SKILL,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,DS_VOLUME_SETTING,TUNE_IN,REMINDERS,AUDIO_PLAYER,VOLUME_SETTING,PAIR_BT_SOURCE,GADGETS,GOLDFISH,CHANGE_NAME,VOICE_TRAINING,TUPLE,POPTART,TAHOE_BYOD,MICROPHONE,DIALOG_INTERFACE_VERSION,TIMERS_AND_ALARMS,SOUND_SETTINGS,LEMUR_ALPHA)\n    'A1RTAM01W29CUP':   {name: 'Windows App', commandSupport: false, icon: 'icons/apps.png'}, // FLASH_BRIEFING,AUDIO_PLAYER,VOLUME_SETTING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SET_LOCALE,SOUND_SETTINGS,PERSISTENT_CONNECTION,KINDLE_BOOKS,CHANGE_NAME,TUNE_IN,TIMERS_AND_ALARMS,AUDIBLE,AMAZON_MUSIC,MICROPHONE,I_HEART_RADIO,EARCONS,REMINDERS,SLEEP,DREAM_TRAINING,DEREGISTER_DEVICE\n    'A1X7HJX9QL16M5':   {name: 'Bespoken.io', commandSupport: false},\n    'A1XWJRHALS1REP':   {name: 'Echo Show 5 2.Gen', commandSupport: true, icon: 'icons/echo_show5.png'}, // SUPPORTS_LOCALE_SWITCH,SOUND_SETTINGS,SPEECH_RECOGNIZER_USS,DREAM_TRAINING,VOLUME_SETTING,TIMERS_AND_ALARMS,SLEEP,ASCENDING_ALARM_VOLUME,GOLDFISH,AUDIO_CONTROLS,LEMUR_ALPHA,MUSIC_SKILL,CHANGE_NAME,SIRIUSXM,DIALOG_INTERFACE_VERSION,REMINDERS,APPLE_MUSIC,ADAPTIVE_LISTENING,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,EQUALIZER_CONTROLLER_TREBLE,SUPPORTS_SOFTWARE_VERSION,ALEXA_PRESENCE,EFDCARDS,KINDLE_BOOKS,DEEZER,MICROPHONE,EQUALIZER_CONTROLLER_MIDRANGE,SUPPORTS_LOCALE,CUSTOM_ALARM_TONE,SALMON,VOICE_TRAINING,TIDAL,AMAZON_MUSIC,AUDIBLE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PAIR_BT_SINK,AUDIO_PLAYER,EARCONS,PERSISTENT_CONNECTION,SUPPORT_CALENDAR_ALERT,GUARD_EARCON,PANDORA,EQUALIZER_CONTROLLER_BASS,LIVE_VIEW,TUNE_IN,I_HEART_RADIO,SHARKNADO,SET_LOCALE,BT_PAIRING_FLOW_V2,DS_VOLUME_SETTING,FLASH_BRIEFING,MULTI_WAKEWORDS_SUPPORTED,DEREGISTER_DEVICE,FAR_FIELD_WAKE_WORD,ALEXA_VOICE\n    'A1VGB7MHSIEYFK':   {name: 'Fire TV Cube', commandSupport: true, icon: 'icons/echo_cube.png'}, // TIMERS_ALARMS_NOTIFICATIONS_VOLUME,SOUND_SETTINGS,SUPPORTS_LOCALE,BT_PAIRING_FLOW_V2,ADAPTIVE_LISTENING,I_HEART_RADIO,SUPPORTS_LOCALE_SWITCH,KINDLE_BOOKS,CUSTOM_ALARM_TONE,SALMON,MULTI_WAKEWORDS_SUPPORTED,SET_LOCALE,CHANGE_NAME,MICROPHONE,EARCONS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AMAZON_MUSIC,PERSISTENT_CONNECTION,VOLUME_SETTING,ACTIVE_AFTER_FRO,VOICE_TRAINING,PANDORA,SUPPORT_CALENDAR_ALERT,DEEZER,MUSIC_SKILL,GOLDFISH,TUNE_IN,SHARKNADO,SPEECH_RECOGNIZER_USS,ARTHUR_TARGET,DIALOG_INTERFACE_VERSION,APPLE_MUSIC,SLEEP,TIDAL,PAIR_BT_SINK,SIRIUSXM,SUPPORTS_SOFTWARE_VERSION,TIMERS_AND_ALARMS,REMINDERS,DREAM_TRAINING,AUDIO_PLAYER,FAR_FIELD_WAKE_WORD,FLASH_BRIEFING,AUDIBLE\n    'A1Z88NGR2BK6A2':   {name: 'Echo Show 8', commandSupport: true, icon: 'icons/echo_show.png'},\n    'A1ZB65LA390I4K':   {name: 'Fire HD 10', commandSupport: true, icon: 'icons/firetab.png'}, // VOLUME_SETTING,CUSTOM_ALARM_TONE,REMINDERS,SOUND_SETTINGS,SUPPORTS_LOCALE,ASX_TIME_ZONE,MICROPHONE,PERSISTENT_CONNECTION,SUPPORTS_SOFTWARE_VERSION,DIALOG_INTERFACE_VERSION,TIMERS_AND_ALARMS,ASCENDING_ALARM_VOLUME,SUPPORTS_LOCALE_SWITCH,NO_TIME_ZONE_SETTING\n    'A21Z3CGI8UIP0F':   {name: 'Apps', commandSupport: false, icon: 'icons/apps.png'}, // AUDIO_PLAYER,AMAZON_MUSIC,PANDORA,CHANGE_NAME,REMINDERS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,I_HEART_RADIO,VOLUME_SETTING,TUNE_IN,LAMBDA_DOWNCHANNEL,PEONY\n    'A222D4HGE48EOR':   {name: 'Alexa App Apple Watch', commandSupport: false}, // ASCENDING_ALARM_VOLUME,AUDIBLE,SET_LOCALE,TOUCH_INITIATED,REMINDERS,MUSIC_SKILL,VOLUME_SETTING,DEREGISTER_DEVICE,SPEECH_RECOGNIZER_USS,APPLE_MUSIC,SET_TIME_ZONE,EARCONS,DEEZER,I_HEART_RADIO,MICROPHONE,SLEEP,TIDAL,AMAZON_MUSIC,SUPPORTS_LOCALE_SWITCH,PEONY,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,TIMERS_AND_ALARMS,DIALOG_INTERFACE_VERSION,GOLDFISH,SIRIUSXM,AUDIO_PLAYER,SOUND_SETTINGS,ADAPTIVE_LISTENING,CUSTOM_ALARM_TONE,MULTI_WAKEWORDS_SUPPORTED,PERSISTENT_CONNECTION,SUPPORTS_LOCALE,DREAM_TRAINING,TUNE_IN,KINDLE_BOOKS\n    'A25OJWHZA1MWNB':   {name: 'Samsung TV Neo', commandSupport: true}, // AMAZON_MUSIC,ASCENDING_ALARM_VOLUME,TIMERS_AND_ALARMS,HANDS_FREE,TUNE_IN,MULTI_WAKEWORDS_SUPPORTED,I_HEART_RADIO,DEEZER,PERSISTENT_CONNECTION,KINDLE_BOOKS,DREAM_TRAINING,SUPPORTS_LOCALE_SWITCH,DIALOG_INTERFACE_VERSION,VOLUME_SETTING,ADAPTIVE_LISTENING,GOLDFISH,SOUND_SETTINGS,SLEEP,CHANGE_NAME,MUSIC_SKILL,DEREGISTER_DEVICE,SPEECH_RECOGNIZER_USS,TIDAL,REMINDERS,SUPPORTS_LOCALE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AUDIBLE,FAR_FIELD,SET_LOCALE,EARCONS,SET_TIME_ZONE,CUSTOM_ALARM_TONE,APPLE_MUSIC,AUDIO_PLAYER,TOUCH_INITIATED,MICROPHONE,SIRIUSXM\n    'A265XOI9586NML':   {name: 'FireTV Strick v3', commandSupport: true, icon: 'icons/firetv.png'}, // VOLUME_SETTING,CHANGE_NAME,ARTHUR_TARGET,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,REMINDERS,SPEECH_RECOGNIZER_USS,TIMERS_AND_ALARMS,CUSTOM_ALARM_TONE,SUPPORTS_LOCALE_SWITCH,EARCONS,PAIR_BT_SINK,ASCENDING_ALARM_VOLUME,ACTIVE_AFTER_FRO,SUPPORTS_SOFTWARE_VERSION,FLASH_BRIEFING,PERSISTENT_CONNECTION,DIALOG_INTERFACE_VERSION,MULTI_WAKEWORDS_SUPPORTED,SOUND_SETTINGS,SUPPORTS_LOCALE,MICROPHONE\n    'A27VEYGQBW3YR5':   {name: 'Echo Link', commandSupport: true}, // I_HEART_RADIO,DEEZER,AUDIO_PLAYER,DS_VOLUME_SETTING,LOCAL_CONTENT_REDISTRIBUTION,MICROPHONE,MUSIC_SKILL,SLEEP,BT_PAIRING_FLOW_V2,KINDLE_B\n    'A2825NDLA7WDZV':   {name: 'Apps', commandSupport: false, icon: 'icons/apps.png'}, // PEONY,VOLUME_SETTING\n    'A2DS1Q2TPDJ48U':   {name: 'Echo Dot 5.Gen Clock', commandSupport: true, icon: 'icons/echo_dot4.png'}, // EQUALIZER_CONTROLLER_BASS,ALEXA_VOICE,SUPPORTS_LOCALE,DEREGISTER_DEVICE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,ASCENDING_ALARM_VOLUME,EFDCARDS,ALEXA_GESTURES,DIALOG_INTERFACE_VERSION,SUPPORTS_SOFTWARE_VERSION,REQUIRES_OOBE_FOR_SETUP,PAIR_BT_SOURCE,PERSISTENT_CONNECTION,TUPLE_CATEGORY_A,TUPLE,SALMON,PANDORA,DISPLAY_BRIGHTNESS_ADJUST,SET_TIME_ZONE,ACTIVE_AFTER_FRO,DREAM_TRAINING,TEMPERATURE_SENSOR,ADAPTIVE_LISTENING,TAP_GESTURES,GOLDFISH,FAR_FIELD_WAKE_WORD,MOTION_SENSOR_RANGE_STRING,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,SUPPORT_CALENDAR_ALERT,SUPPORTS_LOCALE_SWITCH,CUSTOM_ALARM_TONE,VOLUME_SETTING,BT_PAIRING_FLOW_V2,TUNE_IN,AMAZON_MUSIC,EQUALIZER_CONTROLLER_MIDRANGE,KINDLE_BOOKS,CLOCK_FORMAT_24_HR,PAIR_BT_SINK,MICROPHONE,GUARD_EARCON,ALEXA_PRESENCE,AUDIO_CONTROLS,SPEECH_RECOGNIZER_USS,LEMUR_ALPHA,I_HEART_RADIO,UPDATE_WIFI,LOCALIZATION,DEEZER,WAKE_WORD_SENSITIVITY,EQUALIZER_CONTROLLER_TREBLE,SET_LOCALE,AUDIO_PLAYER,TAHOE_BYOD,SLEEP,TIDAL,DISPLAY_POWER_TOGGLE,EARCONS,FLASH_BRIEFING,SOUND_SETTINGS,TAP_GESTURES_SINGLE_TAP,TIMERS_AND_ALARMS,DS_VOLUME_SETTING,MUSIC_SKILL,POPTART,DISPLAY_ADAPTIVE_BRIGHTNESS,REMINDERS,GADGETS,SIRIUSXM,AUDIBLE,CHANGE_NAME,MOTION_DETECTION,VOICE_TRAINING,APPLE_MUSIC,TAP_GESTURES_RESUME_MEDIA,MULTI_WAKEWORDS_SUPPORTED\n    'A2E0SNTXJVT7WK':   {name: 'Fire TV V1', commandSupport: false, icon: 'icons/firetv.png'},\n    'A2EZ3TS0L1S2KV':   {name: 'Sonos Beam Gen 2', commandSupport: true, icon: 'icons/sonos.png'}, // AUDIO_PLAYER,DEREGISTER_DEVICE,APPLE_MUSIC,SUPPORTS_LOCALE,DIALOG_INTERFACE_VERSION,TIDAL,SIRIUSXM,DREAM_TRAINING,ADAPTIVE_LISTENING,DEEZER,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AUDIO_CONTROLS,TIMERS_AND_ALARMS,AMAZON_MUSIC,EQUALIZER_CONTROLLER_TREBLE,SET_LOCALE,VOLUME_SETTING,KINDLE_BOOKS,TUNE_IN,SET_TIME_ZONE,AUDIBLE,EQUALIZER_CONTROLLER_BASS,MULTI_WAKEWORDS_SUPPORTED,REMINDERS,CUSTOM_ALARM_TONE,SUPPORTS_LOCALE_SWITCH,I_HEART_RADIO,EARCONS,MUSIC_SKILL,CHANGE_NAME,GOLDFISH,PERSISTENT_CONNECTION,EQUALIZER_CONTROLLER_MIDRANGE,MICROPHONE,ASCENDING_ALARM_VOLUME,SOUND_SETTINGS,SLEEP,SPEECH_RECOGNIZER_USS\n    'A2GFL5ZMWNE0PX':   {name: 'Fire TV', commandSupport: true, icon: 'icons/firetv.png'}, // SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,VOLUME_SETTING,SUPPORTS_SOFTWARE_VERSION,CHANGE_NAME,ACTIVE_AFTER_FRO,ARTHUR_TARGET,FLASH_BRIEFING\n    'A2H4LV5GIZ1JFT':   {name: 'Echo 4 Clock', commandSupport: true, icon: 'icons/echo4.png'}, // ACTIVE_AFTER_FRO,SLEEP,TAHOE_BYOD,DISPLAY_ADAPTIVE_BRIGHTNESS,EQUALIZER_CONTROLLER_MIDRANGE,SALMON,VOLUME_SETTING,GOLDFISH,MULTI_WAKEWORDS_SUPPORTED,ALEXA_PRESENCE,PERSISTENT_CONNECTION,KINDLE_BOOKS,SET_LOCALE,APPLE_MUSIC,AUDIO_PLAYER,AMAZON_MUSIC,DS_VOLUME_SETTING,SUPPORTS_LOCALE,MOTION_DETECTION,EQUALIZER_CONTROLLER_BASS,SIRIUSXM,MICROPHONE,MUSIC_SKILL,DEEZER,AUDIO_CONTROLS,POPTART,CUSTOM_ALARM_TONE,SUPPORTS_LOCALE_SWITCH,PAIR_BT_SINK,DREAM_TRAINING,EARCONS,DIALOG_INTERFACE_VERSION,BT_PAIRING_FLOW_V2,DEREGISTER_DEVICE,EFDCARDS,TIMERS_AND_ALARMS,REQUIRES_OOBE_FOR_SETUP,EQUALIZER_CONTROLLER_TREBLE,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,SUPPORT_CALENDAR_ALERT,CLOCK_FORMAT_24_HR,TUNE_IN,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TUPLE,ALEXA_GESTURES,DISPLAY_BRIGHTNESS_ADJUST,UPDATE_WIFI,TUPLE_CATEGORY_A,SET_TIME_ZONE,ASCENDING_ALARM_VOLUME,PAIR_BT_SOURCE,GADGETS,CHANGE_NAME,VOICE_TRAINING,REMINDERS,SOUND_SETTINGS,I_HEART_RADIO,DISPLAY_POWER_TOGGLE,AUDIBLE,FLASH_BRIEFING,GUARD_EARCON,SUPPORTS_SOFTWARE_VERSION,FAR_FIELD_WAKE_WORD,PANDORA,TIDAL,LEMUR_ALPHA\n    'A2IS7199CJBT71':    {name: 'LG TV', commandSupport: true}, // ASCENDING_ALARM_VOLUME,AUDIBLE,SET_LOCALE,TOUCH_INITIATED,REMINDERS,MUSIC_SKILL,VOLUME_SETTING,DEREGISTER_DEVICE,SPEECH_RECOGNIZER_USS,APPLE_MUSIC,SET_TIME_ZONE,EARCONS,DEEZER,I_HEART_RADIO,MICROPHONE,SLEEP,TIDAL,AMAZON_MUSIC,SUPPORTS_LOCALE_SWITCH,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,TIMERS_AND_ALARMS,DIALOG_INTERFACE_VERSION,GOLDFISH,SIRIUSXM,AUDIO_PLAYER,SOUND_SETTINGS,ADAPTIVE_LISTENING,CUSTOM_ALARM_TONE,MULTI_WAKEWORDS_SUPPORTED,PERSISTENT_CONNECTION,SUPPORTS_LOCALE,DREAM_TRAINING,TUNE_IN,KINDLE_BOOKS\n    'A2IVLV5VM2W81':    {name: 'Apps', commandSupport: true, icon: 'icons/apps.png'}, // VOLUME_SETTING,MICROPHONE\n    'A2J0R2SD7G9LPA':   {name: 'Tablet', commandSupport: false}, // SHARKNADO,FAR_FIELD_WAKE_WORD,SUPPORTS_LOCALE,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,MUSIC_SKILL,I_HEART_RADIO,SALMON,EARCONS,TIMERS_AND_ALARMS,TUNE_IN,REMINDERS,SUPPORTS_LOCALE_SWITCH,DREAM_TRAINING,GADGETS,PERSISTENT_CONNECTION,SOUND_SETTINGS,VOICE_TRAINING,PANDORA,AUDIO_PLAYER,AUDIBLE,ASCENDING_ALARM_VOLUME,VOLUME_SETTING,CUSTOM_ALARM_TONE,GOLDFISH,AMAZON_MUSIC,FLASH_BRIEFING,SET_LOCALE,SLEEP,KINDLE_BOOKS,CHANGE_NAME,POPTART,MICROPHONE\n    'A2JKHJ0PX4J3L3':   {name: 'FireTV Cube', commandSupport: true, icon: 'icons/echo_cube.png'}, // SUPPORTS_SOFTWARE_VERSION,SIRIUSXM,ACTIVE_AFTER_FRO,AMAZON_MUSIC,CUSTOM_ALARM_TONE,AUDIBLE,FAR_FIELD_WAKE_WORD,DREAM_TRAINING,EARCONS,I_HEART_RADIO,TIDAL,MULTI_WAKEWORDS_SUPPORTED,TUNE_IN,APPLE_MUSIC,KINDLE_BOOKS,DIALOG_INTERFACE_VERSION,REMINDERS,AUDIO_PLAYER,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,FLASH_BRIEFING,SALMON,SUPPORT_CALENDAR_ALERT,SUPPORTS_LOCALE_SWITCH,VOICE_TRAINING,GOLDFISH,SOUND_SETTINGS,ARTHUR_TARGET,PERSISTENT_CONNECTION,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PAIR_BT_SINK,SPEECH_RECOGNIZER_USS,MICROPHONE,DEEZER,TIMERS_AND_ALARMS,SET_LOCALE,ASCENDING_ALARM_VOLUME,PANDORA,MUSIC_SKILL,CHANGE_NAME,SLEEP,SUPPORTS_LOCALE\n    'A2JMVL7QPIXTN5':   {name: 'Amazfit GTR 3 Pro', commandSupport: true}, // SET_LOCALE,SPEECH_RECOGNIZER_USS,MULTI_WAKEWORDS_SUPPORTED,KINDLE_BOOKS,MUSIC_SKILL,CUSTOM_ALARM_TONE,SET_TIME_ZONE,EARCONS,APPLE_MUSIC,DEREGISTER_DEVICE,PERSISTENT_CONNECTION,DEEZER,MICROPHONE,SIRIUSXM,AMAZON_MUSIC,REMINDERS,ADAPTIVE_LISTENING,SUPPORTS_LOCALE,SOUND_SETTINGS,SLEEP,AUDIBLE,DREAM_TRAINING,I_HEART_RADIO,CHANGE_NAME,TOUCH_INITIATED,GOLDFISH,TIDAL,AUDIO_PLAYER,VOLUME_SETTING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TUNE_IN,TIMERS_AND_ALARMS\n    'A2L8KG0CT86ADW':   {name: 'RaspPi', commandSupport: false}, // KINDLE_BOOKS,DREAM_TRAINING,PEONY,VOLUME_SETTING,CHANGE_NAME,PAIR_BT_SINK,SLEEP,I_HEART_RADIO,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PERSISTENT_CONNECTION,TUNE_IN,TIMERS_AND_ALARMS,MICROPHONE,AUDIBLE,DEREGISTER_DEVICE,GOLDFISH,AUDIO_PLAYER,REMINDERS,AMAZON_MUSIC\n    'A2LWARUGJLBYEW':   {name: 'Fire TV Stick V2', commandSupport: true, icon: 'icons/firetv.png'}, // SOUND_SETTINGS,CHANGE_NAME,ARTHUR_TARGET,PERSISTENT_CONNECTION,DIALOG_INTERFACE_VERSION,ASCENDING_ALARM_VOLUME,VOLUME_SETTING,MICROPHONE,TIMERS_AND_ALARMS,SUPPORTS_LOCALE,CUSTOM_ALARM_TONE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,ACTIVE_AFTER_FRO,FLASH_BRIEFING,REMINDERS,SUPPORTS_LOCALE_SWITCH,SUPPORTS_SOFTWARE_VERSION\n    'A2M35JJZWCQOMZ':   {name: 'Echo Plus', commandSupport: true, icon: 'icons/echo.png'}, // PAIR_BT_SINK,SOUND_SETTINGS,FAR_FIELD_WAKE_WORD,CUSTOM_ALARM_TONE,MICROPHONE,VOLUME_SETTING,ACTIVE_AFTER_FRO,POPTART,REMINDERS,AMAZON_MUSIC,TUNE_IN,CHANGE_NAME,I_HEART_RADIO,DEREGISTER_FACTORY_RESET,SUPPORTS_SOFTWARE_VERSION,EARCONS,PAIR_REMOTE,PERSISTENT_CONNECTION,LEMUR_ALPHA,SALMON,DREAM_TRAINING,UPDATE_WIFI,VOICE_TRAINING,TIMERS_AND_ALARMS,ASCENDING_ALARM_VOLUME,AUDIBLE,SLEEP,AUDIO_PLAYER,PAIR_BT_SOURCE,FLASH_BRIEFING,SET_LOCALE,REQUIRES_OOBE_FOR_SETUP,DEREGISTER_DEVICE,PANDORA,GOLDFISH,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,KINDLE_BOOKS\n    'A2M4YX06LWP8WI':   {name: 'Fire Tab', commandSupport: false, icon: 'icons/firetab.png'}, // SUPPORTS_SOFTWARE_VERSION,VOLUME_SETTING,ASX_TIME_ZONE,MICROPHONE,PEONY\n    'A2OSP3UA4VC85F':   {name: 'Sonos', commandSupport: true, icon: 'icons/sonos.png'}, // DEREGISTER_DEVICE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,KINDLE_BOOKS,AUDIO_PLAYER,TIMERS_AND_ALARMS,VOLUME_SETTING,PEONY,AMAZON_MUSIC,REMINDERS,SLEEP,I_HEART_RADIO,AUDIBLE,GOLDFISH,TUNE_IN,DREAM_TRAINING,PERSISTENT_CONNECTION\n    'A2T0P32DY3F7VB':   {name: 'echosim.io', commandSupport: false},\n    'A2TF17PFR55MTB':   {name: 'Apps', commandSupport: true, icon: 'icons/apps.png'}, // VOLUME_SETTING,MICROPHONE\n    'A2TX61L00VISA5':   {name: 'Fire HDX', commandSupport: true, icon: 'icons/firetab.png'}, // SUPPORTS_SOFTWARE_VERSION,NO_TIME_ZONE_SETTING,MICROPHONE,ADAPTIVE_LISTENING,VOLUME_SETTING,ASX_TIME_ZONE\n    'A2U21SRK4QGSE1':   {name: 'Echo Dot 4.Gen', commandSupport: true, icon: 'icons/echo_dot4.png'}, // SPEECH_RECOGNIZER_USS,MUSIC_SKILL,AMAZON_MUSIC,BT_PAIRING_FLOW_V2,SET_LOCALE,SLEEP,TIDAL,REQUIRES_OOBE_FOR_SETUP,EQUALIZER_CONTROLLER_TREBLE,TUNE_IN,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,KINDLE_BOOKS,SUPPORTS_SOFTWARE_VERSION,MICROPHONE,PERSISTENT_CONNECTION,SUPPORTS_LOCALE,FAR_FIELD_WAKE_WORD,UPDATE_WIFI,SALMON,TIMERS_AND_ALARMS,PAIR_BT_SINK,VOICE_TRAINING,EFDCARDS,TUPLE,GADGETS,ACTIVE_AFTER_FRO,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,POPTART,GOLDFISH,SUPPORTS_LOCALE_SWITCH,LEMUR_ALPHA,TUPLE_CATEGORY_A,DS_VOLUME_SETTING,APPLE_MUSIC,GUARD_EARCON,EQUALIZER_CONTROLLER_BASS,AUDIBLE,TAHOE_BYOD,FLASH_BRIEFING,SOUND_SETTINGS,I_HEART_RADIO,ASCENDING_ALARM_VOLUME,DREAM_TRAINING,ALEXA_GESTURES,CUSTOM_ALARM_TONE,ALEXA_PRESENCE,EARCONS,CHANGE_NAME,VOLUME_SETTING,DIALOG_INTERFACE_VERSION,SIRIUSXM,AUDIO_PLAYER,MOTION_DETECTION,MULTI_WAKEWORDS_SUPPORTED,SUPPORT_CALENDAR_ALERT,DEREGISTER_DEVICE,AUDIO_CONTROLS,DEEZER,PAIR_BT_SOURCE,PANDORA,EQUALIZER_CONTROLLER_MIDRANGE,REMINDERS,SET_TIME_ZONE\n    'A2XPGY5LRKB9BE':   {name: 'Fitbit Versa 2', commandSupport: false}, // ARCONS,KINDLE_BOOKS,REMINDERS,AMAZON_MUSIC,DEREGISTER_DEVICE,TIDAL,SLEEP,CHANGE_NAME,MULTI_WAKEWORDS_SUPPORTED,PEONY,SUPPORTS_LOCALE,APPLE_MUSIC,PERSISTENT_CONNECTION,I_HEART_RADIO,SOUND_SETTINGS,SET_TIME_ZONE,DIALOG_INTERFACE_VERSION,AUDIBLE,DEEZER,TUNE_IN,AUDIO_PLAYER,MICROPHONE,MUSIC_SKILL,SIRIUSXM,SUPPORTS_LOCALE_SWITCH,DREAM_TRAINING,TOUCH_INITIATED,SET_LOCALE,GOLDFISH,SPEECH_RECOGNIZER_USS,VOLUME_SETTING,ADAPTIVE_LISTENING,TIMERS_AND_ALARMS,CUSTOM_ALARM_TONE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY\n    'A2Y04QPFCANLPQ':   {name: 'Bose QC35-II', commandSupport: false}, // KINDLE_BOOKS,SLEEP,VOLUME_SETTING,SIRIUSXM,I_HEART_RADIO,TUNE_IN,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,APPLE_MUSIC,PERSISTENT_CONNECTION,AUDIBLE,MUSIC_SKILL,TOUCH_INITIATED,ADAPTIVE_LISTENING,DEREGISTER_DEVICE,DREAM_TRAINING,TIDAL,AUDIO_PLAYER,MICROPHONE,TIMERS_AND_ALARMS,GOLDFISH,DEEZER,AMAZON_MUSIC\n    'A2Z8O30CD35N8F':   {name: 'Sonos Arc', commandSupport: true}, // SET_TIME_ZONE,APPLE_MUSIC,DEEZER,SUPPORTS_LOCALE,SOUND_SETTINGS,CHANGE_NAME,SET_LOCALE,EQUALIZER_CONTROLLER_BASS,SUPPORTS_LOCALE_SWITCH,DIALOG_INTERFACE_VERSION,PERSISTENT_CONNECTION,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,MUSIC_SKILL,DREAM_TRAINING,AMAZON_MUSIC,MULTI_WAKEWORDS_SUPPORTED,VOLUME_SETTING,REMINDERS,TIMERS_AND_ALARMS,AUDIBLE,KINDLE_BOOKS,CUSTOM_ALARM_TONE,AUDIO_PLAYER,AUDIO_CONTROLS,I_HEART_RADIO,EQUALIZER_CONTROLLER_TREBLE,DEREGISTER_DEVICE,ASCENDING_ALARM_VOLUME,GOLDFISH,SPEECH_RECOGNIZER_USS,TUNE_IN,MICROPHONE,TIDAL,SLEEP,SIRIUSXM,EARCONS,EQUALIZER_CONTROLLER_MIDRANGE\n    'A303PJF6ISQ7IC':   {name: 'Echo Auto', commandSupport: false}, // ACTIVE_AFTER_FRO,TUNE_IN,LAMBDA_DOWNCHANNEL,MICROPHONE,SUPPORTS_LOCALE,CHANGE_NAME,EARCONS,FAR_FIELD_WAKE_WORD,AUDIBLE,REQUIRES_OOBE_FOR_SETUP,SUPPORTS_LOCALE_SWITCH,PANDORA,SUPPORTS_CONNECTED_HOME,FLASH_BRIEFING,KINDLE_BOOKS,SUPPORTS_SOFTWARE_VERSION,PAIR_BT_SINK,REMINDERS,I_HEART_RADIO,AUDIO_PLAYER,VOLUME_SETTING,AMAZON_MUSIC,AUDIO_CONTROLS,FACTORY_RESET_DEVICE,DEREGISTER_DEVICE\n    'A30YDR2MK8HMRV':   {name: 'Echo Dot 3.Gen Clock', commandSupport: true, icon: 'icons/echo_dot3.png'}, // DREAM_TRAINING,DISPLAY_BRIGHTNESS_ADJUST,SUPPORTS_LOCALE,TUNE_IN,TUPLE_CATEGORY_A,KINDLE_BOOKS,ALEXA_PRESENCE,GOLDFISH,I_HEART_RADIO,DEREGISTER_DEVICE,DIALOG_INTERFACE_VERSION,CHANGE_NAME,UPDATE_WIFI,AUDIO_CONTROLS,TUPLE,REMINDERS,EQUALIZER_CONTROLLER_TREBLE,GADGETS,SUPPORTS_LOCALE_SWITCH,TIMERS_AND_ALARMS,ALEXA_GESTURES,EQUALIZER_CONTROLLER_MIDRANGE,AUDIO_PLAYER,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,SUPPORTS_SOFTWARE_VERSION,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,POPTART,SOUND_SETTINGS,ACTIVE_AFTER_FRO,PANDORA,DS_VOLUME_SETTING,PERSISTENT_CONNECTION,SLEEP,GUARD_EARCON,SUPPORT_CALENDAR_ALERT,EQUALIZER_CONTROLLER_BASS,AUDIBLE,EARCONS,FLASH_BRIEFING,PAIR_REMOTE,CUSTOM_ALARM_TONE,CLOCK_FORMAT_24_HR,DISPLAY_POWER_TOGGLE,REQUIRES_OOBE_FOR_SETUP,MICROPHONE,MUSIC_SKILL,FAR_FIELD_WAKE_WORD,VOICE_TRAINING,TAHOE_BYOD,PAIR_BT_SOURCE,PAIR_BT_SINK,AMAZON_MUSIC,SALMON,ASCENDING_ALARM_VOLUME,BT_PAIRING_FLOW_V2,LEMUR_ALPHA,SET_LOCALE,DISPLAY_ADAPTIVE_BRIGHTNESS,VOLUME_SETTING\n    'A31DTMEEVDDOIV':   {name: 'Fire TV Stick Lite 2020', commandSupport: true, icon: 'icons/firetv.png'}, // SUPPORTS_LOCALE,MICROPHONE,SUPPORTS_LOCALE_SWITCH,FLASH_BRIEFING,SUPPORTS_SOFTWARE_VERSION,PAIR_BT_SINK,REMINDERS,PERSISTENT_CONNECTION,TIMERS_AND_ALARMS,VOLUME_SETTING,ASCENDING_ALARM_VOLUME,CHANGE_NAME,SOUND_SETTINGS,DIALOG_INTERFACE_VERSION,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,ARTHUR_TARGET,ACTIVE_AFTER_FRO,CUSTOM_ALARM_TONE,MULTI_WAKEWORDS_SUPPORTED,EARCONS,SPEECH_RECOGNIZER_USS\n    'A32933M5JHLHN3':   {name: 'Philips TAB8805/10 3.1 Soundbar (Connected Play-Fi-5)', commandSupport: true}, // AMAZON_MUSIC,TIDAL,FAR_FIELD,ADAPTIVE_LISTENING,LAMBDA_DOWNCHANNEL,SIRIUSXM,PANDORA,HANDS_FREE,I_HEART_RADIO,APPLE_MUSIC,REMINDERS,VOLUME_SETTING,TUNE_IN,MUSIC_SKILL,AUDIO_PLAYER,DEEZER,CHANGE_NAME,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY\n    'A32DOYMUN6DTXA':   {name: 'Echo Dot 3.Gen', commandSupport: true, icon: 'icons/echo_dot3.png'}, // PAIR_BT_SINK,CUSTOM_ALARM_TONE,PAIR_REMOTE,TIMERS_AND_ALARMS,SUPPORTS_CONNECTED_HOME,TUNE_IN,SOUND_SETTINGS,DEREGISTER_DEVICE,SET_LOCALE,SLEEP,EARCONS,UPDATE_WIFI,PAIR_BT_SOURCE,SUPPORTS_SOFTWARE_VERSION,REQUIRES_OOBE_FOR_SETUP,MICROPHONE,SALMON,TAHOE_BYOD,CHANGE_NAME,FAR_FIELD_WAKE_WORD,VOLUME_SETTING,AUDIO_PLAYER,I_HEART_RADIO,REMINDERS,PERSISTENT_CONNECTION,AUDIBLE,GADGETS,SUPPORTS_CONNECTED_HOME_ALL,AMAZON_MUSIC,VOICE_TRAINING,FLASH_BRIEFING,DEREGISTER_FACTORY_RESET,GOLDFISH,PANDORA,ACTIVE_AFTER_FRO,DREAM_TRAINING,LEMUR_ALPHA,POPTART,KINDLE_BOOKS\n    'A378ND93PD0NC4':   {name: 'VR Radio', commandSupport: true}, // SLEEP,CHANGE_NAME,TUNE_IN,MICROPHONE,VOLUME_SETTING,DEREGISTER_DEVICE,GOLDFISH,AMAZON_MUSIC,KINDLE_BOOKS,REMINDERS,AUDIBLE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TIMERS_AND_ALARMS,PAIR_BT_SINK,PEONY,PERSISTENT_CONNECTION,AUDIO_PLAYER,I_HEART_RADIO,DREAM_TRAINING,MUSIC_SKILL\n    'A3D5JL73E6MKZ1':   {name: 'Fire TV 4-Serie Smart-TV', commandSupport: true, icon: 'icons/firetv.png'}, // TIMEZONE,TIMERS_AND_ALARMS,KINDLE_BOOKS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,ARTHUR_TARGET,SUPPORTS_LOCALE_SWITCH,TAHOE_BYOD,APPLE_MUSIC,CHANGE_NAME,TUNE_IN,DEEZER,MULTI_WAKEWORDS_SUPPORTED,MUSIC_SKILL,SIRIUSXM,SUPPORTS_LOCALE,AMAZON_MUSIC,I_HEART_RADIO,DIALOG_INTERFACE_VERSION,REQUIRES_OOBE_FOR_SETUP,VOLUME_SETTING,SET_LOCALE,SALMON,SOUND_SETTINGS,ASCENDING_ALARM_VOLUME,SUPPORTS_CONNECTED_HOME_ALL,ADAPTIVE_LISTENING,DREAM_TRAINING,SLEEP,AUDIO_CONTROLS,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,CUSTOM_ALARM_TONE,PANDORA,SHARKNADO,EQUALIZER_CONTROLLER_MIDRANGE,AUDIO_PLAYER,REMINDERS,SUPPORTS_SOFTWARE_VERSION,FLASH_BRIEFING,PERSISTENT_CONNECTION,VOICE_TRAINING,SPEECH_RECOGNIZER_USS,EARCONS,TIDAL,ACTIVE_AFTER_FRO,AUDIBLE,MICROPHONE\n    'A37SHHQ3NUL7B5':   {name: 'Bose Homespeaker', commandSupport: false}, // MICROPHONE,AMAZON_MUSIC,AUDIO_PLAYER,SLEEP,PERSISTENT_CONNECTION,I_HEART_RADIO,AUDIBLE,TIMERS_AND_ALARMS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,DREAM_TRAINING,TUNE_IN,VOLUME_SETTING,GOLDFISH,KINDLE_BOOKS,DEREGISTER_DEVICE,CHANGE_NAME\n    'A38BPK7OW001EX':   {name: 'Raspberry Alexa', commandSupport: false, icon: 'icons/raspi.png'}, // TIMERS_AND_ALARMS,AMAZON_MUSIC,VOLUME_SETTING,AUDIBLE,I_HEART_RADIO,TUNE_IN,KINDLE_BOOKS,DEREGISTER_DEVICE,AUDIO_PLAYER,SLEEP,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PERSISTENT_CONNECTION,DREAM_TRAINING,MICROPHONE,GOLDFISH,CHANGE_NAME,PEONY\n    'A38EHHIB10L47V':   {name: 'Echo Dot', commandSupport: true, icon: 'icons/echo_dot.png'}, // ASCENDING_ALARM_VOLUME,MICROPHONE,REMINDERS,ASX_TIME_ZONE,VOLUME_SETTING,PEONY,SUPPORTS_SOFTWARE_VERSION)\n    'A39OV95SPFQ9YG':   {name: 'Sonos Era 100', commandSupport: true, icon: 'icons/sonos.png'}, // KINDLE_BOOKS,SUPPORTS_LOCALE_SWITCH,DEREGISTER_DEVICE,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,DREAM_TRAINING,FAR_FIELD,PERSISTENT_CONNECTION,DIALOG_INTERFACE_VERSION,SPEECH_RECOGNIZER_USS,HANDS_FREE,VOLUME_SETTING,SOUND_SETTINGS,SIRIUSXM,SET_TIME_ZONE,TUNE_IN,ASCENDING_ALARM_VOLUME,CUSTOM_ALARM_TONE,SUPPORTS_LOCALE,TIMERS_AND_ALARMS,AMAZON_MUSIC,GOLDFISH,MUSIC_SKILL,EARCONS,DEEZER,REMINDERS,SET_LOCALE,ADAPTIVE_LISTENING,AUDIBLE,APPLE_MUSIC,TIDAL,MICROPHONE,SLEEP,AUDIO_PLAYER,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,I_HEART_RADIO,MULTI_WAKEWORDS_SUPPORTED,CHANGE_NAME\n    'A39Y3UG1XLEJLZ':   {name: 'Fitbit Sense', commandSupport: false}, // DIALOG_INTERFACE_VERSION,SLEEP,DREAM_TRAINING,APPLE_MUSIC,AUDIBLE,SUPPORTS_LOCALE,AMAZON_MUSIC,GOLDFISH,SET_TIME_ZONE,SUPPORTS_LOCALE_SWITCH,TUNE_IN,SPEECH_RECOGNIZER_USS,REMINDERS,TIDAL,KINDLE_BOOKS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,I_HEART_RADIO,MUSIC_SKILL,SET_LOCALE,CHANGE_NAME,VOLUME_SETTING,DEREGISTER_DEVICE,PERSISTENT_CONNECTION,EARCONS,DEEZER,TIMERS_AND_ALARMS,PEONY,CUSTOM_ALARM_TONE,SIRIUSXM,MULTI_WAKEWORDS_SUPPORTED,AUDIO_PLAYER,MICROPHONE,SOUND_SETTING\n    'A3BW5ZVFHRCQPO':   {name: 'BMW Alexa (Amazon Alexa Car Integration)', commandSupport: true}, // TIDAL,GOLDFISH,PEONY,DEEZER,AMAZON_MUSIC,MUSIC_SKILL,SLEEP,AUDIBLE,DEREGISTER_DEVICE,HANDS_FREE,MICROPHONE,PERSISTENT_CONNECTION,APPLE_MUSIC,DREAM_TRAINING,ADAPTIVE_LISTENING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,VOLUME_SETTING,KINDLE_BOOKS,TIMERS_AND_ALARMS,SIRIUSXM,I_HEART_RADIO,TUNE_IN,REMINDERS,AUDIO_PLAYER\n    'A3C9PE6TNYLTCH':   {name: 'Multiroom', commandSupport: true, icon: 'icons/multiroom.png'}, // AUDIO_PLAYER,AMAZON_MUSIC,KINDLE_BOOKS,TUNE_IN,AUDIBLE,PANDORA,I_HEART_RADIO,SALMON,VOLUME_SETTING\n    'A3EVMLQTU6WL1W':   {name: 'Echo Show 8 2.Gen', commandSupport: true, icon: 'icons/echo_show.png'}, //ACTIVE_AFTER_FRO,PERSISTENT_CONNECTION,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,VOLUME_SETTING,SUPPORTS_SOFTWARE_VERSION,CHANGE_NAME,SHARKNADO,ARTHUR_TARGET,FLASH_BRIEFING,MICROPHONE,ADAPTIVE_LISTENING\n    'A3FX4UWTP28V1P':   {name: 'Echo 3', commandSupport: true, icon: 'icons/echo_plus2.png'}, // SET_LOCALE,TIMERS_AND_ALARMS,EARCONS,VOLUME_SETTING,AUX_SETTINGS,GOLDFISH,CHANGE_NAME,TUNE_IN,UPDATE_WIFI,KINDLE_BOOKS,DEREGISTER_DEVICE,PAIR_BT_SINK,PANDORA,SLEEP,AUDIO_CONTROLS,EQUALIZER_CONTROLLER_MIDRANGE,MUSIC_SKILL,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,TUPLE,SOUND_SETTINGS,PAIR_BT_SOURCE,AMAZON_MUSIC,EQUALIZER_CONTROLLER_TREBLE,MICROPHONE,FAR_FIELD_WAKE_WORD,BT_PAIRING_FLOW_V2,REQUIRES_OOBE_FOR_SETUP,ASCENDING_ALARM_VOLUME,PAIR_REMOTE,SUPPORT_CALENDAR_ALERT,REMINDERS,GADGETS,DREAM_TRAINING,GUARD_EARCON,ACTIVE_AFTER_FRO,SALMON,SUPPORTS_LOCALE_SWITCH,DS_VOLUME_SETTING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CUSTOM_ALARM_TONE,TUPLE_CATEGORY_A,POPTART,SUPPORTS_LOCALE,AUDIO_PLAYER,VOICE_TRAINING,ALEXA_PRESENCE,AUDIBLE,TAHOE_BYOD,EQUALIZER_CONTROLLER_BASS,DIALOG_INTERFACE_VERSION,SUPPORTS_SOFTWARE_VERSION,FLASH_BRIEFING,I_HEART_RADIO,LEMUR_ALPHA,PERSISTENT_CONNECTION\n    'A3GZUE7F9MEB4U':   {name: 'Fire TV Cube', commandSupport: true, icon: 'icons/firetv.png'}, // (AMAZON_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,VOLUME_SETTING,I_HEART_RADIO,AUDIBLE,SLEEP,DREAM_TRAINING,KINDLE_BOOKS,TIMERS_AND_ALARMS,MUSIC_SKILL,MICROPHONE,DEREGISTER_DEVICE,TUNE_IN,GOLDFISH,PERSISTENT_CONNECTION,AUDIO_PLAYER)\n    'A3H674413M2EKB':   {name: 'echosim.io', commandSupport: false},\n    'A3HF4YRA2L7XGC':   {name: 'Fire TV Cube', commandSupport: true}, // FLASH_BRIEFING,TUNE_IN,PANDORA,FAR_FIELD_WAKE_WORD,DREAM_TRAINING,AMAZON_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AUDIBLE,SUPPORTS_SOFTWARE_VERSION,PAIR_BT_SINK,CHANGE_NAME,AUDIO_PLAYER,VOICE_TRAINING,SET_LOCALE,EARCONS,SOUND_SETTINGS,SALMON,ACTIVE_AFTER_FRO,SLEEP,I_HEART_RADIO,TIMERS_AND_ALARMS,CUSTOM_ALARM_TONE,PERSISTENT_CONNECTION,ARTHUR_TARGET,KINDLE_BOOKS,REMINDERS\n    'A3L2K717GERE73':   {name: 'Alexa App Apple Watch', commandSupport: false}, // ASCENDING_ALARM_VOLUME,AUDIBLE,SET_LOCALE,TOUCH_INITIATED,REMINDERS,MUSIC_SKILL,VOLUME_SETTING,DEREGISTER_DEVICE,SPEECH_RECOGNIZER_USS,APPLE_MUSIC,SET_TIME_ZONE,EARCONS,DEEZER,I_HEART_RADIO,MICROPHONE,SLEEP,TIDAL,AMAZON_MUSIC,SUPPORTS_LOCALE_SWITCH,PEONY,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,TIMERS_AND_ALARMS,DIALOG_INTERFACE_VERSION,GOLDFISH,SIRIUSXM,AUDIO_PLAYER,SOUND_SETTINGS,ADAPTIVE_LISTENING,CUSTOM_ALARM_TONE,MULTI_WAKEWORDS_SUPPORTED,PERSISTENT_CONNECTION,SUPPORTS_LOCALE,DREAM_TRAINING,TUNE_IN,KINDLE_BOOKS\n    'A3NPD82ABCPIDP':   {name: 'Sonos Beam', commandSupport: true, icon: 'icons/sonos.png'}, // AMAZON_MUSIC,CHANGE_NAME,AUDIO_PLAYER,KINDLE_BOOKS,SLEEP,DREAM_TRAINING,AUDIBLE,DEREGISTER_DEVICE,I_HEART_RADIO,GOLDFISH,PERSISTENT_CONNECTION,MICROPHONE,TIMERS_AND_ALARMS,PEONY,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,REMINDERS,VOLUME_SETTING,TUNE_IN\n    'A3OCOCNTAPDC9O':   {name: 'Mi Smart Band 6 NFC', commandSupport: false}, // DREAM_TRAINING,SIRIUSXM,MULTI_WAKEWORDS_SUPPORTED,I_HEART_RADIO,KINDLE_BOOKS,SET_TIME_ZONE,AMAZON_MUSIC,SPEECH_RECOGNIZER_USS,HANDS_FREE,VOLUME_SETTING,SOUND_SETTINGS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,MUSIC_SKILL,DEEZER,AUDIBLE,DIALOG_INTERFACE_VERSION,APPLE_MUSIC,ASCENDING_ALARM_VOLUME,SLEEP,SET_LOCALE,SUPPORTS_LOCALE_SWITCH,PERSISTENT_CONNECTION,CUSTOM_ALARM_TONE,TIMERS_AND_ALARMS,CHANGE_NAME,MICROPHONE,ADAPTIVE_LISTENING,EARCONS,AUDIO_PLAYER,TIDAL,REMINDERS,GOLDFISH,TUNE_IN,DEREGISTER_DEVICE,SUPPORTS_LOCALE\n    'A3R8XIAIU4HJAX':   {name: 'Echo Show', commandSupport: true, icon: 'icons/echo_show.png'}, // AMAZON_MUSIC,MUSIC_SKILL,CHANGE_NAME,I_HEART_RADIO,TUNE_IN,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PEONY,REMINDERS,AUDIO_PLAYER,PANDORA,VOLUME_SETTING,LAMBDA_DOWNCHANNEL\n    'A3R9S4ZZECZ6YL':   {name: 'Fire Tab HD 10', commandSupport: true, icon: 'icons/firetab.png'}, // ASX_TIME_ZONE,PEONY,VOLUME_SETTING,SUPPORTS_SOFTWARE_VERSION\n    'A3RBAYBE7VM004':   {name: 'Echo Studio', commandSupport: true, icon: 'icons/echo_studio.png'}, // TUNE_IN,AUDIBLE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,LEMUR_ALPHA,GADGETS,FLASH_BRIEFING,SHARKNADO,PAIR_BT_SINK,PERSISTENT_CONNECTION,CHANGE_NAME,CUSTOM_ALARM_TONE,TUPLE_CATEGORY_B,EARCONS,I_HEART_RADIO,REMINDERS,SET_LOCALE,DREAM_TRAINING,POPTART,AMAZON_MUSIC,KINDLE_BOOKS,SALMON,FACTORY_RESET_DEVICE,SLEEP,GOLDFISH,ASCENDING_ALARM_VOLUME,SOUND_SETTINGS,ALLOW_LOG_UPLOAD,FAR_FIELD_WAKE_WORD,AUDIO_PLAYER,VOLUME_SETTING,VOICE_TRAINING,TUPLE,TIMERS_AND_ALARMS,SUPPORTS_SOFTWARE_VERSION,PANDORA,MICROPHONE\n    'A3RMGO6LYLH7YN':   {name: 'Echo Plus 4.Gen', commandSupport: true, icon: 'icons/echo4.png'}, // PANDORA,PAIR_BT_SINK,FAR_FIELD_WAKE_WORD,GADGETS,DS_VOLUME_SETTING,TUNE_IN,EQUALIZER_CONTROLLER_BASS,DIALOG_INTERFACE_VERSION,MOTION_DETECTION,SALMON,TIMERS_AND_ALARMS,TIDAL,AUDIO_PROCESSING,TUPLE,EARCONS,POPTART,EQUALIZER_CONTROLLER_MIDRANGE,ALEXA_GESTURES,SUPPORTS_LOCALE,UPDATE_WIFI,TAHOE_BYOD,MUSIC_SKILL,ASCENDING_ALARM_VOLUME,ALEXA_PRESENCE,BT_PAIRING_FLOW_V2,EQUALIZER_CONTROLLER_TREBLE,ACTIVE_AFTER_FRO,DREAM_TRAINING,DEEZER,DEREGISTER_DEVICE,AUDIBLE,REQUIRES_OOBE_FOR_SETUP,VOICE_TRAINING,AUDIO_CONTROLS,SUPPORTS_SOFTWARE_VERSION,SOUND_SETTINGS,CUSTOM_ALARM_TONE,FLASH_BRIEFING,I_HEART_RADIO,SUPPORT_CALENDAR_ALERT,SET_LOCALE,LEMUR_ALPHA,APPLE_MUSIC,MICROPHONE,REMINDERS,GOLDFISH,SUPPORTS_LOCALE_SWITCH,MULTI_WAKEWORDS_SUPPORTED,SLEEP,KINDLE_BOOKS,TEMPERATURE_SENSOR,SIRIUSXM,GUARD_EARCON,AUDIO_PLAYER,PERSISTENT_CONNECTION,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,EFDCARDS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TUPLE_CATEGORY_A,CHANGE_NAME,VOLUME_SETTING,PAIR_BT_SOURCE,AUX_SETTINGS,SET_TIME_ZONE,AMAZON_MUSIC\n    'A3S5BH2HU6VAYF':   {name: 'Echo Dot 2.Gen', commandSupport: true, icon: 'icons/echo_dot.png'}, // PAIR_BT_SINK,CUSTOM_ALARM_TONE,PAIR_REMOTE,TIMERS_AND_ALARMS,SUPPORTS_CONNECTED_HOME,TUNE_IN,SOUND_SETTINGS,DEREGISTER_DEVICE,SET_LOCALE,SLEEP,EARCONS,UPDATE_WIFI,PAIR_BT_SOURCE,SUPPORTS_SOFTWARE_VERSION,REQUIRES_OOBE_FOR_SETUP,MICROPHONE,SALMON,TAHOE_BYOD,CHANGE_NAME,FAR_FIELD_WAKE_WORD,VOLUME_SETTING,AUDIO_PLAYER,I_HEART_RADIO,REMINDERS,PERSISTENT_CONNECTION,AUDIBLE,GADGETS,SUPPORTS_CONNECTED_HOME_ALL,AMAZON_MUSIC,VOICE_TRAINING,FLASH_BRIEFING,DEREGISTER_FACTORY_RESET,GOLDFISH,PANDORA,ACTIVE_AFTER_FRO,DREAM_TRAINING,LEMUR_ALPHA,POPTART,KINDLE_BOOKS\n    'A3SSG6GR8UU7SN':   {name: 'Echo Sub', commandSupport: true, icon: 'icons/echo_sub.png'}, // PERSISTENT_CONNECTION,ACTIVE_AFTER_FRO,SET_LOCALE,MICROPHONE,VOLUME_SETTING,AUDIBLE,AUDIO_PLAYER,UPDATE_WIFI,TUPLE,CUSTOM_ALARM_TONE,REQUIRES_OOBE_FOR_SETUP,EARCONS,KINDLE_BOOKS,TUPLE_CATEGORY_C,SUPPORTS_SOFTWARE_VERSION,ALLOW_LOG_UPLOAD,DEREGISTER_DEVICE,SLEEP,SOUND_SETTINGS,CHANGE_NAME,FLASH_BRIEFING,REMINDERS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TIMERS_AND_ALARMS\n    'A3TCJ8RTT3NVI7':   {name: 'Listens for Alexa', commandSupport: false, icon: 'icons/microphone.png'}, // (AUDIBLE,DEREGISTER_DEVICE,MICROPHONE,GOLDFISH,CHANGE_NAME,PERSISTENT_CONNECTION,TUNE_IN,SLEEP,AUDIO_PLAYER,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,KINDLE_BOOKS,VOLUME_SETTING,I_HEART_RADIO,DREAM_TRAINING,MUSIC_SKILL,PEONY,AMAZON_MUSIC,TIMERS_AND_ALARMS)\n    'A3V3VA38K169FO':   {name: 'Fire Tab', commandSupport: true, icon: 'icons/firetab.png'}, // (VOLUME_SETTING,MUSIC_SKILL,AUDIO_PLAYER,AUDIBLE,KINDLE_BOOKS,SLEEP,I_HEART_RADIO,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PERSISTENT_CONNECTION,CHANGE_NAME,TIMERS_AND_ALARMS,PEONY,DREAM_TRAINING,MICROPHONE,TUNE_IN,AMAZON_MUSIC,GOLDFISH,DEREGISTER_DEVICE)\n    'A3VRME03NAXFUB':   {name: 'Echo Flex', commandSupport: true, icon: 'icons/echo_flex.png'}, // (LEMUR_ALPHA,TUNE_IN,EARCONS,ALEXA_PRESENCE,AMAZON_MUSIC,TUPLE,DREAM_TRAINING,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,FLASH_BRIEFING,DIALOG_INTERFACE_VERSION,PAIR_REMOTE,SOUND_SETTINGS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,UPDATE_WIFI,AUDIBLE,SLEEP,VOLUME_SETTING,GOLDFISH,CHANGE_NAME,SALMON,GADGETS,ACTIVE_AFTER_FRO,PANDORA,ASCENDING_ALARM_VOLUME,PAIR_BT_SINK,KINDLE_BOOKS,VOICE_TRAINING,DS_VOLUME_SETTING,SUPPORT_CALENDAR_ALERT,TAHOE_BYOD,FAR_FIELD_WAKE_WORD,I_HEART_RADIO,SUPPORTS_SOFTWARE_VERSION,PAIR_BT_SOURCE,TUPLE_CATEGORY_A,ALLOW_LOG_UPLOAD,POPTART,SET_LOCALE,TIMERS_AND_ALARMS,BT_PAIRING_FLOW_V2,REQUIRES_OOBE_FOR_SETUP,CUSTOM_ALARM_TONE,REMINDERS,MUSIC_SKILL,DEREGISTER_DEVICE,PERSISTENT_CONNECTION,MICROPHONE,DISPLAY_BRIGHTNESS_ADJUST,AUDIO_PLAYER)\n    'A4ZP7ZC4PI6TO':    {name: 'Echo Show 5', commandSupport: true, icon: 'icons/echo_show5.png'}, // FAR_FIELD_WAKE_WORD,AMAZON_MUSIC,LEMUR_ALPHA,MICROPHONE,PANDORA,I_HEART_RADIO,VOLUME_SETTING,EARCONS,PAIR_BT_SINK,DS_VOLUME_SETTING,SOUND_SETTINGS,TIMERS_AND_ALARMS,LOCAL_VOICE,DIALOG_INTERFACE_VERSION,GOLDFISH,SHARKNADO,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,CHANGE_NAME,SUPPORTS_SOFTWARE_VERSION,REMINDERS,AUDIO_PLAYER,SALMON,MUSIC_SKILL,ALLOW_LOG_UPLOAD,TUNE_IN,DREAM_TRAINING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,ASCENDING_ALARM_VOLUME,AUDIBLE,FACTORY_RESET_DEVICE,FLASH_BRIEFING,SLEEP,SET_LOCALE,PERSISTENT_CONNECTION,VOICE_TRAINING,CUSTOM_ALARM_TONE,KINDLE_BOOKS,SUPPORT_CALENDAR_ALERT\n    'A4ZXE0RM7LQ7A':   {name: 'Echo Dot 5.Gen', commandSupport: true, icon: 'icons/echo_dot4.png'}, // SET_LOCALE,GUARD_EARCON,DEREGISTER_DEVICE,REQUIRES_OOBE_FOR_SETUP,SUPPORTS_SOFTWARE_VERSION,VOLUME_SETTING,BT_PAIRING_FLOW_V2,REMINDERS,TAHOE_BYOD,SLEEP,ACTIVE_AFTER_FRO,PAIR_BT_SINK,ALEXA_PRESENCE,MUSIC_SKILL,CHANGE_NAME,SPEECH_RECOGNIZER_USS,LOCALIZATION,SET_TIME_ZONE,PAIR_BT_SOURCE,TAP_GESTURES_RESUME_MEDIA,TEMPERATURE_SENSOR,TIMERS_AND_ALARMS,SUPPORTS_LOCALE_SWITCH,MOTION_DETECTION,SUPPORT_CALENDAR_ALERT,CUSTOM_ALARM_TONE,EQUALIZER_CONTROLLER_BASS,PERSISTENT_CONNECTION,KINDLE_BOOKS,LEMUR_ALPHA,SUPPORTS_LOCALE,GADGETS,I_HEART_RADIO,DIALOG_INTERFACE_VERSION,TIDAL,EFDCARDS,WAKE_WORD_SENSITIVITY,EQUALIZER_CONTROLLER_TREBLE,SALMON,PANDORA,TAP_GESTURES_SINGLE_TAP,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,VOICE_TRAINING,EQUALIZER_CONTROLLER_MIDRANGE,AUDIO_PLAYER,TUPLE,SIRIUSXM,TUPLE_CATEGORY_A,TUNE_IN,AUDIBLE,DS_VOLUME_SETTING,ASCENDING_ALARM_VOLUME,FLASH_BRIEFING,DEEZER,POPTART,TAP_GESTURES,UPDATE_WIFI,MOTION_SENSOR_RANGE_STRING,ALEXA_VOICE,SOUND_SETTINGS,GOLDFISH,ADAPTIVE_LISTENING,DREAM_TRAINING,APPLE_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,MULTI_WAKEWORDS_SUPPORTED,EARCONS,FAR_FIELD_WAKE_WORD,AMAZON_MUSIC,MICROPHONE,AUDIO_CONTROLS\n    'A7WXQPH584YP':     {name: 'Echo 2.Gen', commandSupport: true, icon: 'icons/echo2.png'}, // PAIR_BT_SINK,CUSTOM_ALARM_TONE,PAIR_REMOTE,TIMERS_AND_ALARMS,TUNE_IN,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SOUND_SETTINGS,DEREGISTER_DEVICE,SET_LOCALE,SLEEP,EARCONS,UPDATE_WIFI,PAIR_BT_SOURCE,SUPPORTS_SOFTWARE_VERSION,REQUIRES_OOBE_FOR_SETUP,MICROPHONE,SALMON,TAHOE_BYOD,CHANGE_NAME,FAR_FIELD_WAKE_WORD,VOLUME_SETTING,AUDIO_PLAYER,I_HEART_RADIO,REMINDERS,ASCENDING_ALARM_VOLUME,PERSISTENT_CONNECTION,AUDIBLE,GADGETS,TUPLE_CATEGORY_A,AMAZON_MUSIC,VOICE_TRAINING,FLASH_BRIEFING,DEREGISTER_FACTORY_RESET,GOLDFISH,TUPLE,PANDORA,ACTIVE_AFTER_FRO,DREAM_TRAINING,LEMUR_ALPHA,POPTART,KINDLE_BOOKS\n    'A8D2OKFFQKQ56':    {name: 'Bose Smart Soundbar 900', commandSupport: true}, // ADAPTIVE_LISTENING,TIMERS_AND_ALARMS,DEREGISTER_DEVICE,DEEZER,GOLDFISH,TIDAL,SPEECH_RECOGNIZER_USS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,APPLE_MUSIC,FAR_FIELD,PERSISTENT_CONNECTION,SLEEP,SUPPORTS_LOCALE,MICROPHONE,VOLUME_SETTING,DIALOG_INTERFACE_VERSION,SIRIUSXM,SOUND_SETTINGS,I_HEART_RADIO,CHANGE_NAME,AUDIBLE,DREAM_TRAINING,SET_TIME_ZONE,EARCONS,AUDIO_PLAYER,TUNE_IN,MUSIC_SKILL,AMAZON_MUSIC,KINDLE_BOOKS,REMINDERS,MULTI_WAKEWORDS_SUPPORTED,CUSTOM_ALARM_TONE\n    'A8DM4FYR6D3HT':    {name: 'LG WebOS TV', commandSupport: true}, // CHANGE_NAME,AUDIBLE,ASCENDING_ALARM_VOLUME,REMINDERS,DIALOG_INTERFACE_VERSION,DEREGISTER_DEVICE,MUSIC_SKILL,I_HEART_RADIO,SET_LOCALE,CUSTOM_ALARM_TONE,SET_TIME_ZONE,MULTI_WAKEWORDS_SUPPORTED,TUNE_IN,MICROPHONE,KINDLE_BOOKS,DREAM_TRAINING,SOUND_SETTINGS,GOLDFISH,VOLUME_SETTING,SUPPORTS_LOCALE_SWITCH,SLEEP,TIMERS_AND_ALARMS,AUDIO_PLAYER,SUPPORTS_LOCALE,AMAZON_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PERSISTENT_CONNECTION\n    'AB72C64C86AW2':    {name: 'Echo', commandSupport: true, icon: 'icons/echo.png'}, // PAIR_BT_SINK,CUSTOM_ALARM_TONE,PAIR_REMOTE,TIMERS_AND_ALARMS,SUPPORTS_CONNECTED_HOME,TUNE_IN,SOUND_SETTINGS,DEREGISTER_DEVICE,SET_LOCALE,SLEEP,EARCONS,UPDATE_WIFI,PAIR_BT_SOURCE,SUPPORTS_SOFTWARE_VERSION,REQUIRES_OOBE_FOR_SETUP,TUPLE_CATEGORY_B,MICROPHONE,SALMON,TAHOE_BYOD,CHANGE_NAME,FAR_FIELD_WAKE_WORD,VOLUME_SETTING,AUDIO_PLAYER,I_HEART_RADIO,REMINDERS,ASCENDING_ALARM_VOLUME,PERSISTENT_CONNECTION,AUDIBLE,GADGETS,SUPPORTS_CONNECTED_HOME_ALL,AMAZON_MUSIC,VOICE_TRAINING,FLASH_BRIEFING,DEREGISTER_FACTORY_RESET,GOLDFISH,TUPLE,PANDORA,ACTIVE_AFTER_FRO,DREAM_TRAINING,LEMUR_ALPHA,POPTART,KINDLE_BOOKS\n    'ADOUDFQX2QVX0':    {name: 'FireTV Omni', commandSupport: true, icon: 'icons/firetv.png'}, // SUPPORTS_LOCALE,PERSISTENT_CONNECTION,SUPPORTS_LOCALE_SWITCH,PAIR_BT_SINK,TIMEZONE,AUDIBLE,EQUALIZER_CONTROLLER_MIDRANGE,REMINDERS,EARCONS,PANDORA,TAHOE_BYOD,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,ARTHUR_TARGET,SET_LOCALE,DEEZER,REQUIRES_OOBE_FOR_SETUP,SLEEP,AMAZON_MUSIC,SIRIUSXM,ASCENDING_ALARM_VOLUME,CUSTOM_ALARM_TONE,DREAM_TRAINING,SHARKNADO,TUNE_IN,TIMERS_AND_ALARMS,FLASH_BRIEFING,ACTIVE_AFTER_FRO,KINDLE_BOOKS,APPLE_MUSIC,MICROPHONE,SOUND_SETTINGS,SALMON,DIALOG_INTERFACE_VERSION,SUPPORTS_CONNECTED_HOME_ALL,AUDIO_PLAYER,SPEECH_RECOGNIZER_USS,VOICE_TRAINING,I_HEART_RADIO,BT_PAIRING_FLOW_V2,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CHANGE_NAME,MUSIC_SKILL,SUPPORTS_SOFTWARE_VERSION,MULTI_WAKEWORDS_SUPPORTED,ADAPTIVE_LISTENING,VOLUME_SETTING,AUDIO_CONTROLS,TIDAL\n    'ADVBD696BHNV5':    {name: 'Fire TV Stick V1', commandSupport: true, icon: 'icons/firetv.png'}, // REMINDERS,CUSTOM_ALARM_TONE,VOLUME_SETTING,SUPPORTS_LOCALE_SWITCH,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,DIALOG_INTERFACE_VERSION,SUPPORTS_SOFTWARE_VERSION,SOUND_SETTINGS,PERSISTENT_CONNECTION,MICROPHONE,SUPPORTS_LOCALE,FLASH_BRIEFING,ACTIVE_AFTER_FRO,ARTHUR_TARGET,ASCENDING_ALARM_VOLUME,CHANGE_NAME,TIMERS_AND_ALARMS\n    'AGB8PT05FOQ61':    {name: 'Sonos Move 2', commandSupport: true, icon: 'icons/sonos.png'}, // VOLUME_SETTING,AUDIO_PLAYER,SIRIUSXM,APPLE_MUSIC,PERSISTENT_CONNECTION,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,SET_TIME_ZONE,SOUND_SETTINGS,REMINDERS,SUPPORTS_LOCALE,AMAZON_MUSIC,SPEECH_RECOGNIZER_USS,DREAM_TRAINING,MICROPHONE,TIMERS_AND_ALARMS,SLEEP,GOLDFISH,TUNE_IN,MULTI_WAKEWORDS_SUPPORTED,MUSIC_SKILL,FAR_FIELD,I_HEART_RADIO,KINDLE_BOOKS,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,DEREGISTER_DEVICE,DEEZER,SUPPORTS_LOCALE_SWITCH,CHANGE_NAME,EARCONS,HANDS_FREE,ADAPTIVE_LISTENING,DIALOG_INTERFACE_VERSION,CUSTOM_ALARM_TONE,ASCENDING_ALARM_VOLUME,AUDIBLE,TIDAL,SET_LOCALE\n    'AHCEDGRIFN5RP':    {name: 'Xiaomi Smart Fire TV', commandSupport: true}, // DREAM_TRAINING,AMAZON_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,CUSTOM_ALARM_TONE,SUPPORTS_CONNECTED_HOME_ALL,ARTHUR_TARGET,SALMON,VOLUME_SETTING,I_HEART_RADIO,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,SUPPORTS_LOCALE,REMINDERS,AUDIO_PLAYER,ADAPTIVE_LISTENING,KINDLE_BOOKS,TAHOE_BYOD,EARCONS,FLASH_BRIEFING,AUDIO_CONTROLS,PANDORA,SET_LOCALE,TIMERS_AND_ALARMS,SPEECH_RECOGNIZER_USS,PERSISTENT_CONNECTION,AUDIBLE,MICROPHONE,REQUIRES_OOBE_FOR_SETUP,TIDAL,SIRIUSXM,SHARKNADO,CHANGE_NAME,EQUALIZER_CONTROLLER_MIDRANGE,SUPPORTS_LOCALE_SWITCH,DEEZER,MUSIC_SKILL,SLEEP,SUPPORTS_SOFTWARE_VERSION,APPLE_MUSIC,MULTI_WAKEWORDS_SUPPORTED,DIALOG_INTERFACE_VERSION,VOICE_TRAINING,ACTIVE_AFTER_FRO,TUNE_IN,ASCENDING_ALARM_VOLUME,SOUND_SETTINGS,TIMEZONE\n    'AHJYKVA63YCAQ':    {name: 'Sonos Roam', commandSupport: true, icon: 'icons/sonos.png'}, // SIRIUSXM,PERSISTENT_CONNECTION,TIDAL,SUPPORTS_LOCALE_SWITCH,DIALOG_INTERFACE_VERSION,DREAM_TRAINING,CUSTOM_ALARM_TONE,APPLE_MUSIC,GOLDFISH,KINDLE_BOOKS,CHANGE_NAME,SET_LOCALE,AUDIO_PLAYER,AUDIBLE,TUNE_IN,DEREGISTER_DEVICE,SPEECH_RECOGNIZER_USS,TIMERS_AND_ALARMS,MULTI_WAKEWORDS_SUPPORTED,EARCONS,MICROPHONE,DEEZER,ASCENDING_ALARM_VOLUME,MUSIC_SKILL,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SLEEP,SOUND_SETTINGS,SUPPORTS_LOCALE,I_HEART_RADIO,REMINDERS,AMAZON_MUSIC,SET_TIME_ZONE,VOLUME_SETTING\n    'AILBSA2LNTOYL':    {name: 'reverb App', commandSupport: false, icon: 'icons/reverb.png'},\n    'AINRG27IL8AS0':    {name: 'Megablast Speaker', commandSupport: false}, // (TUNE_IN,KINDLE_BOOKS,PAIR_BT_SINK,TIMERS_AND_ALARMS,MICROPHONE,AUDIBLE,CHANGE_NAME,GOLDFISH,REMINDERS,VOLUME_SETTING,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,DREAM_TRAINING,SLEEP,PERSISTENT_CONNECTION,AMAZON_MUSIC,I_HEART_RADIO,MUSIC_SKILL,PEONY,AUDIO_PLAYER,DEREGISTER_DEVICE)\n    'AIPK7MM90V7TB':    {name: 'Echo Show 10', commandSupport: true, icon: 'icons/echo_show5.png'}, // TIDAL,ALEXA_VOICE,FAR_FIELD_WAKE_WORD,POPTART,PANDORA,TUNE_IN,DEREGISTER_DEVICE,EQUALIZER_CONTROLLER_MIDRANGE,CUSTOM_ALARM_TONE,AUDIO_PROCESSING,TIMERS_AND_ALARMS,PAIR_BT_SINK,AUDIO_PLAYER,SALMON,SHARKNADO,APPLE_MUSIC,SLEEP,KINDLE_BOOKS,EARCONS,DREAM_TRAINING,EFDCARDS,CHANGE_NAME,AUDIBLE,SUPPORTS_LOCALE_SWITCH,VOLUME_SETTING,DIALOG_INTERFACE_VERSION,SUPPORT_CALENDAR_ALERT,FLASH_BRIEFING,MUSIC_SKILL,GOLDFISH,MULTI_WAKEWORDS_SUPPORTED,SMART_MOTION,SPEECH_RECOGNIZER_USS,EQUALIZER_CONTROLLER_BASS,LEMUR_ALPHA,VOICE_TRAINING,ASCENDING_ALARM_VOLUME,LIVE_VIEW,GUARD_EARCON,ALEXA_PRESENCE,SUPPORTS_SOFTWARE_VERSION,SIRIUSXM,EQUALIZER_CONTROLLER_TREBLE,AUDIO_CONTROLS,NEED_WARMUP_FOR_LIVE_VIEW,MICROPHONE,SUPPORTS_LOCALE,TUPLE_CATEGORY_B,BT_PAIRING_FLOW_V2,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,REMINDERS,TUPLE,AMAZON_MUSIC,CLOCK_FORMAT_24_HR,I_HEART_RADIO,SOUND_SETTINGS,SET_LOCALE,DS_VOLUME_SETTING,DEEZER,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,ADAPTIVE_LISTENING,PERSISTENT_CONNECTION\n    'AKOAGQTKAS9YB':    {name: 'Echo Connect', commandSupport: false}, // VOLUME_SETTING,PEONY,BLOWER,DEREGISTER_DEVICE,PERSISTENT_CONNECTION,NO_UNITS_AND_TIMEZONES,SUPPORTS_SOFTWARE_VERSION,UPDATE_WIFI,CHANGE_NAME,MICROPHONE\n    'AKPGW064GI9HE':    {name: 'Fire Stick 4K', commandSupport: true, icon: 'icons/firetv.png'}, // SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,SUPPORTS_SOFTWARE_VERSION,CHANGE_NAME,PERSISTENT_CONNECTION,ARTHUR_TARGET,ACTIVE_AFTER_FRO,FLASH_BRIEFING,MICROPHONE,VOLUME_SETTING\n    'AO50AHDYKXRFG':   {name: 'Bose Headphones', commandSupport: false}, // KINDLE_BOOKS,AUDIBLE,DEEZER,AMAZON_MUSIC,TUNE_IN,AUDIO_PLAYER,MICROPHONE,DEREGISTER_DEVICE,MUSIC_SKILL,TIDAL,SLEEP,SIRIUSXM,DREAM_TRAINING,TOUCH_INITIATED,CHANGE_NAME,HANDS_FREE,GOLDFISH,VOLUME_SETTING,ADAPTIVE_LISTENING,TIMERS_AND_ALARMS,APPLE_MUSIC,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,PERSISTENT_CONNECTION,I_HEART_RADIO\n    'AP1F6KUH00XPV':    {name: 'Stereo/Subwoofer Pair', commandSupport: true}, // AUDIO_PLAYER,TUNE_IN,SALMON,I_HEART_RADIO,PANDORA,AMAZON_MUSIC,VOLUME_SETTING,MUSIC_SKILL,MICROPHONE\n    'ASO109Y6XSVHD':    {name: 'Amazfit GTR 4 LE', commandSupport: true}, // SET_LOCALE,DEEZER,ADAPTIVE_LISTENING,SLEEP,MUSIC_SKILL,REMINDERS,MULTI_WAKEWORDS_SUPPORTED,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TIDAL,TOUCH_INITIATED,SIRIUSXM,PERSISTENT_CONNECTION,CHANGE_NAME,VOLUME_SETTING,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,TIMERS_AND_ALARMS,MICROPHONE,SOUND_SETTINGS,SUPPORTS_LOCALE_SWITCH,SET_TIME_ZONE,AUDIO_PLAYER,AUDIBLE,TUNE_IN,AMAZON_MUSIC,APPLE_MUSIC,SUPPORTS_LOCALE,DREAM_TRAINING,KINDLE_BOOKS,GOLDFISH,DEREGISTER_DEVICE,CUSTOM_ALARM_TONE,I_HEART_RADIO,EARCONS,SPEECH_RECOGNIZER_USS\n    'ASQZWP4GPYUT7': {name: 'Echo Pop', commandSupport: true}, // SET_LOCALE,BT_PAIRING_FLOW_V2,TIMERS_AND_ALARMS,PERSISTENT_CONNECTION,VOICE_TRAINING,TAHOE_BYOD,EFDCARDS,ACTIVE_AFTER_FRO,ALEXA_PRESENCE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,AMAZON_MUSIC,AUDIBLE,TUPLE_CATEGORY_A,PANDORA,GOLDFISH,REQUIRES_OOBE_FOR_SETUP,REMINDERS,LEMUR_ALPHA,EQUALIZER_CONTROLLER_MIDRANGE,DEREGISTER_DEVICE,SUPPORT_CALENDAR_ALERT,ASCENDING_ALARM_VOLUME,SET_TIME_ZONE,MUSIC_SKILL,LOCALIZATION,SLEEP,EQUALIZER_CONTROLLER_BASS,TAP_GESTURES,SUPPORTS_LOCALE,DIALOG_INTERFACE_VERSION,ALEXA_VOICE,DREAM_TRAINING,DS_VOLUME_SETTING,TIDAL,EARCONS,SUPPORTS_SOFTWARE_VERSION,SIRIUSXM,SUPPORTS_LOCALE_SWITCH,SOUND_SETTINGS,APPLE_MUSIC,MULTI_WAKEWORDS_SUPPORTED,AUDIO_CONTROLS,POPTART,AUDIO_PLAYER,DEEZER,GUARD_EARCON,CHANGE_NAME,TIMERS_ALARMS_NOTIFICATIONS_VOLUME,PAIR_BT_SINK,DISPLAY_BRIGHTNESS_ADJUST,SALMON,ADAPTIVE_LISTENING,SPEECH_RECOGNIZER_USS,KINDLE_BOOKS,FLASH_BRIEFING,FAR_FIELD_WAKE_WORD,TAP_GESTURES_SINGLE_TAP,I_HEART_RADIO,VOLUME_SETTING,TUNE_IN,TUPLE,CUSTOM_ALARM_TONE,MICROPHONE,GADGETS,PAIR_BT_SOURCE,UPDATE_WIFI,EQUALIZER_CONTROLLER_TREBLE\n    'ATNLRCEBX3W4P':    {name: 'Fire HD 10', commandSupport: true}, // SOUND_SETTINGS,ASCENDING_ALARM_VOLUME,MICROPHONE,EARCONS,SUPPORTS_SOFTWARE_VERSION,REMINDERS,ADAPTIVE_LISTENING,SPEECH_RECOGNIZER_USS,NO_TIME_ZONE_SETTING,MULTI_WAKEWORDS_SUPPORTED,SUPPORTS_LOCALE,ASX_TIME_ZONE,VOLUME_SETTING,CUSTOM_ALARM_TONE,SUPPORTS_LOCALE_SWITCH,DIALOG_INTERFACE_VERSION,PERSISTENT_CONNECTION,TIMERS_AND_ALARMS\n    'AVD3HM0HOJAAL':    {name: 'Sonos One 2.Gen', commandSupport: true, icon: 'icons/sonos.png'}, // CHANGE_NAME,DEREGISTER_DEVICE,DREAM_TRAINING,PEONY,AMAZON_MUSIC,MICROPHONE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TIMERS_AND_ALARMS,VOLUME_SETTING,AUDIBLE,KINDLE_BOOKS,SLEEP,AUDIO_PLAYER,GOLDFISH,I_HEART_RADIO,TUNE_IN,MUSIC_SKILL,PERSISTENT_CONNECTION,REMINDERS\n    'AVE5HX13UR5NO':    {name: 'Logitech Zero Touch', commandSupport: false}, // SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,TUNE_IN,MICROPHONE,AUDIO_PLAYER,TIMERS_AND_ALARMS,PEONY,GOLDFISH,DEREGISTER_DEVICE,AUDIBLE,AMAZON_MUSIC,PERSISTENT_CONNECTION,KINDLE_BOOKS,CHANGE_NAME,I_HEART_RADIO,SLEEP,DREAM_TRAINING,VOLUME_SETTING\n    'AVU7CPPF2ZRAS':    {name: 'Fire HD 8', commandSupport: true}, // VOLUME_SETTING,SUPPORTS_SOFTWARE_VERSION,REMINDERS,PERSISTENT_CONNECTION,NO_TIME_ZONE_SETTING,DIALOG_INTERFACE_VERSION,TIMERS_AND_ALARMS,SOUND_SETTINGS,CUSTOM_ALARM_TONE,ASX_TIME_ZONE,ASCENDING_ALARM_VOLUME,MICROPHONE\n    'AWZZ5CVHX2CD':     {name: 'Echo Show 2.Gen', commandSupport: true, icon: 'icons/echo_show2.png'}, // TUNE_IN,AUDIBLE,SUPPORTS_CONNECTED_HOME_CLOUD_ONLY,LEMUR_ALPHA,GADGETS,FLASH_BRIEFING,SHARKNADO,PAIR_BT_SINK,PERSISTENT_CONNECTION,CHANGE_NAME,CUSTOM_ALARM_TONE,TUPLE_CATEGORY_B,EARCONS,I_HEART_RADIO,REMINDERS,SET_LOCALE,DREAM_TRAINING,POPTART,AMAZON_MUSIC,KINDLE_BOOKS,SALMON,FACTORY_RESET_DEVICE,SLEEP,GOLDFISH,ASCENDING_ALARM_VOLUME,SOUND_SETTINGS,ALLOW_LOG_UPLOAD,FAR_FIELD_WAKE_WORD,AUDIO_PLAYER,VOLUME_SETTING,VOICE_TRAINING,TUPLE,TIMERS_AND_ALARMS,SUPPORTS_SOFTWARE_VERSION,PANDORA,MICROPHONE\n};\n\nconst unknownDeviceWarnings = {};\n\nlet proxyUrl = null;\n\nlet resetCrashCheckerTimeout = null;\nlet updateStateTimer;\nlet updateHistoryTimer;\nlet updateConfigurationTimer;\nlet updateSmartHomeDevicesTimer;\nconst updatePlayerTimer = {};\nconst updateNotificationTimer = {};\n\nlet musicProviders;\nlet routineSounds;\nlet routineSkills;\nconst rememberedNotificationVolumeRestoreCallbacks = {};\nconst initialDeviceVolumes = {};\nlet automationRoutines;\nlet routineTriggerUtterances;\nconst playerDevices = {};\nconst playingDevices = {};\nconst appDevices = {};\nconst listMap =  {};\nconst equalizerUpdateTimeouts = {};\n\nconst lastPlayerState = {};\nconst notificationTimer = {};\nlet pushConnected = false;\nconst shApplianceEntityMap = {};\nconst shEntityApplianceMap = {};\nconst shMergedApplianceIdMap = {};\nconst shGroupDetails = {};\nconst shDeviceParamValues = {};\nconst shQueryBlocker = {};\nconst shQueryEnabled = {};\nconst shDeviceParamControlValues = {};\nconst shDeviceRefreshTimeoutAfterControl = {};\nlet shDeviceDiscoveryTimeout = null;\n\nconst stateChangeTrigger = {};\nconst objectQueue = [];\n\nconst existingStates = {};\nconst adapterObjects = {};\n\nlet crashCheckFileName;\nlet isCrashStop = false;\nconst useCrashCheck = true; // Only disable for development!\nlet initDone = false;\nlet installationUuid;\n\nconst shdCounters = {\n    getSkill: 0,\n    get: 0,\n    setSkill: 0,\n    set: 0\n};\nlet checkerInterval = null;\n\nprocess.on('uncaughtException', () => {\n    isCrashStop = true;\n});\nprocess.on('unhandledRejection', () => {\n    isCrashStop = true;\n});\n\nfunction setOrUpdateObject(id, obj, value, stateChangeCallback, createNow) {\n    let callback = null;\n    if (typeof value === 'function') {\n        createNow = stateChangeCallback;\n        stateChangeCallback = value;\n        value = undefined;\n    }\n    if (typeof createNow === 'function') {\n        callback = createNow;\n        createNow = true;\n    }\n\n    if (! obj.type) {\n        obj.type = 'state';\n    }\n    if (! obj.common) {\n        obj.common = {};\n    }\n    if (! obj.native) {\n        obj.native = {};\n    }\n    if (obj.common && obj.common.type === undefined && obj.type === 'state') {\n        if (value !== null && value !== undefined) {\n            obj.common.type = typeof value;\n        }\n        else if (obj.common.def !== undefined) {\n            obj.common.type = typeof obj.common.def;\n        }\n        else if (obj.common.min !== undefined || obj.common.max !== undefined) {\n            obj.common.type = 'number';\n        }\n        else {\n            obj.common.type = 'mixed';\n        }\n    }\n    if (obj.common && obj.common.read === undefined) {\n        obj.common.read = true; //!(obj.common.type === 'boolean' && !!stateChangeCallback);\n    }\n    if (obj.common && obj.common.write === undefined) {\n        obj.common.write = !!stateChangeCallback;\n    }\n    /*\n    if (obj.common && obj.common.def === undefined && value !== null && value !== undefined) {\n        obj.common.def = value;\n    }*/\n    if (obj.common && obj.common.name === undefined) {\n        obj.common.name = id.split('.').pop();\n    }\n\n    let preserveSettings;\n    if (obj.preserveSettings) {\n        preserveSettings = obj.preserveSettings;\n        delete obj.preserveSettings;\n    }\n\n    if (!adapterObjects[id] && existingStates[id]) {\n        adapterObjects[id] = existingStates[id];\n        if (adapterObjects[id].from) delete adapterObjects[id].from;\n        if (adapterObjects[id].ts) delete adapterObjects[id].ts;\n        if (adapterObjects[id].acl) delete adapterObjects[id].acl;\n        if (adapterObjects[id]._id) delete adapterObjects[id]._id;\n        if (obj.common && adapterObjects[id].common) {\n            if (obj.common.def === undefined && adapterObjects[id].common.def !== undefined) delete adapterObjects[id].common.def;\n            if (obj.common.unit === undefined && adapterObjects[id].common.unit !== undefined) delete adapterObjects[id].common.unit;\n            if (obj.common.min === undefined && adapterObjects[id].common.min !== undefined) delete adapterObjects[id].common.min;\n            if (obj.common.max === undefined && adapterObjects[id].common.max !== undefined) delete adapterObjects[id].common.max;\n        }\n        //value = undefined; // when exists and it is first time do not overwrite value!\n    }\n    if (existingStates[id]) delete(existingStates[id]);\n    if (adapterObjects[id] && isEquivalent(obj, adapterObjects[id])) {\n        //adapter.log.debug('Object unchanged for ' + id + ': ' + JSON.stringify(adapterObjects[id]) + ' - update only: ' + JSON.stringify(value));\n        //if (value !== undefined) adapter.setState(id, value, true);\n        //if (stateChangeCallback) stateChangeTrigger[id] = stateChangeCallback;\n        adapterObjects[id] = JSON.parse(JSON.stringify(obj));\n        obj = null;\n    } else {\n        adapterObjects[id] = JSON.parse(JSON.stringify(obj));\n    }\n    //adapter.log.debug('Add Object for ' + id + ': ' + JSON.stringify(adapterObjects[id]) + '/' + JSON.stringify(obj));\n\n    objectQueue.push({\n        id: id,\n        value: value,\n        obj: obj,\n        stateChangeCallback: stateChangeCallback,\n        preserveSettings\n    });\n    //adapter.log.debug('Create object for ' + id + ': ' + JSON.stringify(obj) + ' with value: ' + JSON.stringify(value));\n\n    if (createNow) {\n        processObjectQueue(callback);\n    }\n}\n\nfunction deleteObject(id) {\n    if (id.startsWith(adapter.namespace)) {\n        id = id.substr(adapter.namespace.length + 1);\n    }\n    const obj = adapterObjects[id];\n    if (obj && obj.type) {\n        if (obj.type !== 'state') {\n            adapter.log.debug(`Delete objects for ${id}`);\n            Object.keys(adapterObjects).forEach((objId) => {\n                if (objId.startsWith(`${id}.`)) {\n                    try {\n                        adapter.delObject(objId, (err) => {\n                            err = err ? ` (${err})` : '';\n                            if (!err) {\n                                delete adapterObjects[objId];\n                                delete stateChangeTrigger[objId];\n                            } else {\n                                adapter.log.info(`${adapterObjects[objId] ? adapterObjects[objId].type : '?'} ${objId} deleted${err}`);\n                            }\n                        });\n                    } catch (err) {\n                        adapter.log.info(`Cannot delete ${objId}: ${err}`);\n                    }\n                }\n            });\n        }\n        try {\n            adapter.delObject(id, (err) => {\n                if (!err) {\n                    delete adapterObjects[id];\n                    delete stateChangeTrigger[id];\n                } else {\n                    adapter.log.info(`${obj.type} ${id} deleted (${err})`);\n                }\n            });\n        } catch (err) {\n            adapter.log.info(`Cannot delete ${id}: ${err}`);\n        }\n    }\n    else {\n        adapter.log.info(`Try to delete unknown object ${id}`);\n    }\n}\n\nfunction ucFirst(str) {\n    return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nfunction isEquivalent(a, b) {\n    //adapter.log.debug('Compare ' + JSON.stringify(a) + ' with ' +  JSON.stringify(b));\n    // Create arrays of property names\n    if (a === null || a === undefined || b === null || b === undefined) {\n        return (a === b);\n    }\n    const aProps = Object.getOwnPropertyNames(a);\n    const bProps = Object.getOwnPropertyNames(b);\n\n    // If number of properties is different,\n    // objects are not equivalent\n    if (aProps.length !== bProps.length) {\n        //console.log('num props different: ' + JSON.stringify(aProps) + ' / ' + JSON.stringify(bProps));\n        return false;\n    }\n\n    for (let i = 0; i < aProps.length; i++) {\n        const propName = aProps[i];\n\n        if (typeof a[propName] !== typeof b[propName]) {\n            //console.log('type props ' + propName + ' different');\n            return false;\n        }\n        if (typeof a[propName] === 'object') {\n            if (!isEquivalent(a[propName], b[propName])) {\n                return false;\n            }\n        }\n        else {\n            // If values of same property are not equal,\n            // objects are not equivalent\n            if (a[propName] !== b[propName]) {\n                //console.log('props ' + propName + ' different');\n                return false;\n            }\n        }\n    }\n\n    // If we made it this far, objects\n    // are considered equivalent\n    return true;\n}\n\nfunction processObjectQueue(callback) {\n    if (!objectQueue.length) {\n        callback && callback();\n        return;\n    }\n\n    function handleObject(queueEntry, callback) {\n        if (!queueEntry.obj) {\n            handleValue(queueEntry, () => {\n                return callback && callback();\n            });\n            return;\n        }\n        const options = {};\n        if (queueEntry.preserveSettings) {\n            options.preserve = queueEntry.preserveSettings; //{ common: ['name'], native: true } };\n        }\n        adapter.getObject(queueEntry.id, (err, obj) => {\n            if (!err && obj) {\n                adapter.extendObject(queueEntry.id, queueEntry.obj, options, () => {\n                    handleValue(queueEntry, () => {\n                        return callback && callback();\n                    });\n                });\n            }\n            else {\n                adapter.setObject(queueEntry.id, queueEntry.obj, () => {\n                    handleValue(queueEntry, () => {\n                        return callback && callback();\n                    });\n                });\n            }\n        });\n    }\n\n    function handleValue(queueEntry, callback) {\n        if (queueEntry.value === undefined) {\n            stateChangeTrigger[queueEntry.id] = queueEntry.stateChangeCallback;\n            return callback && callback();\n        }\n        adapter.setState(queueEntry.id, queueEntry.value, true, () => {\n            stateChangeTrigger[queueEntry.id] = queueEntry.stateChangeCallback;\n            return callback && callback();\n        });\n    }\n\n    const queueEntry = objectQueue.shift();\n    handleObject(queueEntry, () => {\n        return processObjectQueue(callback);\n    });\n}\n\nfunction initSentry(callback) {\n    if (!adapter.ioPack.common || !adapter.ioPack.common.plugins || !adapter.ioPack.common.plugins.sentry) {\n        return callback && callback();\n    }\n    if (adapter.supportsFeature && adapter.supportsFeature('PLUGINS')) {\n        const sentryInstance = adapter.getPluginInstance('sentry');\n        if (sentryInstance) {\n            Sentry = sentryInstance.getSentryObject();\n        }\n    }\n    return callback && callback();\n}\n\nfunction startAdapter(options) {\n    options = options || {};\n    Object.assign(options, {\n        name: 'alexa2'\n    });\n\n    adapter = new utils.Adapter(options);\n\n    adapter.on('unload', (callback) => {\n        stopped = true;\n        if (resetCrashCheckerTimeout) {\n            clearTimeout(resetCrashCheckerTimeout);\n            if (!isCrashStop) {\n                try {\n                    if (fs.existsSync(crashCheckFileName)) {\n                        fs.unlinkSync(crashCheckFileName);\n                    }\n                } catch (err) {\n                    adapter.log.error(`Error deleting crashCheck file: ${err.message}`);\n                }\n            }\n        }\n        checkerInterval && clearInterval(checkerInterval);\n        updateSmartHomeDevicesTimer && clearTimeout(updateSmartHomeDevicesTimer);\n        updateStateTimer && clearTimeout(updateStateTimer);\n        updateConfigurationTimer && clearTimeout(updateConfigurationTimer);\n        updateHistoryTimer && clearTimeout(updateHistoryTimer);\n        Object.keys(notificationTimer).forEach(timer => notificationTimer[timer] && clearTimeout(notificationTimer[timer]));\n        Object.keys(updateNotificationTimer).forEach(timer => updateNotificationTimer[timer] && clearTimeout(updateNotificationTimer[timer]));\n        Object.keys(updatePlayerTimer).forEach(timer => updatePlayerTimer[timer] && clearTimeout(updatePlayerTimer[timer]));\n        Object.keys(lastPlayerState).forEach(timer => lastPlayerState[timer] && lastPlayerState[timer].timeout && clearTimeout(lastPlayerState[timer].timeout));\n        Object.keys(shDeviceRefreshTimeoutAfterControl).forEach(timer => shDeviceRefreshTimeoutAfterControl[timer] && clearTimeout(shDeviceRefreshTimeoutAfterControl[timer]));\n\n        if (alexa) {\n            alexa.stop();\n        }\n        callback && callback();\n    });\n\n    adapter.on('stateChange', (id, state) => {\n        adapter.log.debug(`State changed ${id}: ${JSON.stringify(state)}`);\n        if (!state || state.ack) return;\n        id = id.substr(adapter.namespace.length + 1);\n\n        if (state && state.val && state.from && state.from.startsWith('system.adapter.cloud') && id.endsWith('.Commands.speak')) {\n            state.val = state.val.replace(/<[^>]+>/g, '').replace('/  /g', ' ');\n        }\n\n        if (typeof stateChangeTrigger[id] === 'function') {\n            if (adapterObjects[id] && adapterObjects[id].common && adapterObjects[id].common.type && adapterObjects[id].common.type !== 'mixed') {\n                if (adapterObjects[id].common.type === 'boolean' && adapterObjects[id].common.role && adapterObjects[id].common.role.startsWith('button')) state.val = !!state.val;\n                if (typeof state.val !== adapterObjects[id].common.type) {\n                    adapter.log.error(`Datatype \"${typeof state.val}\" for ${id} differs from expected \"${adapterObjects[id].common.type}\", ignore state change! Please write correct datatype`);\n                    return;\n                }\n            }\n            stateChangeTrigger[id](state.val);\n        }\n\n        if (!pushConnected) scheduleStatesUpdate(3000);\n    });\n\n    adapter.on('objectChange', (id, object) => {\n        if (!initDone) return;\n        adapter.log.debug(`Object changed ${id}: ${JSON.stringify(object)}`);\n        if (!id || !id.startsWith(`${adapter.namespace}.Echo-Devices.`)) return;\n        const ar = id.split('.');\n        if (ar[2] === 'Echo-Devices' && ar.length === 4) {\n            if (object === null) {\n                //deleted, do nothing\n                return;\n            }\n            const device = alexa.serialNumbers[ar[3]];\n            if (device && object && object.common && object.common.name && device._name && object.common.name !== device._name) {\n                if (typeof device.rename === 'function') device.rename(object.common.name);\n            }\n        }\n    });\n\n    adapter.on('message', function(msg) {\n        processMessage(msg);\n    });\n\n    adapter.on('ready', () => {\n        initSentry(() => loadExistingAccessories(checkInstanceObject(main)));\n    });\n\n    return adapter;\n}\n\nfunction checkInstanceObject(callback) {\n    adapter.getForeignObject('system.adapter.' + adapter.namespace, (err, obj) => {\n        if (obj && obj.common && obj.common.restartSchedule) {\n            const cronParts = obj.common.restartSchedule.split(' ');\n            if ((cronParts[0] || '').startsWith('*') || (cronParts[1] || '').startsWith('*') || (cronParts.length === 6 && cronParts[2].startsWith('*'))) {\n                adapter.log.error(`Restart schedule \"${obj.common.restartSchedule}\" is too often, please set a restart schedule that makes sense. Disabling adapter now.`);\n                adapter.terminate && adapter.terminate(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);\n                return;\n            }\n        }\n        callback();\n    });\n}\n\nfunction processMessage(msg) {\n    adapter.log.debug(`Message: ${JSON.stringify(msg)}`);\n    switch (msg.command) {\n        case 'getStatusInfo':\n            getStatusInfo(msg);\n            break;\n        case 'sendSequenceCommand':\n            sendSequenceCommand(msg);\n            break;\n    }\n}\n\nfunction getStatusInfo(msg) {\n    adapter.sendTo(msg.from, msg.command, {\n        result: {\n            proxyActive: proxyUrl !== null,\n            proxyUrl: proxyUrl,\n            cookieTimestamp: (adapter.config.cookieData && adapter.config.cookieData.tokenDate) ? adapter.formatDate(adapter.config.cookieData.tokenDate) : 0\n        },\n        error: null\n    }, msg.callback);\n}\n\nfunction sendSequenceCommand(msg) {\n    if (!msg.message.sequenceNodes) {\n        adapter.sendTo(msg.from, msg.command, {\n            result: null,\n            error: 'No sequenceNodes given'\n        }, msg.callback);\n        return;\n    }\n    if (!msg.message.deviceSerialNumber) {\n        adapter.sendTo(msg.from, msg.command, {\n            result: null,\n            error: 'No deviceId given'\n        }, msg.callback);\n        return;\n    }\n    if (!alexa) {\n        adapter.sendTo(msg.from, msg.command, {\n            result: null,\n            error: 'Alexa connection not initialized'\n        }, msg.callback);\n        return;\n    }\n    alexa.sendMultiSequenceCommand(msg.message.deviceSerialNumber, msg.message.sequenceNodes, msg.message.sequenceType, alexa.ownerCustomerId, (err, res) => {\n        adapter.sendTo(msg.from, msg.command, {\n            result: res,\n            error: err\n        }, msg.callback);\n    });\n}\n\nfunction setRequestResult(err, res) {\n    if (!err) return;\n    adapter.setState('requestResult', err.message ? err.message : err, true);\n}\n\n/**\n * Konvertiert eine Sekundenzahl in einen String im Format (HH:)MM:SS\n *\n * @param {number} sec seconds\n * @return string\n */\nfunction sec2HMS(sec) {\n    if (sec  === 0) {\n        return '0';\n    }\n\n    const sec_num = parseInt(sec, 10);\n    let hours   = Math.floor(sec_num / 3600);\n    let minutes = Math.floor((sec_num - (hours * 3600)) / 60);\n    let seconds = sec_num - (hours * 3600) - (minutes * 60);\n\n    if (minutes < 10) {minutes = `0${minutes}`;}\n    if (seconds < 10) {seconds = `0${seconds}`;}\n    if (hours === 0) {\n        return `${minutes}:${seconds}`;\n    }\n\n    if (hours < 10) {hours = `0${hours}`;}\n    return `${hours}:${minutes}:${seconds}`;\n}\n\n\nfunction scheduleNotificationUpdate(deviceId, delay, onlyIfNew) {\n    if (updateNotificationTimer[deviceId]) {\n        if (onlyIfNew) return;\n        clearTimeout(updateNotificationTimer[deviceId]);\n    }\n    updateNotificationTimer[deviceId] = setTimeout(() => {\n        updateNotificationTimer[deviceId] = null;\n        updateNotificationStates(deviceId);\n    }, delay);\n}\n\nfunction schedulePlayerUpdate(deviceId, delay, onlyIfNew) {\n    if (updatePlayerTimer[deviceId]) {\n        if (onlyIfNew) return;\n        clearTimeout(updatePlayerTimer[deviceId]);\n    }\n    adapter.log.debug(`Schedule new Player Update in ${delay}ms`);\n    updatePlayerTimer[deviceId] = setTimeout(() => {\n        updatePlayerTimer[deviceId] = null;\n        updatePlayerStatus(deviceId);\n    }, delay);\n}\n\nfunction scheduleStatesUpdate(delay) {\n    if (updateStateTimer) {\n        clearTimeout(updateStateTimer);\n    }\n    if (delay === undefined) {\n        if (!adapter.config.updateStateInterval) return;\n        delay = adapter.config.updateStateInterval * 1000;\n        if (pushConnected) delay = 60 * 60 * 1000; // 1h\n    }\n    delay = Math.max(delay, 300000);\n    updateStateTimer = setTimeout(() => {\n        if (stopped) return;\n        updateStateTimer = null;\n        updateStates();\n    }, delay);\n}\n\nasync function setEqualizer(device) {\n    delete equalizerUpdateTimeouts[device.serialNumber];\n\n    const bassState = await adapter.getStateAsync(`Echo-Devices.${device.serialNumber}.Preferences.equalizerBass`);\n    const midState = await adapter.getStateAsync(`Echo-Devices.${device.serialNumber}.Preferences.equalizerMidRange`);\n    const trebleState = await adapter.getStateAsync(`Echo-Devices.${device.serialNumber}.Preferences.equalizerTreble`);\n\n    alexa.setEqualizerSettings(device, bassState ? bassState.val : undefined, midState ? midState.val : undefined, trebleState ? trebleState.val : undefined, (err, res) => {\n        if (err || !res) {\n            adapter.log.error(`${device.serialNumber} Error setting equalizer settings: ${err}`);\n            return;\n        }\n        if (device.capabilities.includes('EQUALIZER_CONTROLLER_BASS') && res.bass !== undefined) {\n            adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.equalizerBass`, res.bass, true);\n        }\n        if (device.capabilities.includes('EQUALIZER_CONTROLLER_MIDRANGE') && res.mid !== undefined) {\n            adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.equalizerMidRange`, res.mid, true);\n        }\n        if (device.capabilities.includes('EQUALIZER_CONTROLLER_TREBLE') && res.treble !== undefined) {\n            adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.equalizerTreble`, res.treble, true);\n        }\n    });\n}\n\nfunction initEqualizerData(device) {\n    return new Promise(resolve => {\n        if (!device.capabilities.includes('EQUALIZER_CONTROLLER_BASS') && !device.capabilities.includes('EQUALIZER_CONTROLLER_MIDRANGE') && !device.capabilities.includes('EQUALIZER_CONTROLLER_TREBLE')) {\n            return resolve(0);\n        }\n        alexa.getEqualizerEnabled(device, (err, res) => {\n            if (err || !res) {\n                adapter.log.error(`${device.serialNumber} Error getting equalizer enabled: ${err}`);\n                return resolve(0);\n            }\n            if (!res.enabled) {\n                return resolve(0);\n            }\n            alexa.getEqualizerRange(device, (err, res) => {\n                if (err || !res) {\n                    adapter.log.error(`${device.serialNumber} Error getting equalizer range: ${err}`);\n                    return resolve(0);\n                }\n                const rangeMin = res.min || -6;\n                const rangeMax = res.max || 6;\n\n                alexa.getEqualizerSettings(device, (err, res) => {\n                    if (err || !res) {\n                        adapter.log.error(`${device.serialNumber} Error getting equalizer setting: ${err}`);\n                        return resolve(0);\n                    }\n\n                    let numStates = 0;\n\n                    if (device.capabilities.includes('EQUALIZER_CONTROLLER_BASS')) {\n                        numStates++;\n                        setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.equalizerBass`, {\n                            common: {\n                                name: `Equalizer Bass`,\n                                type: 'number',\n                                role: 'level.bass',\n                                min: rangeMin,\n                                max: rangeMax\n                            }\n                        }, res.bass, function (device, value) {\n                            if (typeof value !== 'number') {\n                                value = parseInt(value, 10);\n                                if (isNaN(value)) {\n                                    adapter.log.error(`${device.serialNumber} Error bass value must be a number`);\n                                    return;\n                                }\n                            }\n                            equalizerUpdateTimeouts[device.serialNumber] && clearTimeout(equalizerUpdateTimeouts[device.serialNumber]);\n                            equalizerUpdateTimeouts[device.serialNumber] = setTimeout(() => setEqualizer(device), 500);\n                        }.bind(alexa, device));\n                    }\n                    if (device.capabilities.includes('EQUALIZER_CONTROLLER_MIDRANGE')) {\n                        numStates++;\n                        setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.equalizerMidRange`, {\n                            common: {\n                                name: `Equalizer Midrange`,\n                                type: 'number',\n                                role: 'level.midrange',\n                                min: rangeMin,\n                                max: rangeMax\n                            }\n                        }, res.mid, function (device, value) {\n                            if (typeof value !== 'number') {\n                                value = parseInt(value, 10);\n                                if (isNaN(value)) {\n                                    adapter.log.error(`${device.serialNumber} Error midrange value must be a number`);\n                                    return;\n                                }\n                            }\n                            equalizerUpdateTimeouts[device.serialNumber] && clearTimeout(equalizerUpdateTimeouts[device.serialNumber]);\n                            equalizerUpdateTimeouts[device.serialNumber] = setTimeout(() => setEqualizer(device), 500);\n                        }.bind(alexa, device));\n                    }\n                    if (device.capabilities.includes('EQUALIZER_CONTROLLER_TREBLE')) {\n                        numStates++;\n                        setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.equalizerTreble`, {\n                            common: {\n                                name: `Equalizer Treble`,\n                                type: 'number',\n                                role: 'level.treble',\n                                min: rangeMin,\n                                max: rangeMax\n                            }\n                        }, res.treble, function (device, value) {\n                            if (typeof value !== 'number') {\n                                value = parseInt(value, 10);\n                                if (isNaN(value)) {\n                                    adapter.log.error(`${device.serialNumber} Error treble value must be a number`);\n                                    return;\n                                }\n                            }\n                            equalizerUpdateTimeouts[device.serialNumber] && clearTimeout(equalizerUpdateTimeouts[device.serialNumber]);\n                            equalizerUpdateTimeouts[device.serialNumber] = setTimeout(() => setEqualizer(device), 500);\n                        }.bind(alexa, device));\n                    }\n\n                    resolve(numStates);\n                });\n            });\n        });\n    });\n}\n\nfunction initDeviceAuxController(device) {\n    return new Promise(resolve => {\n        let numStates = 0;\n        alexa.getAuxControllerState(device, (err, res) => {\n            if (err) {\n                adapter.log.error(`${device.serialNumber} Error getting aux controller state: ${err}`);\n                return resolve(0);\n            }\n            if (res && res.ports && Array.isArray(res.ports) && device.capabilities.includes('AUX_SETTINGS')) {\n                res.ports.forEach(port => {\n                    numStates++;\n                    setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.auxPort-${port.id}-Direction`, {\n                        common: {\n                            name: `Port ${port.id} direction`,\n                            type: 'string',\n                            role: 'text',\n                            states: {'INPUT': 'INPUT', 'OUTPUT': 'OUTPUT'}\n                        }\n                    }, port.direction, function (device, port, value) {\n                        if (value !== 'INPUT' && value !== 'OUTPUT') {\n                            adapter.log.error(`${device.serialNumber} Error port direction value invalid`);\n                            return;\n                        }\n                        alexa.setAuxControllerPortDirection(device, value, port.id, (err, res) => {\n                            if (err) {\n                                adapter.log.error(`${device.serialNumber} Error setting aux controller port direction: ${err}`);\n                                return;\n                            }\n                            adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.auxPort-${port.id}-Direction`, value, true);\n                        });\n                    }.bind(alexa, device, port));\n                });\n            }\n\n            alexa.getConnectedSpeakerOptionSetting(device, (err, res2) => {\n                if (err) {\n                    adapter.log.error(`${device.serialNumber} Error getting connected speaker setting: ${err}`);\n                    return resolve(numStates);\n                }\n                if (res2) {\n                    numStates++;\n                    const states = {\n                        'Bluetooth': 'Bluetooth',\n                        'InternalSpeaker': 'InternalSpeaker'\n                    };\n                    if (res && res.ports && res.ports.length) {\n                        states['Aux'] = 'Aux (if supported)';\n                    }\n                    setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.connectedSpeaker`, {\n                        common: {\n                            name: `Connected Speaker Option`,\n                            type: 'string',\n                            role: 'text',\n                            states\n                        }\n                    }, res2, function (device, value) {\n                        if (states[value] === undefined) {\n                            adapter.log.error(`${device.serialNumber} Error connected speaker value invalid`);\n                            return;\n                        }\n                        alexa.setConnectedSpeakerOptionSetting(device, value, (err,res) => {\n                            if (err) {\n                                adapter.log.error(`${device.serialNumber} Error setting connected speaker setting: ${err}`);\n                                return;\n                            }\n                            adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.connectedSpeaker`, value, true);\n                        });\n                    }.bind(alexa, device));\n                }\n                resolve(numStates);\n            });\n        });\n    });\n}\n\nfunction initDisplaySettings(device) {\n    // eslint-disable-next-line no-async-promise-executor\n    return new Promise(async resolve => {\n        if (!device.capabilities.includes('DISPLAY_POWER_TOGGLE') && !device.capabilities.includes('DISPLAY_BRIGHTNESS_ADJUST') && !device.capabilities.includes('DISPLAY_ADAPTIVE_BRIGHTNESS')) {\n            return resolve(0);\n        }\n        let numStates = 0;\n        if (device.capabilities.includes('DISPLAY_POWER_TOGGLE')) {\n            numStates++;\n            const displayPower = await new Promise(resolve => {\n                alexa.getDisplayPowerSetting(device, (err, res) => {\n                    if (err) {\n                        adapter.log.error(`${device.serialNumber} Error getting display enabled setting: ${err}`);\n                        return resolve(undefined);\n                    }\n                    resolve(res);\n                });\n            });\n            setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.displayEnabled`, {\n                common: {\n                    name: `Display enabled`,\n                    type: 'boolean',\n                    role: 'switch'\n                }\n            }, displayPower, function (device, value) {\n                alexa.setDisplayPowerSetting(device, !!value, (err, res) => {\n                    if (err) {\n                        adapter.log.error(`${device.serialNumber} Error setting display enabled: ${err}`);\n                        return;\n                    }\n                    adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.displayEnabled`, !!value, true);\n                });\n            }.bind(alexa, device));\n        }\n        if (device.capabilities.includes('DISPLAY_BRIGHTNESS_ADJUST')) {\n            numStates++;\n            const displayBrightness = await new Promise(resolve => {\n                alexa.getBrightnessSetting(device, (err, res) => {\n                    if (err) {\n                        adapter.log.error(`${device.serialNumber} Error getting display brightness setting: ${err}`);\n                        return resolve(undefined);\n                    }\n                    resolve(res);\n                });\n            });\n            setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.displayBrightness`, {\n                common: {\n                    name: `Display Brightness`,\n                    type: 'number',\n                    role: 'level.brightness',\n                    min: 0,\n                    max: 100,\n                    unit: '%'\n                }\n            }, displayBrightness, function (device, value) {\n                if (typeof value !== 'number') {\n                    value = parseInt(value, 10);\n                    if (isNaN(value)) {\n                        adapter.log.error(`${device.serialNumber} Error brightness value must be a number`);\n                        return;\n                    }\n                }\n                if (value < 0 || value > 100) {\n                    adapter.log.error(`${device.serialNumber} Error brightness value must be between 0 and 100`);\n                    return;\n                }\n                alexa.setBrightnessSetting(device, value, (err, res) => {\n                    if (err) {\n                        adapter.log.error(`${device.serialNumber} Error setting display brightness: ${err}`);\n                        return;\n                    }\n                    adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.displayBrightness`, value, true);\n                });\n            }.bind(alexa, device));\n        }\n        if (device.capabilities.includes('DISPLAY_ADAPTIVE_BRIGHTNESS')) {\n            numStates++;\n            const adaptiveBrightness = await new Promise(resolve => {\n                alexa.getAdaptiveBrightnessSetting(device, (err, res) => {\n                    if (err) {\n                        adapter.log.error(`${device.serialNumber} Error getting display adaptive brightness setting: ${err}`);\n                        return resolve(undefined);\n                    }\n                    resolve(res);\n                });\n            });\n            setOrUpdateObject(`Echo-Devices.${device.serialNumber}.Preferences.displayAdaptiveBrightnessEnabled`, {\n                common: {\n                    name: `Display adaptive brightness`,\n                    type: 'boolean',\n                    role: 'switch'\n                }\n            }, adaptiveBrightness, function (device, value) {\n                alexa.setAdaptiveBrightnessSetting(device, !!value, (err, res) => {\n                    if (err) {\n                        adapter.log.error(`${device.serialNumber} Error setting display adaptive brightness: ${err}`);\n                        return;\n                    }\n                    adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.displayAdaptiveBrightnessEnabled`, !!value, true);\n                });\n            }.bind(alexa, device));\n        }\n        resolve(numStates);\n    });\n}\n\n\nfunction updateDeviceConfigurationStates(callback) {\n    if (stopped) return;\n    alexa.getDoNotDisturb((err, res) => {\n        if (stopped) return;\n\n        if (!err && res && res.doNotDisturbDeviceStatusList && Array.isArray(res.doNotDisturbDeviceStatusList)) {\n            res.doNotDisturbDeviceStatusList.forEach(status => {\n                const device = alexa.find(status.deviceSerialNumber);\n                if (device && device.deviceTypeDetails && device.deviceTypeDetails.commandSupport) {\n                    adapter.setState(`Echo-Devices.${device.serialNumber}.Commands.doNotDisturb`, status.enabled, true);\n                }\n            });\n        }\n\n        alexa.getDevicePreferences((err, res) => {\n            if (stopped) return;\n            if (!err && res && res.devicePreferences && Array.isArray(res.devicePreferences)) {\n                res.devicePreferences.forEach(pref => {\n                    if (!pref.deviceSerialNumber) return;\n                    const device = alexa.find(pref.deviceSerialNumber);\n                    if (!device) return;\n                    device.preferences = pref;\n\n                    if (device.capabilities.includes('EARCONS') && pref.notificationEarconEnabled !== undefined && adapterObjects[`Echo-Devices.${device.serialNumber}.Preferences.ringNotificationsEnabled`]) {\n                        adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.ringNotificationsEnabled`, pref.notificationEarconEnabled, true);\n                    }\n                });\n            }\n\n            if (adapter.config.updateConfigurationInterval > 0) {\n                updateConfigurationTimer = setTimeout(() => {\n                    updateDeviceConfigurationStates();\n                }, Math.max(adapter.config.updateConfigurationInterval * 1000, 300000));\n            }\n            callback && callback();\n        });\n    });\n}\n\nfunction updateStates(callback) {\n    if (updateStateTimer) {\n        clearTimeout(updateStateTimer);\n        updateStateTimer = null;\n    }\n    if (stopped) return;\n\n    updateDeviceStatus(() => {\n        updateBluetoothStatus(() => {\n            updatePlayerStatus(callback);\n        });\n    });\n}\n\n/**\n * Inkrementiert 'mediaProgress' alle 2 Sekunden um 2. So wird ein permanentes https-get überflüssig\n * ruft sich nach 2 Sekunden erneut selbst auf, wenn 'currentState' noch auf 'PLAYING' steht.\n * ist 'mediaProgress' größer als 'mediaLength', so ist der Song zu Ende und 'updateDevice' wird aufgerufen.\n *\n * @param {string} serialNumber serial number\n */\nfunction updateMediaProgress(serialNumber) {\n    if (!lastPlayerState[serialNumber] || !lastPlayerState[serialNumber].resPlayer) return;\n\n    if (lastPlayerState[serialNumber].timeout) {\n        clearTimeout(lastPlayerState[serialNumber].timeout);\n        lastPlayerState[serialNumber].timeout = null;\n    }\n\n    const resPlayer = lastPlayerState[serialNumber].resPlayer;\n    const devId = lastPlayerState[serialNumber].devId;\n    const lastTimestamp = lastPlayerState[serialNumber].ts;\n\n    const currentState = resPlayer.playerInfo.state;\n\n    if (currentState === 'PLAYING' && resPlayer.playerInfo && resPlayer.playerInfo.progress) {\n        const mediaProgress = parseInt(resPlayer.playerInfo.progress.mediaProgress, 10);\n        const mediaLength = parseInt(resPlayer.playerInfo.progress.mediaLength || '0', 10);\n        const timeframe = ~~((Date.now() - lastTimestamp) / 1000); // calculate time since last data\n        const mediaProgressNew = mediaProgress + timeframe; // add this to the progress\n\n        // Am Ende des Titels soll neu geladen werden. Ist es Radio (länge = 0) dann alle 200 sekunden\n        if (mediaProgressNew > mediaLength && (mediaLength > 0 || mediaProgressNew % 200 < 2)) {\n            schedulePlayerUpdate(serialNumber, 2000);\n            return;\n        }\n\n        // Nun mediaProgress und mediaProgressPercent neu berechnen\n        let mediaProgressPercent = 0;\n        let mediaRemaining = 0;\n        if (mediaLength > 0) {\n            mediaProgressPercent = Math.round((((mediaProgressNew) * 100) / mediaLength));\n            mediaRemaining = mediaLength - mediaProgressNew;\n        }\n        adapter.setState(`${devId}.Player.mediaProgressPercent`, mediaProgressPercent, true);\n        adapter.setState(`${devId}.Player.mediaProgress`, mediaProgressNew, true);\n        adapter.setState(`${devId}.Player.mediaProgressStr`, sec2HMS(mediaProgressNew), true);\n        adapter.setState(`${devId}.Player.mediaRemaining`, mediaRemaining, true);\n        adapter.setState(`${devId}.Player.mediaLengthStr`, sec2HMS(mediaRemaining), true);\n\n        lastPlayerState[serialNumber].timeout = setTimeout( () => {\n            lastPlayerState[serialNumber].timeout = null;\n            updateMediaProgress(serialNumber);\n        }, 2000);\n    }\n    else {\n        schedulePlayerUpdate(serialNumber, (2 * 60 * 60 + 5) * 1000);\n    }\n}\n\nfunction generateApplianceQueryArray(applianceId, queryAllProperties) {\n    let propertiesToQuery;\n    if (!queryAllProperties) {\n        propertiesToQuery = shApplianceEntityMap[applianceId].supportedProperties;\n    }\n    if (!queryAllProperties && (!propertiesToQuery || !propertiesToQuery.length)) {\n        return [];\n    }\n\n    const reqArr = [];\n    const reqApplianceIds = {};\n    reqApplianceIds[applianceId] = true;\n    if (shApplianceEntityMap[applianceId].mergedApplianceIds && Array.isArray(shApplianceEntityMap[applianceId].mergedApplianceIds)) {\n        shApplianceEntityMap[applianceId].mergedApplianceIds.forEach(id => {\n            reqApplianceIds[id] = true;\n        });\n    }\n    Object.keys(reqApplianceIds).forEach(id => {\n        reqArr.push({\n            entityId: id,\n            entityType: 'APPLIANCE',\n            properties: propertiesToQuery\n        });\n    });\n    if (reqArr.length > 1 || (reqArr.length === 1 && !reqArr[0].entityId.startsWith('SKILL'))) {\n        reqArr.push({\n            entityId: shApplianceEntityMap[applianceId].entityId,\n            entityType: 'ENTITY'\n        });\n    }\n    return reqArr;\n}\n\nfunction queryAllSmartHomeDevices(initial, cloudOnly, callback) {\n    if (updateSmartHomeDevicesTimer) {\n        clearTimeout(updateSmartHomeDevicesTimer);\n        updateSmartHomeDevicesTimer = null;\n    }\n\n    if (!adapter.config.synchronizeSmartHomeDevices) {\n        return callback && callback();\n    }\n\n    const reqArr = [];\n    const blocked = [];\n    for (const applianceId of Object.keys(shApplianceEntityMap)) {\n        const queryIt = shQueryEnabled[applianceId] && (cloudOnly ? shApplianceEntityMap[applianceId].cloudReadable : shApplianceEntityMap[applianceId].readable);\n\n        if (queryIt) {\n            if (shQueryBlocker[applianceId]) {\n                blocked.push(applianceId);\n                continue;\n            }\n            reqArr.push(...generateApplianceQueryArray(applianceId, false));\n            if (!initial) {\n                /**\n                 * Please DO NOT modify these block values and DO NOT lower them! Doing this might mean that Amazon\n                 * blocks the Smart home device queries for all > 20k ioBroker Adapter users!\n                 */\n                let delay = 1200000;\n                if (!applianceId.startsWith('SKILL_') && shApplianceEntityMap[applianceId].cloudReadable) delay = 900000;\n                shQueryBlocker[applianceId] = setTimeout(() => {\n                    shQueryBlocker[applianceId] = null;\n                }, delay);\n            }\n        }\n    }\n    if (blocked.length) {\n        adapter.log.warn(`Smarthome device queries blocked for ${blocked.length} devices: ${blocked.join(',')}`);\n    }\n\n    if (!reqArr.length) {\n        adapter.log.info('No smart home devices to query');\n        return callback && callback();\n    }\n\n    const cachedDeviceStatesFileName = path.join(__dirname, `cachedDeviceStates.${adapter.namespace}.json`);\n    try {\n        if (fs.existsSync(cachedDeviceStatesFileName)) {\n            const stats = fs.statSync(cachedDeviceStatesFileName);\n            if (stats.mtime.getTime() + 900 * 1000 > Date.now()) {\n                adapter.log.info(`Home Device states last requested ${stats.mtime} ... Do not request again now`);\n\n                return callback && callback();\n            }\n        }\n    } catch (err) {\n        adapter.log.info('Could not cache devices: ' + err.message);\n    }\n\n    alexa.querySmarthomeDevices(reqArr, (err, res) => {\n        if (!err) {\n            if (res && !blocked.length) {\n                try {\n                    fs.writeFileSync(cachedDeviceStatesFileName, JSON.stringify(res));\n                } catch (err) {\n                    adapter.log.info(`Cannot write cached device states to file: ${err}`);\n                }\n            }\n            updateSmarthomeDeviceStates(res);\n        }\n\n        return callback && callback();\n    });\n}\n\nfunction buildSmartHomeControlParameters(entityId, objs, selectorName, changedParamName, changedParamvalue, capInstance) {\n    function getValueToSend(obj, value) {\n        if (value && obj.native.valueTrue) {\n            value = obj.native.valueTrue;\n        }\n        else if (!value && obj.native.valueFalse) {\n            value = obj.native.valueFalse;\n        }\n        else if (obj.native.valueMap) {\n            value = obj.native.valueMap[value];\n        }\n        else {\n            if (value === undefined && obj.native.defaultValue !== undefined) {\n                value = obj.native.defaultValue;\n            }\n            if (typeof value === 'number' && obj.native.factor) {\n                value *= obj.native.factor;\n            }\n            value = String(value);\n        }\n        return value;\n    }\n\n    adapter.log.debug(`buildSmartHomeControlParameters: ${entityId} ${selectorName} ${changedParamName} ${changedParamvalue} for objs = ${JSON.stringify(objs)}`);\n\n    const fullObj = objs;\n    if (selectorName !== null) {\n        objs = objs[selectorName];\n    }\n\n    const parameters = {};\n    let sendAdditional = [];\n    if (!Array.isArray(objs)) objs = [objs];\n    if (capInstance) {\n        parameters.instance = capInstance;\n    }\n    for (const obj of objs) {\n        let paramName = obj.common.name;\n        const stateId = capInstance ? `${capInstance.replace(/\\./g, '-')}-${obj.common.name}`: obj.common.name;\n        if (obj.common.name === changedParamName) {\n            if (obj.native.action) {\n                parameters.action = obj.native.action;\n                if (obj.native.paramName) paramName = obj.native.paramName;\n                if (obj.native.sendInSubStructureOf) {\n                    parameters[obj.native.sendInSubStructureOf] = parameters[obj.native.sendInSubStructureOf] || {};\n                    parameters[obj.native.sendInSubStructureOf][paramName] = getValueToSend(obj, changedParamvalue);\n                } else {\n                    parameters[paramName] = getValueToSend(obj, changedParamvalue);\n                }\n            }\n            else {\n                if (changedParamvalue && obj.native.actionTrue) {\n                    parameters.action = obj.native.actionTrue;\n                }\n                else if (!changedParamvalue && obj.native.actionFalse) {\n                    parameters.action = obj.native.actionFalse;\n                }\n            }\n            if (obj.native.sendAdditional) {\n                sendAdditional = sendAdditional.concat(obj.native.sendAdditional);\n            }\n        }\n        else if (!obj.native.donotsend) {\n            if (obj.native.paramName) paramName = obj.native.paramName;\n            if (obj.native.sendInSubStructureOf) {\n                parameters[obj.native.sendInSubStructureOf] = parameters[obj.native.sendInSubStructureOf] || {};\n                parameters[obj.native.sendInSubStructureOf][paramName] = getValueToSend(obj, shDeviceParamValues[`Smart-Home-Devices.${entityId}.${stateId}`]);\n            } else {\n                parameters[paramName] = getValueToSend(obj, shDeviceParamValues[`Smart-Home-Devices.${entityId}.${stateId}`]);\n            }\n        }\n    }\n    if (sendAdditional.length) {\n        sendAdditional.forEach(actionName => {\n            let addObjs = fullObj[actionName];\n            if (!Array.isArray(addObjs)) addObjs = [addObjs];\n            for (const obj of addObjs) {\n                let paramName = obj.common.name;\n                if (!obj.native.donotsend) {\n                    if (obj.native.paramName) paramName = obj.native.paramName;\n                    if (obj.native.sendInSubStructureOf) {\n                        parameters[obj.native.sendInSubStructureOf] = parameters[obj.native.sendInSubStructureOf] || {};\n                        parameters[obj.native.sendInSubStructureOf][paramName] = getValueToSend(obj, shDeviceParamValues[`Smart-Home-Devices.${entityId}.${obj.common.name}`]);\n                    } else {\n                        parameters[paramName] = getValueToSend(obj, shDeviceParamValues[`Smart-Home-Devices.${entityId}.${obj.common.name}`]);\n                    }\n                }\n            }\n        });\n    }\n\n    adapter.log.debug(`buildSmartHomeControlParameters Response: ${JSON.stringify(parameters)}`);\n    return parameters;\n}\n\nfunction padding(num) {\n    num = num.toString(16);\n    if (num.length < 2) num = `0${num}`;\n    return num;\n}\n\n// expected hue range: [0, 360]\n// expected saturation range: [0, 1]\n// expected lightness range: [0, 1]\n// Based on http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\nfunction hsvToRgb(h, s, v) {\n    let r;\n    let g;\n    let b;\n    h = h / 360;\n    const i = Math.floor(h * 6);\n    const f = h * 6 - i;\n    const p = v * (1 - s);\n    const q = v * (1 - f * s);\n    const t = v * (1 - (1 - f) * s);\n\n    switch(i % 6){\n        case 0:\n            r = v;\n            g = t;\n            b = p;\n            break;\n\n        case 1:\n            r = q;\n            g = v;\n            b = p;\n            break;\n\n        case 2:\n            r = p;\n            g = v;\n            b = t;\n            break;\n\n        case 3:\n            r = p;\n            g = q;\n            b = v;\n            break;\n\n        case 4:\n            r = t;\n            g = p;\n            b = v;\n            break;\n\n        case 5:\n            r = v;\n            g = p;\n            b = q;\n            break;\n    }\n    return `#${padding(Math.round(r * 255))}${padding(Math.round(g * 255))}${padding(Math.round(b * 255))}`;\n}\n\nfunction updateSmarthomeDeviceStates(res) {\n    function handleObject(deviceEntityId, cap, stateName) {\n        if (!adapterObjects[`Smart-Home-Devices.${deviceEntityId}.${stateName}`]) {\n            adapter.log.debug(`ignoring value \"${cap.namespace}.${cap.value}\" for Smart-Home-Devices.${deviceEntityId}.${stateName}`);\n            return null;\n        }\n        const native = adapterObjects[`Smart-Home-Devices.${deviceEntityId}.${stateName}`].native;\n        const common = adapterObjects[`Smart-Home-Devices.${deviceEntityId}.${stateName}`].common;\n        let value = cap.value;\n        if (typeof value === 'object') {\n            if (value[native.valueSubKey || 'value'] === undefined) {\n                if (!native.noFallbackStringifiedValue) {\n                    value = JSON.stringify(value);\n                } else {\n                    value = undefined;\n                }\n            } else {\n                value = value[native.valueSubKey || 'value'];\n                if (value !== null && typeof value === 'object' && !native.noFallbackStringifiedValue) { // TODO: maybe always stringified?\n                    value = JSON.stringify(value);\n                }\n            }\n        } else if (native.valueSubKey && native.valueSubKey !== 'value') {\n            // We requested a sub value !== 'value' key but it is a plain value, so value requested not present\n            value = undefined;\n        }\n        if (value === undefined) {\n            adapter.log.debug(`Value not provided in answer for Smart-Home-Devices.${deviceEntityId}.${stateName}`);\n            return null;\n        }\n        if (native.valueTrue && native.valueTrue === value) {\n            value = true;\n        } else if (native.valueFalse && native.valueFalse === value) {\n            value = false;\n        } else if (native.valueTrue && !native.valueFalse && native.valueTrue !== value) {\n            value = false;\n        } else if (native.valueFalse && !native.valueTrue && native.valueFalse !== value) {\n            value = true;\n        } else if (native.valueMap && Array.isArray(native.valueMap) && native.valueMap.length) {\n            adapter.log.debug(`Get Index for value \"${cap.namespace}.${cap.value}\" for Smart-Home-Devices.${deviceEntityId}.${stateName}, value=${value} of ${JSON.stringify(native.valueMap)}`);\n            value = native.valueMap.indexOf(value);\n            if (value === -1) return null;\n        }\n        if (typeof value !== common.type && common.type !== 'mixed') {\n            if (common.type === 'number') {\n                const convertedValue = parseFloat(value);\n                if (!isNaN(convertedValue)) {\n                    value = convertedValue;\n                }\n            } else if (common.type === 'boolean') {\n                value = !!value;\n            }\n        }\n        value = {\n            val: value,\n            ack: true\n        };\n        if (cap.timeOfSample) value.ts = new Date(cap.timeOfSample).getTime();\n        adapter.setState(`Smart-Home-Devices.${deviceEntityId}.${stateName}`, value);\n        shDeviceParamValues[`Smart-Home-Devices.${deviceEntityId}.${stateName}`] = value.val;\n        return value.val;\n    }\n\n    if (!res) return;\n    if (res && res.errors && res.errors.length === 1 && res.errors[0] && res.errors[0].code) {\n        if (!res.deviceStates || res.deviceStates.length === 0) {\n            adapter.setState('requestResult', res.errors[0].code, true);\n        }\n    }\n    if (res.deviceStates) {\n        for (const states of res.deviceStates) {\n            if (!states.entity || !states.entity.entityId) continue;\n            const inputEntityId = states.entity.entityId;\n            let mainApplianceId;\n            if (shApplianceEntityMap[inputEntityId]) { // ID is a \"main appliance id\"\n                mainApplianceId = inputEntityId;\n            } else if (shMergedApplianceIdMap[inputEntityId]) { // ID is a \"merged appliance id\"\n                mainApplianceId = shMergedApplianceIdMap[inputEntityId];\n            } else if (shEntityApplianceMap[inputEntityId]) { // ID is an \"entity id\"\n                mainApplianceId = shEntityApplianceMap[inputEntityId];\n            }\n            if (!mainApplianceId) {\n                adapter.log.debug(`No main appliance id found for ${inputEntityId}`);\n                continue;\n            }\n\n            const deviceEntityId = shApplianceEntityMap[mainApplianceId].entityId;\n            if (deviceEntityId && states.capabilityStates) {\n                let colorDataIncluded = false;\n                const capValues = {};\n                for (let cap of states.capabilityStates) {\n                    try {\n                        cap = JSON.parse(cap);\n                    }\n                    catch (e) {\n                        adapter.log.warn(e);\n                        continue;\n                    }\n                    if (!shObjects.capabilityObjects[cap.namespace]) {\n                        if (shObjects.capabilityObjects[cap.namespace] !== null) {\n                            adapter.log.debug(`unsupported namespace \"${cap.namespace}\" for Smart-Home-Devices.${deviceEntityId}.${cap.name}`);\n                        }\n                        continue;\n                    }\n                    if (!shObjects.capabilityObjects[cap.namespace][cap.name]) {\n                        adapter.log.debug(`unsupported name \"${cap.namespace}.${cap.name}\" for Smart-Home-Devices.${deviceEntityId}.${cap.name}`);\n                        continue;\n                    }\n                    for (const obj of shObjects.capabilityObjects[cap.namespace][cap.name]) {\n                        if (typeof obj === 'string') { // Redirect!!\n                            capValues[obj] = handleObject(deviceEntityId, cap, obj);\n                            adapter.log.debug(`${cap.namespace}.${cap.value}: setValueFor=${obj} to ${capValues[obj]}`);\n                        }\n                        else {\n                            let name = obj.common.name;\n                            if (cap.instance) {\n                                name = `${cap.instance.replace(/\\./g, '-')}-${name}`;\n                            }\n                            let val = handleObject(deviceEntityId, cap, name);\n                            if (val === null && cap.instance && !adapterObjects[`Smart-Home-Devices.${deviceEntityId}.${name}`] && adapterObjects[`Smart-Home-Devices.${deviceEntityId}.${obj.common.name}`]) {\n                                // seems Amazon provided invalid details about cap.instance, try fallback\n                                val = handleObject(deviceEntityId, cap, obj.common.name);\n                                name = obj.common.name;\n                            }\n                            capValues[name] = val;\n                        }\n                    }\n                    if (cap.namespace === 'Alexa.ColorController') {\n                        if (capValues['color-hue'] !== null && capValues['color-saturation'] !== null && capValues['color-brightness'] !== null) {\n                            const colorRgb = hsvToRgb(capValues['color-hue'], capValues['color-saturation'], capValues['color-brightness']);\n                            adapter.setState(`Smart-Home-Devices.${deviceEntityId}.colorRgb`, colorRgb, true);\n                            shDeviceParamValues[`Smart-Home-Devices.${deviceEntityId}.colorRgb`] = colorRgb;\n                            capValues.colorRgb = colorRgb;\n                            colorDataIncluded = true;\n                        }\n                    }\n                }\n                if (colorDataIncluded && capValues.colorRgb && capValues.colorName === null) {\n                    const colorRgbSearch = hsvToRgb(capValues['color-hue'], capValues['color-saturation'], 1.0);\n                    const nearestColor = shObjects.nearestColor(colorRgbSearch);\n                    const native = adapterObjects[`Smart-Home-Devices.${deviceEntityId}.colorName`].native;\n                    const value = native.valueMap.indexOf(nearestColor.name);\n                    adapter.log.debug(`find nearest color for ${colorRgbSearch} (${capValues.colorRgb}): index=${value} / ${JSON.stringify(nearestColor)}`);\n                    if (value !== -1) {\n                        adapter.setState(`Smart-Home-Devices.${deviceEntityId}.colorName`, value, true);\n                        shDeviceParamValues[`Smart-Home-Devices.${deviceEntityId}.colorName`] = value;\n                        capValues.colorName = value;\n                    }\n                }\n            }\n        }\n    }\n    if (res.errors) {\n        for (const states of res.errors) {\n            if (!states.entity || !states.entity.entityId || !shApplianceEntityMap[states.entity.entityId]) continue;\n            const deviceEntityId = shApplianceEntityMap[states.entity.entityId].entityId;\n            if (states.code === 'ENDPOINT_UNREACHABLE' && adapterObjects[`Smart-Home-Devices.${deviceEntityId}.connectivity`]) {\n                adapter.setState(`Smart-Home-Devices.${deviceEntityId}.connectivity`, false, true);\n                adapter.log.debug(`Set Connectivity for ${deviceEntityId} to false because ${states.code}`);\n            }\n        }\n    }\n}\n\nfunction getCachedSmarthomeDevices(callback) {\n    const cachedDevicesFilename = path.join(__dirname, `cachedDevices.${adapter.namespace}.json`);\n\n    function getCachedDeviceList(ignoreTimestamp) {\n        try {\n            if (fs.existsSync(cachedDevicesFilename)) {\n                const stats = fs.statSync(cachedDevicesFilename);\n                if (ignoreTimestamp || stats.mtime.getTime() + 30 * 60 * 1000 > Date.now()) {\n                    const cachedDevices = JSON.parse(fs.readFileSync(cachedDevicesFilename, 'utf8'));\n                    adapter.log.info(`Using cached smart home devices list from ${stats.mtime}`);\n                    adapter.log.debug(JSON.stringify(cachedDevices));\n                    return cachedDevices;\n                }\n            }\n        } catch (err) {\n            adapter.log.info('Could not cache devices: ' + err.message);\n        }\n        return null;\n    }\n    const cachedDeviceList = getCachedDeviceList();\n    if (cachedDeviceList) {\n        callback(null, cachedDeviceList);\n        return;\n    }\n\n    alexa.getSmarthomeDevices((err, res) => {\n        if (!err && res && res.locationDetails && res.locationDetails.Default_Location) {\n            try {\n                fs.writeFileSync(cachedDevicesFilename, JSON.stringify(res));\n            } catch (err) {\n                adapter.log.info('Could not cache devices: ' + err.message);\n            }\n        }\n        if (err || !res || !res.locationDetails || !res.locationDetails.Default_Location) {\n            adapter.log.info(`Could not get smart home devices from Amazon: ${err ? err.message : 'no data'}`);\n            res = getCachedDeviceList(true);\n            if (res) {\n                err = null;\n                adapter.log.info('Could not get smart home devices from Amazon. Using cached list.');\n            }\n        }\n        callback(err, res);\n    });\n}\n\nfunction checkSmartHomeControlParameters(parameterId, value) {\n    let counter = 1;\n    const now = Date.now();\n    if (shDeviceParamControlValues[parameterId]) {\n        if (shDeviceParamControlValues[parameterId].ts > now - 300000) {\n            if (shDeviceParamControlValues[parameterId].value == value && typeof shDeviceParamControlValues[parameterId].value !== 'boolean') {\n                adapter.log.info(`Ignore update of ${parameterId} because it was set to the same value in last 5min`);\n                return false;\n            }\n            if (typeof shDeviceParamControlValues[parameterId].value === 'number') {\n                const difference = Math.abs(shDeviceParamControlValues[parameterId].value - value);\n                if (difference < 0.5) {\n                    adapter.log.info(`Ignore update of ${parameterId} because value only changed ${difference.toFixed(2)} since last set within 5min`);\n                    return false;\n                }\n            }\n        }\n        if (shDeviceParamControlValues[parameterId].ts > now - 600000) {\n            counter = ++shDeviceParamControlValues[parameterId].counter;\n            if (counter > 4) {\n                adapter.log.info(`Ignore update of ${parameterId} because it was set too often in the last time (${counter})`);\n                return false;\n            }\n        }\n    }\n    shDeviceParamControlValues[parameterId] = {\n        value,\n        ts: now,\n        counter\n    };\n    return true;\n}\n\nfunction createSmarthomeStates(callback) {\n    if (!adapter.config.synchronizeSmartHomeDevices) {\n        return callback && callback();\n    }\n\n    // TODO alexa.getSmarthomeGroups nutzen\n\n    adapter.getStates('Smart-Home-Devices.*.#includeInAllQuery', (err, states) => {\n        const shQueryEnabledEntities = {};\n        if (!err && states) {\n            Object.keys(states).forEach(id => {\n                if (!states[id]) return;\n                const entityId = id.replace(new RegExp(`^${adapter.namespace}.Smart-Home-Devices.`), '').replace(/\\.#includeInAllQuery$/, '');\n                shQueryEnabledEntities[entityId] = !!states[id].val;\n                adapter.log.debug(`${id} for ${entityId} is ${shQueryEnabledEntities[entityId] ? 'enabled' : 'disabled'}`);\n            });\n        }\n        alexa.getSmarthomeBehaviourActionDefinitions((err, resProperties) => {\n            if (stopped) return;\n            if (!err && resProperties) shObjects.patchProperties(resProperties);\n\n            getCachedSmarthomeDevices((err, res) => {\n                if (stopped) return;\n                setOrUpdateObject('Smart-Home-Devices', {type: 'folder', common: {name: 'Smart Home Devices'}});\n\n                setOrUpdateObject('Smart-Home-Devices.deleteAll', {common: { type: 'boolean', read: false, write: true, role: 'button'}}, false, (val) => {\n                    const cachedDevicesFilename = path.join(__dirname, `cachedDevices.${adapter.namespace}.json`);\n                    try {\n                        if (fs.existsSync(cachedDevicesFilename)) {\n                            fs.unlinkSync(cachedDevicesFilename);\n                        }\n                    } catch (err) {\n                        adapter.log.info('Could not delete cached devices: ' + err.message);\n                    }\n                    alexa.deleteAllSmarthomeDevices((err, res) => {\n                        adapter.delObject('Smart-Home-Devices', { recursive: true }, () => {\n                            setTimeout(createSmarthomeStates, 1000);\n                        });\n                    });\n                });\n                setOrUpdateObject('Smart-Home-Devices.discoverDevices', {common: {name: 'Let Alexa search for devices', type: 'boolean', read: false, write: true, role: 'button'}}, false, (val) => {\n                    if (shDeviceDiscoveryTimeout) {\n                        adapter.log.info('Discovery was executed too short ago, can not trigger it');\n                        return;\n                    }\n                    const cachedDevicesFilename = path.join(__dirname, `cachedDevices.${adapter.namespace}.json`);\n                    try {\n                        if (fs.existsSync(cachedDevicesFilename)) {\n                            fs.unlinkSync(cachedDevicesFilename);\n                        }\n                    } catch (err) {\n                        adapter.log.info('Could not delete cached devices: ' + err.message);\n                    }\n                    shDeviceDiscoveryTimeout = setTimeout(() => shDeviceDiscoveryTimeout = null, 300000);\n                    alexa.discoverSmarthomeDevice((err, res) => {\n                        return createSmarthomeStates();\n                    });\n                });\n\n                if (err || !res) {\n                    return processObjectQueue(() => callback && callback(err));\n                }\n\n                alexa.getSmarthomeEntities((err, res2) => {\n                    if (stopped) return;\n                    const behaviours = {};\n                    if (res2 && Array.isArray(res2)) {\n                        res2.forEach((behaviour) => {\n                            behaviours[behaviour.id] = behaviour;\n                        });\n                    }\n\n                    let readableCounter = 0;\n                    let all = {};\n                    if (\n                        res &&\n                        res.locationDetails &&\n                        res.locationDetails.Default_Location &&\n                        res.locationDetails.Default_Location.amazonBridgeDetails &&\n                        res.locationDetails.Default_Location.amazonBridgeDetails.amazonBridgeDetails\n                    ) {\n                        all = res.locationDetails.Default_Location.amazonBridgeDetails.amazonBridgeDetails;\n                    }\n                    for (const i of Object.keys(all)) {\n                        for (const n of Object.keys(all[i].applianceDetails.applianceDetails)) {\n                            const shDevice = all[i].applianceDetails.applianceDetails[n];\n                            if (!shDevice.isEnabled) continue;\n                            let friendlyName = shDevice.friendlyName;\n                            shApplianceEntityMap[shDevice.applianceId] = {\n                                entityId: shDevice.entityId,\n                                readable: false,\n                                cloudReadable: false,\n                                mergedApplianceIds: shDevice.mergedApplianceIds,\n                                supportedProperties: []\n                            };\n                            shEntityApplianceMap[shDevice.entityId] = shDevice.applianceId;\n                            if (shDevice.mergedApplianceIds && Array.isArray(shDevice.mergedApplianceIds)) {\n                                shDevice.mergedApplianceIds.forEach((applianceId) => {\n                                    shMergedApplianceIdMap[applianceId] = shDevice.applianceId;\n                                });\n                            }\n                            if (shDevice.aliases && shDevice.aliases[0] && shDevice.aliases[0].friendlyName) {\n                                friendlyName = shDevice.aliases[0].friendlyName;\n                            }\n                            setOrUpdateObject(`Smart-Home-Devices.${shDevice.entityId}`, {\n                                type: 'channel',\n                                common: {\n                                    name: friendlyName,\n                                    role: 'channel'\n                                },\n                                native: {\n                                    friendlyDescription: shDevice.friendlyDescription,\n                                    friendlyName: friendlyName,\n                                    modelName: shDevice.modelName,\n                                    additionalApplianceIds:  shDevice.additionalApplianceDetails.additionalApplianceDetails.ids || null,\n                                    object: n,\n                                    manufacturerName: shDevice.manufacturerName,\n                                }\n                            });\n                            setOrUpdateObject(`Smart-Home-Devices.${shDevice.entityId}.#enabled`, {common: {role: 'indicator', write: true}}, shDevice.isEnabled, function (val) {\n                                alexa.setEnablementForSmarthomeDevice(n, val);\n                            }.bind(alexa));\n                            setOrUpdateObject(`Smart-Home-Devices.${shDevice.entityId}.#delete`, {common: { type: 'boolean', read: false, write: true, role: 'button'}}, false, function (entityId, val) {\n                                alexa.deleteSmarthomeDevice(n);\n                                adapter.delObject(`Smart-Home-Devices.${entityId}`, { recursive: true});\n                            }.bind(alexa, shDevice.entityId));\n\n                            const excludeReadable = shDevice.manufacturerName.startsWith('ioBroker') || shDevice.manufacturerName.startsWith('openHAB');\n                            const deviceActions = {};\n                            if (behaviours[shDevice.entityId] && behaviours[shDevice.entityId].supportedOperations) {\n                                behaviours[shDevice.entityId].supportedOperations.forEach((a) => {\n                                    deviceActions[a] = true;\n                                });\n                            }\n                            if (shDevice.actions && shDevice.actions.length) {\n                                shDevice.actions.forEach((a) => {\n                                    deviceActions[a] = true;\n                                });\n                            }\n                            const applianceDriverIdentity = shDevice.applianceDriverIdentity ? (shDevice.applianceDriverIdentity.identifier || '') : '';\n                            let readableProperties = 0;\n                            let capabilitiesReadable = null;\n                            let capabilitiesCloudReadable = null;\n                            if (shDevice.capabilities.length) {\n                                for (const cap of shDevice.capabilities) {\n                                    if (cap.interfaceName) {\n                                        // For Scenes we add the action\n                                        if ((!cap.properties || !cap.properties.supported) && cap.interfaceName === 'Alexa.SceneController') {\n                                            cap.properties = {\n                                                'supported': [{\n                                                    'name': 'active'\n                                                }]\n                                            };\n                                        } else if (!cap.properties || !cap.properties.supported || !cap.properties.supported.length) {\n                                            continue;\n                                        } else if (!cap.properties.retrievable) {\n                                            capabilitiesReadable = capabilitiesReadable || false;\n                                            if (!(cap.properties.proactivelyReported && applianceDriverIdentity === 'SonarCloudService' && !!shDevice.connectedVia && !shDevice.applianceId.startsWith('SKILL_'))) {\n                                                continue;\n                                            }\n                                        }\n                                        capabilitiesCloudReadable = capabilitiesCloudReadable || cap.properties.proactivelyReported;\n                                        if (!shObjects.capabilityObjects[cap.interfaceName]) {\n                                            if (shObjects.capabilityObjects[cap.interfaceName] !== null) {\n                                                adapter.log.debug(`Smarthome-Device Capability ${cap.interfaceName} unknown. Report to developer this and next log line from logfile on disk!`);\n                                                adapter.log.debug(`${JSON.stringify(shDevice)} / ${JSON.stringify(behaviours[shDevice.entityId])}`);\n                                            }\n                                            continue;\n                                        }\n\n                                        for (const capProp of cap.properties.supported) {\n                                            if (!shObjects.capabilityObjects[cap.interfaceName][capProp.name]) {\n                                                adapter.log.debug(`Smarthome-Device Capability ${cap.interfaceName} for ${capProp.name} unknown. Report to developer this and next log line from logfile on disk!`);\n                                                adapter.log.debug(`${JSON.stringify(shDevice)} / ${JSON.stringify(behaviours[shDevice.entityId])}`);\n                                                continue;\n                                            }\n\n                                            let propertyToQuery = false;\n                                            for (let obj of shObjects.capabilityObjects[cap.interfaceName][capProp.name]) {\n                                                if (typeof obj === 'string') continue;\n                                                obj = JSON.parse(JSON.stringify(obj));\n                                                if (obj.experimental) {\n                                                    adapter.log.debug(`Smarthome-Device Capability ${cap.interfaceName} for ${capProp.name}.${obj.common.name} experimentally supported. Please check and report to developer this and next log line from logfile on disk if it works!!`);\n                                                    adapter.log.debug(`${JSON.stringify(shDevice)} / ${JSON.stringify(`${behaviours[shDevice.entityId]} / ${JSON.stringify(obj)}`)}`);\n                                                }\n                                                if (obj.experimental) delete obj.experimental;\n                                                if (obj.common && obj.common.read) {\n                                                    if (!excludeReadable && (cap.properties.retrievable || (!cap.properties.retrievable && cap.properties.proactivelyReported && applianceDriverIdentity === 'SonarCloudService' && !!shDevice.connectedVia && !shDevice.applianceId.startsWith('SKILL_'))) ) {\n                                                        readableProperties++;\n                                                        propertyToQuery = true;\n                                                    }\n                                                    else {\n                                                        obj.common.read = false;\n                                                    }\n                                                }\n\n                                                const origName = obj.common.name;\n                                                if (cap.instance) {\n                                                    if (!obj.native.paramName) {\n                                                        obj.native.paramName = obj.common.name;\n                                                    }\n                                                    obj.common.name = `${cap.instance.replace(/\\./g, '-')}-${obj.common.name}`;\n                                                }\n\n                                                if (cap.configuration) {\n                                                    obj = shObjects.configureCapabilityObject(cap, obj);\n                                                }\n\n                                                if (obj.native.supportedActions && obj.native.supportedActions.length) {\n                                                    obj.native.supportedActions.forEach((n) => {\n                                                        if (deviceActions[n] !== undefined) delete deviceActions[n];\n                                                        if (cap.instance && deviceActions[`${n}@${shDevice.entityId}_${cap.instance}`] !== undefined) delete deviceActions[`${n}@${shDevice.entityId}_${cap.instance}`];\n                                                    });\n                                                }\n\n                                                setOrUpdateObject(`Smart-Home-Devices.${shDevice.entityId}.${obj.common.name}`, obj, undefined, function (entityId, paramName, applianceId, value) {\n                                                    if (!obj.common.write) return;\n                                                    const parameters = buildSmartHomeControlParameters(entityId, shObjects.capabilityObjects[cap.interfaceName], capProp.name, paramName, value, cap.instance);\n                                                    let actionName = parameters.action;\n                                                    if (cap.instance) {\n                                                        if (actionName === 'turnOffToggle') {\n                                                            actionName = `turnOff@${shDevice.entityId}_${cap.instance}`;\n                                                        } else if (actionName === 'turnOnToggle') {\n                                                            actionName = `turnOn@${shDevice.entityId}_${cap.instance}`;\n                                                        } else {\n                                                            actionName = `${actionName}@${shDevice.entityId}_${cap.instance}`;\n                                                        }\n                                                    }\n                                                    if (!parameters.action || !behaviours[entityId] || ! behaviours[entityId].supportedOperations || !behaviours[entityId].supportedOperations.includes(actionName)) {\n                                                        if (parameters.action && !parameters.action.startsWith('turn') && !parameters.action.startsWith('scene')) {\n                                                            adapter.log.debug(`Invalid action ${parameters.action} provided for Capability ${cap.interfaceName} for ${obj.common.name}. Report to developer this and next log line from logfile on disk!`);\n                                                            adapter.log.debug(`${JSON.stringify(shDevice)} / ${JSON.stringify(behaviours[entityId])}`);\n                                                        }\n                                                        else {\n                                                            adapter.log.debug(`Action ${actionName} provided for Capability ${cap.interfaceName} for ${obj.common.name} is not supported, ignore`);\n                                                        }\n                                                        return;\n                                                    }\n\n                                                    if (!checkSmartHomeControlParameters(`${entityId}.${obj.common.name}`, value)) return;\n\n                                                    alexa.executeSmarthomeDeviceAction(entityId, parameters, (err, res) => {\n                                                        if (!err && res && res.controlResponses && res.controlResponses[0] && res.controlResponses[0].code && res.controlResponses[0].code === 'SUCCESS' && !excludeReadable) {\n                                                            if (shQueryBlocker[applianceId]) {\n                                                                clearTimeout(shQueryBlocker[applianceId]);\n                                                                shQueryBlocker[applianceId] = null;\n                                                            }\n                                                            if (shDeviceRefreshTimeoutAfterControl[applianceId]) {\n                                                                clearTimeout(shDeviceRefreshTimeoutAfterControl[applianceId]);\n                                                                shDeviceRefreshTimeoutAfterControl[applianceId] = null;\n                                                            }\n                                                            shDeviceRefreshTimeoutAfterControl[applianceId] = setTimeout(() => {\n                                                                shDeviceRefreshTimeoutAfterControl[applianceId] = null;\n\n                                                                if (shQueryBlocker[applianceId]) {\n                                                                    clearTimeout(shQueryBlocker[applianceId]);\n                                                                    shQueryBlocker[applianceId] = null;\n                                                                }\n                                                                /**\n                                                                 * Please do not change these delay, else Amazon might block the smart home device state query function\n                                                                 * for all >20k Adapter users!\n                                                                 */\n                                                                let delay = 900000;\n                                                                if (!applianceId.startsWith('SKILL_') || shApplianceEntityMap[applianceId].cloudReadable) delay = 600000;\n                                                                shQueryBlocker[applianceId] = setTimeout(() => {\n                                                                    shQueryBlocker[applianceId] = null;\n                                                                }, delay);\n\n                                                                alexa.querySmarthomeDevices(generateApplianceQueryArray(applianceId, true), (err, res) => {\n                                                                    if (!err) {\n                                                                        updateSmarthomeDeviceStates(res);\n                                                                    }\n                                                                });\n                                                            }, 2000);\n                                                        }\n                                                        else {\n                                                            updateSmarthomeDeviceStates(res);\n                                                        }\n                                                    });\n                                                }.bind(alexa, shDevice.entityId, origName, shDevice.applianceId));\n\n                                                if (shDevice.tags && shDevice.tags.tagNameToValueSetMap && shDevice.tags.tagNameToValueSetMap.groupIdentity) {\n                                                    for (const group of shDevice.tags.tagNameToValueSetMap.groupIdentity) {\n                                                        shGroupDetails[group] = shGroupDetails[group] || {\n                                                            applianceIds: {},\n                                                            entityIds: {},\n                                                            parameters: {}\n                                                        };\n                                                        shGroupDetails[group].applianceIds[shDevice.applianceId] = true;\n                                                        shGroupDetails[group].entityIds[shDevice.entityId] = true;\n                                                        shGroupDetails[group].parameters[obj.common.name] = obj;\n                                                    }\n                                                }\n                                            }\n                                            if (propertyToQuery) {\n                                                shApplianceEntityMap[shDevice.applianceId].supportedProperties.push({\n                                                    namespace: cap.interfaceName,\n                                                    name: capProp.name,\n                                                    instance: cap.instance // ???\n                                                });\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                            const deviceActionsArr = Object.keys(deviceActions);\n                            if (deviceActionsArr.length) {\n                                let readable = false;\n                                if (!excludeReadable) {\n                                    for (const action of deviceActionsArr) {\n                                        if (action.startsWith('get') || action.startsWith('retrieve')) {\n                                            readable = true;\n                                            readableProperties++;\n                                        }\n                                    }\n                                }\n\n                                let ignoreSecondTurnOnOff = false;\n                                for (let action of deviceActionsArr) {\n                                    if (ignoreSecondTurnOnOff && (action === 'turnOn' || action === 'turnOff')) continue;\n                                    if ((action === 'turnOn' && deviceActionsArr.includes('turnOff')) || (action === 'turnOff' && deviceActionsArr.includes('turnOn'))) {\n                                        action = 'turnOnOff';\n                                        ignoreSecondTurnOnOff = true;\n                                    }\n                                    if (!shObjects.actionObjects[action]) {\n                                        if (!action.includes('@')) {\n                                            adapter.log.debug(`Smarthome-Device Action ${action} unknown. Report to developer this and next log line from logfile on disk!`);\n                                            adapter.log.debug(`${JSON.stringify(shDevice)} / ${JSON.stringify(behaviours[shDevice.entityId])}`);\n                                        }\n                                        continue;\n                                    }\n                                    for (let obj of shObjects.actionObjects[action]) {\n                                        if (typeof obj === 'string') continue;\n                                        obj = JSON.parse(JSON.stringify(obj));\n                                        if (obj.experimental) {\n                                            adapter.log.debug(`Smarthome-Device Action ${action}.${obj.common.name} experimentally supported. Please check and report to developer this and next log line from logfile on disk if it works!!`);\n                                            adapter.log.debug(`${JSON.stringify(shDevice)} / ${JSON.stringify(behaviours[shDevice.entityId])} / ${JSON.stringify(obj)}`);\n                                        }\n                                        if (obj.experimental) delete obj.experimental;\n                                        if (obj.common && obj.common.read && excludeReadable) {\n                                            obj.common.read = false;\n                                        }\n                                        if (obj.native.supportedActions && obj.native.supportedActions.length) {\n                                            obj.native.supportedActions.forEach((n) => {\n                                                if (deviceActions[n] !== undefined) delete deviceActions[n];\n                                            });\n                                        }\n                                        if (\n                                            (action === 'turnOn') ||\n                                            (action === 'sceneActivate' && behaviours[shDevice.entityId] && behaviours[shDevice.entityId].supportedOperations && !behaviours[shDevice.entityId].supportedOperations.includes('sceneDeactivate'))\n                                        ) {\n                                            obj.common.role = 'button';\n                                            obj.common.read = false;\n                                        }\n                                        obj.native.readable = readable;\n\n                                        setOrUpdateObject(`Smart-Home-Devices.${shDevice.entityId}.${obj.common.name}`, obj, undefined, function (entityId, paramName, applianceId, value) {\n                                            if (!obj.common.write) return;\n                                            const origValue = value;\n                                            const parameters = buildSmartHomeControlParameters(shDevice.entityId, shObjects.actionObjects[action], null, paramName, value);\n\n                                            if (!behaviours[entityId] || !behaviours[entityId].supportedOperations || !behaviours[entityId].supportedOperations.includes(parameters.action)) {\n                                                if (!parameters.action.startsWith('turn') && !parameters.action.startsWith('scene')) {\n                                                    adapter.log.debug(`Invalid action ${parameters.action} provided for Action ${action}. Report to developer this and next log line from logfile on disk!`);\n                                                    adapter.log.debug(`${JSON.stringify(shDevice)} / ${JSON.stringify(behaviours[entityId])}`);\n                                                }\n                                                else {\n                                                    adapter.log.debug(`Action ${parameters.action} provided for Action ${action} for ${obj.common.name} is not supported, ignore`);\n                                                }\n                                                return;\n                                            }\n\n                                            if (!checkSmartHomeControlParameters(`${entityId}.${obj.common.name}`, value)) return;\n\n                                            alexa.executeSmarthomeDeviceAction(entityId, parameters, (err, res) => {\n                                                if (!err && res && res.controlResponses && res.controlResponses[0] && res.controlResponses[0].code && res.controlResponses[0].code === 'SUCCESS') {\n                                                    if (obj.native.readable) {\n                                                        if (shQueryBlocker[applianceId]) {\n                                                            clearTimeout(shQueryBlocker[applianceId]);\n                                                            shQueryBlocker[applianceId] = null;\n                                                        }\n                                                        if (shDeviceRefreshTimeoutAfterControl[applianceId]) {\n                                                            clearTimeout(shDeviceRefreshTimeoutAfterControl[applianceId]);\n                                                            shDeviceRefreshTimeoutAfterControl[applianceId] = null;\n                                                        }\n                                                        shDeviceRefreshTimeoutAfterControl[applianceId] = setTimeout(() => {\n                                                            shDeviceRefreshTimeoutAfterControl[applianceId] = null;\n\n                                                            if (shQueryBlocker[applianceId]) {\n                                                                clearTimeout(shQueryBlocker[applianceId]);\n                                                                shQueryBlocker[applianceId] = null;\n                                                            }\n                                                            /**\n                                                             * Please do not change these delay, else Amazon might block the smart home device state query function\n                                                             * for all >20k Adapter users!\n                                                             */\n                                                            let delay = 900000;\n                                                            if (!applianceId.startsWith('SKILL_') || shApplianceEntityMap[applianceId].cloudReadable) delay = 600000;\n                                                            shQueryBlocker[applianceId] = setTimeout(() => {\n                                                                shQueryBlocker[applianceId] = null;\n                                                            }, delay);\n\n                                                            alexa.querySmarthomeDevices(generateApplianceQueryArray(applianceId, true), (err, res) => {\n                                                                if (!err) {\n                                                                    updateSmarthomeDeviceStates(res);\n                                                                }\n                                                            });\n                                                        }, 2000);\n                                                    }\n                                                    else {\n                                                        adapter.setState(`Smart-Home-Devices.${shDevice.entityId}.${obj.common.name}`, origValue, true);\n                                                    }\n                                                }\n                                                else {\n                                                    updateSmarthomeDeviceStates(res);\n                                                }\n                                            });\n                                        }.bind(alexa, shDevice.entityId, obj.common.name, shDevice.applianceId));\n\n                                        if (shDevice.tags && shDevice.tags.tagNameToValueSetMap && shDevice.tags.tagNameToValueSetMap.groupIdentity) {\n                                            for (const group of shDevice.tags.tagNameToValueSetMap.groupIdentity) {\n                                                shGroupDetails[group] = shGroupDetails[group] || {\n                                                    applianceIds: {},\n                                                    entityIds: {},\n                                                    parameters: {}\n                                                };\n                                                shGroupDetails[group].applianceIds[shDevice.applianceId] = true;\n                                                shGroupDetails[group].entityIds[shDevice.entityId] = true;\n                                                shGroupDetails[group].parameters[obj.common.name] = obj;\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                            if (readableProperties > 0 && !excludeReadable) {\n                                shApplianceEntityMap[shDevice.applianceId].readable = true;\n                                shApplianceEntityMap[shDevice.applianceId].cloudReadable = capabilitiesCloudReadable;\n                                adapter.log.debug(`Smarthome-Device ${shDevice.entityId} is readable / cloud readable (${capabilitiesCloudReadable})`);\n                                readableCounter++;\n                                setOrUpdateObject(`Smart-Home-Devices.${shDevice.entityId}.#query`, {common: {type: 'boolean', read: false, write: true, role: 'button'}}, false, function (applianceId, value) {\n                                    if (shQueryBlocker[applianceId]) {\n                                        adapter.log.warn(`Smart Home device request blocked for ${applianceId}`);\n                                        return;\n                                    }\n                                    /**\n                                     * Please do not change these delay, else Amazon might block the smart home device state query function\n                                     * for all >20k Adapter users!\n                                     */\n                                    let delay = 900000;\n                                    if (!applianceId.startsWith('SKILL_') || shApplianceEntityMap[applianceId].cloudReadable) delay = 600000;\n                                    shQueryBlocker[applianceId] = setTimeout(() => {\n                                        shQueryBlocker[applianceId] = null;\n                                    }, delay);\n                                    alexa.querySmarthomeDevices(generateApplianceQueryArray(applianceId, true), (err, res) => {\n                                        if (!err) {\n                                            updateSmarthomeDeviceStates(res);\n                                        }\n                                    });\n                                }.bind(alexa, shDevice.applianceId));\n                                shQueryEnabled[shDevice.applianceId] = !!shQueryEnabledEntities[shDevice.entityId];\n                                adapter.log.debug(`Smarthome-Device ${shDevice.applianceId} is query enabled (${shQueryEnabled[shDevice.applianceId]})`);\n                                setOrUpdateObject(`Smart-Home-Devices.${shDevice.entityId}.#includeInAllQuery`, {common: {type: 'boolean', read: true, write: true, role: 'switch', def: false}}, undefined, function (applianceId, value) {\n                                    shQueryEnabled[applianceId] = !!value;\n                                    adapter.setState(`Smart-Home-Devices.${shDevice.entityId}.#includeInAllQuery`, !!value, true);\n                                }.bind(alexa, shDevice.applianceId));\n                            }\n                        }\n                    }\n                    let allGroups = {};\n                    if (\n                        res &&\n                        res.locationDetails &&\n                        res.locationDetails.Default_Location &&\n                        res.locationDetails.Default_Location.applianceGroups &&\n                        res.locationDetails.Default_Location.applianceGroups.applianceGroups\n                    ) {\n                        allGroups = res.locationDetails.Default_Location.applianceGroups.applianceGroups;\n                    }\n                    for (const i of Object.keys(allGroups)) {\n                        /*\n                        \"amzn1.HomeAutomation.ApplianceGroup.A3NSX4MMJVG96V.550a4bf5-4852-4a35-81c4-b3568b222033\": {\n                            \"applianceGroupName\": \"Lichter Küche\",\n                            \"applianceGroupIdentifier\": {\n                                \"value\": \"amzn1.HomeAutomation.ApplianceGroup.A3NSX4MMJVG96V.550a4bf5-4852-4a35-81c4-b3568b222033\"\n                            },\n                            \"spaceTypes\": [],\n                            \"children\": [],\n                            \"alexaEndpoints\": [],\n                            \"defaults\": []\n                        }\n                        */\n                        const groupData = allGroups[i];\n                        const friendlyName = groupData.applianceGroupName;\n                        const groupParamData = shGroupDetails[groupData.applianceGroupIdentifier.value];\n                        if (!groupParamData) continue;\n                        const groupIdShort = i.substr(i.lastIndexOf('.') + 1);\n                        setOrUpdateObject(`Smart-Home-Devices.${groupIdShort}`, {\n                            type: 'channel',\n                            common: {\n                                name: `Gruppe ${friendlyName}`,\n                                role: 'channel'\n                            },\n                            native: {\n                                friendlyName: friendlyName,\n                                ids:  groupData.applianceGroupIdentifier.value,\n                                object: groupData\n                            }\n                        });\n                        setOrUpdateObject(`Smart-Home-Devices.${groupIdShort}.#delete`, {common: { type: 'boolean', read: false, write: true, role: 'button'}}, false, function (entityId, val) {\n                            alexa.deleteSmarthomeGroup(entityId);\n                            adapter.delObject(`Smart-Home-Devices.${groupIdShort}`, { recursive: true});\n                        }.bind(alexa, i));\n\n                        for (const param of Object.keys(groupParamData.parameters)) {\n                            const obj = groupParamData.parameters[param];\n                            if (obj.native && obj.native.hideInGroups) continue;\n                            //obj.common.read = false;\n                            setOrUpdateObject(`Smart-Home-Devices.${groupIdShort}.${obj.common.name}`, obj, undefined, function (entityId, paramName, applianceId, value) {\n                                if (!obj.common.write) return;\n                                const parameters = buildSmartHomeControlParameters(entityId, obj, null, paramName, value);\n\n                                if (!behaviours[groupIdShort] || ! behaviours[groupIdShort].supportedOperations || !behaviours[groupIdShort].supportedOperations.includes(parameters.action)) {\n                                    adapter.log.debug(`Invalid action ${parameters.action} provided for Group-Action ${parameters.action}. Report to developer this and next log line from logfile on disk!`);\n                                    adapter.log.debug(`${JSON.stringify(groupData)} / ${JSON.stringify(behaviours[groupIdShort])}`);\n                                    return;\n                                }\n\n                                if (!checkSmartHomeControlParameters(`${groupIdShort}.${obj.common.name}`, value)) return;\n\n                                alexa.executeSmarthomeDeviceAction(groupIdShort, parameters, 'GROUP', (err, res) => {\n                                    if (!err && res && res.controlResponses && res.controlResponses[0] && res.controlResponses[0].code && res.controlResponses[0].code === 'SUCCESS') {\n                                        if (obj.native.readable) {\n                                            if (shQueryBlocker[applianceId]) {\n                                                clearTimeout(shQueryBlocker[applianceId]);\n                                                shQueryBlocker[applianceId] = null;\n                                            }\n                                            if (shDeviceRefreshTimeoutAfterControl[applianceId]) {\n                                                clearTimeout(shDeviceRefreshTimeoutAfterControl[applianceId]);\n                                                shDeviceRefreshTimeoutAfterControl[applianceId] = null;\n                                            }\n                                            shDeviceRefreshTimeoutAfterControl[applianceId] = setTimeout(() => {\n                                                shDeviceRefreshTimeoutAfterControl[applianceId] = null;\n\n                                                if (shQueryBlocker[applianceId]) {\n                                                    clearTimeout(shQueryBlocker[applianceId]);\n                                                    shQueryBlocker[applianceId] = null;\n                                                }\n                                                /**\n                                                 * Please do not change these delay, else Amazon might block the smart home device state query function\n                                                 * for all >20k Adapter users!\n                                                 */\n                                                let delay = 900000;\n                                                if (!applianceId.startsWith('SKILL_') || shApplianceEntityMap[applianceId].cloudReadable) delay = 600000;\n                                                shQueryBlocker[applianceId] = setTimeout(() => {\n                                                    shQueryBlocker[applianceId] = null;\n                                                }, delay);\n                                                alexa.querySmarthomeDevices(generateApplianceQueryArray(applianceId, true), (err, res) => {\n                                                    if (!err) {\n                                                        updateSmarthomeDeviceStates(res);\n                                                    }\n                                                });\n                                            }, 2000);\n                                        }\n                                    }\n                                    else {\n                                        updateSmarthomeDeviceStates(res);\n                                    }\n                                });\n                            }.bind(alexa, Object.keys(groupParamData.entityIds), obj.common.name, Object.keys(groupParamData.applianceIds)));\n\n                        }\n                    }\n                    if (readableCounter) {\n                        setOrUpdateObject('Smart-Home-Devices.queryAll', {common: { type: 'boolean', read: false, write: true, role: 'button'}}, false, (val) => {\n                            queryAllSmartHomeDevices(false, false);\n                        });\n                    }\n                    processObjectQueue(callback);\n                });\n            });\n        });\n    });\n}\n\nfunction scheduleHistoryUpdate(delay) {\n    if (delay === undefined) {\n        if (adapter.config.updateStateInterval === 0) return;\n        delay = adapter.config.updateHistoryInterval * 1000;\n    }\n    if (updateHistoryTimer) {\n        clearTimeout(updateHistoryTimer);\n    }\n    if (pushConnected) return;\n    if (stopped) return;\n    delay = Math.max(delay, 60000);\n    updateHistoryTimer = setTimeout(() => {\n        updateHistoryTimer = null;\n        if (stopped) return;\n        updateHistory();\n    }, delay);\n}\n\nfunction updateHistory(callback) {\n    if (updateHistoryTimer) {\n        clearTimeout(updateHistoryTimer);\n        updateHistoryTimer = null;\n    }\n    if (stopped) return;\n    alexa.getCustomerHistoryRecords({maxRecordSize: 3, filter: true, startTime: Date.now() - 60000}, (err, res) => {\n        if (stopped) return;\n        if (err || !res || !Array.isArray(res)) {\n            if (adapter.config.updateHistoryInterval > 0) {\n                scheduleHistoryUpdate();\n            }\n            return callback && callback();\n        }\n\n        adapter.getState('History.creationTime', (err, state) => {\n            if (err || !state) {\n                if (adapter.config.updateHistoryInterval > 0) {\n                    scheduleHistoryUpdate();\n                }\n                return callback && callback();\n            }\n\n            let last = state.val;\n            let i = res.length - 1;\n            (function doIt() {\n                if (i < 0) {\n                    if (adapter.config.updateHistoryInterval > 0) {\n                        scheduleHistoryUpdate();\n                    }\n                    return callback && callback();\n                }\n\n                const o = res[i--];\n                if (last >= o.creationTimestamp) return doIt();\n\n                updateHistoryStates(o);\n                last = o.creationTimestamp;\n\n                doIt();\n            })();\n        });\n    });\n}\n\n/*\nactivityCardContent - > activity.domainAttributes.card.content\nactivityCard - > JSON.stringify(activity.domainAttributes.card)\n*/\n\nfunction updateHistoryStates(o) {\n    if (adapter.config.historyIgnoreEmptySummary && !o.description.summary.length && !o.alexaResponse) {\n        adapter.log.debug(`Ignore History entry because configured: ${JSON.stringify(o)}`);\n        return;\n    }\n\n    adapter.setState('History.name', o.name, true);\n    adapter.setState('History.serialNumber', o.deviceSerialNumber, true);\n    adapter.setState('History.summary', o.description.summary || '', true);\n    o.activityStatus !== undefined && adapter.setState('History.status', o.activityStatus, true);\n    adapter.setState('History.creationTime', o.creationTimestamp, true);\n\n    const jsonHistory = {\n        name: o.name,\n        serialNumber: o.deviceSerialNumber,\n        summary: o.description.summary,\n        creationTime: o.creationTimestamp,\n        status: o.activityStatus,\n        domainApplicationId: '',\n        domainApplicationName: o.data.skillName,\n        cardContent: '',\n        card: '',\n        answerText: o.alexaResponse || '',\n        utteranceType: o.data.utteranceType,\n        domain: o.data.domain,\n        intent: o.data.intent\n    };\n\n    if (o.domainAttributes) { // deprecated\n        if (o.domainAttributes.applicationMetadata) {\n            adapter.setState('History.domainApplicationId', o.domainAttributes.applicationMetadata.applicationId || '', true);\n            jsonHistory.domainApplicationId = o.domainAttributes.applicationMetadata.applicationId || '';\n            adapter.setState('History.domainApplicationName', o.domainAttributes.applicationMetadata.applicationName || '', true);\n            jsonHistory.domainApplicationName = o.domainAttributes.applicationMetadata.applicationName;\n        }\n        else {\n            adapter.setState('History.domainApplicationId', '', true);\n            adapter.setState('History.domainApplicationName', '', true);\n        }\n        if (o.domainAttributes.card) {\n            adapter.setState('History.cardContent', o.domainAttributes.card.content || '', true);\n            jsonHistory.cardContent = o.domainAttributes.card.content || '';\n            adapter.setState('History.cardJson', JSON.stringify(o.domainAttributes.card), true);\n            jsonHistory.card = o.domainAttributes.card;\n        }\n        else {\n            adapter.setState('History.cardContent', '', true);\n            adapter.setState('History.cardJson', '', true);\n        }\n        if (o.domainAttributes.nBestList && o.domainAttributes.nBestList[0]) {\n            adapter.setState('History.answerText', o.domainAttributes.nBestList[0].answerText || o.domainAttributes.nBestList[0].text || '', true);\n            jsonHistory.answerText = o.domainAttributes.nBestList[0].answerText;\n        }\n        else {\n            adapter.setState('History.answerText', '', true);\n        }\n    }\n    else {\n        adapter.setState('History.domainApplicationId', jsonHistory.domainApplicationId, true);\n        adapter.setState('History.domainApplicationName', jsonHistory.domainApplicationName, true);\n        adapter.setState('History.cardContent', jsonHistory.cardContent, true);\n        adapter.setState('History.cardJson', JSON.stringify(jsonHistory.card), true);\n        adapter.setState('History.answerText', jsonHistory.answerText, true);\n        adapter.setState('History.utteranceType', jsonHistory.utteranceType, true);\n        adapter.setState('History.domain', jsonHistory.domain, true);\n        adapter.setState('History.intent', jsonHistory.intent, true);\n\n        if (jsonHistory.domain === 'Global' && jsonHistory.intent === 'StopIntent') {\n            if (lastPlayerState[jsonHistory.serialNumber] && lastPlayerState[jsonHistory.serialNumber].resPlayer && lastPlayerState[jsonHistory.serialNumber].resPlayer.state === 'PLAYING') {\n                schedulePlayerUpdate(jsonHistory.serialNumber, 5000, true);\n            }\n        } else if (jsonHistory.domain === 'Music') {\n            schedulePlayerUpdate(jsonHistory.serialNumber, 5000, true);\n        } else if (jsonHistory.domain === 'Notifications') {\n            scheduleNotificationUpdate(jsonHistory.deviceSerialNumber, 2000);\n        }\n    }\n    adapter.setState('History.json', JSON.stringify(jsonHistory), true);\n}\n\nfunction iterateMultiroom(device, commandCallback, doneCallback, counter) {\n    if (!device.isMultiroomDevice) {\n        return commandCallback(device, doneCallback);\n    }\n    if (counter === undefined) {\n        adapter.log.debug(`iterate START: ${JSON.stringify(device.clusterMembers)}`);\n        counter = 0;\n    }\n    if (counter >= device.clusterMembers.length) {\n        adapter.log.debug(`iterate done ${counter} vs. ${device.clusterMembers.length}`);\n        return doneCallback && doneCallback();\n    }\n    const currDevice = alexa.find(device.clusterMembers[counter]);\n    counter++;\n    if (!currDevice) {\n        adapter.log.debug(`iterate ${counter}: NOT FOUND`);\n        return iterateMultiroom(device, commandCallback, doneCallback, counter);\n    }\n    adapter.log.debug(`iterate ${counter}: ${currDevice.serialNumber}`);\n    return commandCallback(currDevice, () => iterateMultiroom(device, commandCallback, doneCallback, counter));\n}\n\nfunction createStatesForDevice(device, additionalDeviceData) {\n    return new Promise(resolve => {\n        const devId = `Echo-Devices.${device.serialNumber}`;\n\n        device.getNotificationSounds('Alarm', (err, res) => {\n            if (!err && res && res.notificationSounds && Array.isArray(res.notificationSounds)) {\n                device.alarmNotificationSounds = res.notificationSounds;\n                device.alarmNotificationSoundsStateList = {};\n                device.alarmNotificationSounds.forEach((n) => {\n                    let soundId = n.id;\n                    let displayName = n.displayName;\n                    if (n.providerId !== 'ECHO') {\n                        const folder = n.folder.replace(/^FOLDER_/, '');\n                        soundId = `${folder}-${n.id}`;\n                        displayName = `${n.providerId}: ${displayName}`;\n                        device.alarmNotificationSounds.push(Object.assign({}, n, {id: soundId, origId: n.id}));\n                    }\n                    device.alarmNotificationSoundsStateList[soundId] = displayName;\n                });\n            }\n\n            device.getNotificationSounds('Timer', (err, res) => {\n                if (!err && res && res.notificationSounds && Array.isArray(res.notificationSounds)) {\n                    device.timerNotificationSounds = res.notificationSounds;\n                    device.timerNotificationSoundsStateList = {};\n                    device.timerNotificationSounds.forEach((n) => {\n                        device.timerNotificationSoundsStateList[n.id] = n.displayName;\n                    });\n                }\n\n                device.getDeviceNotificationState((err, res) => {\n                    if (!err && res && res.volumeLevel !== undefined) {\n                        additionalDeviceData.notificationVolume = res.volumeLevel;\n                    }\n\n                    device.getDeviceNotificationDefaultSound('Alarm', (err, res) => {\n                        if (!err && res && res.defaultNotificationSound !== undefined) {\n                            additionalDeviceData.defaultAlarmNotificationSound = res.defaultNotificationSound;\n                        }\n\n                        device.getDeviceNotificationDefaultSound('Timer', (err, res) => {\n                            if (!err && res && res.defaultNotificationSound !== undefined) {\n                                additionalDeviceData.defaultTimerNotificationSound = res.defaultNotificationSound;\n                            }\n\n                            createDeviceStates(device, additionalDeviceData, () => {\n                                if (device.ignore) {\n                                    setTimeout(() => !stopped && resolve(), 300);\n                                }\n\n                                if (device.isControllable) {\n                                    playerDevices[device.serialNumber] = true;\n                                    setOrUpdateObject(`${devId}.Player`, {type: 'channel'});\n\n                                    setOrUpdateObject(`${devId}.Player.contentType`, {common: {role: 'text', write: false, def: ''}}); // 'LIVE_STATION' | 'TRACKS' | 'CUSTOM_STATION'\n                                    setOrUpdateObject(`${devId}.Player.currentState`, {common: {role: 'media.state', write: false, def: false}}); // 'PAUSED' | 'PLAYING'\n                                    setOrUpdateObject(`${devId}.Player.imageURL`, {common: {name: 'Huge image', role: 'media.cover.big', write: false, def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.providerId`, {common: {role: 'text', write: false, def: ''}}); // 'TUNE_IN' | 'CLOUD_PLAYER' | 'ROBIN'\n                                    setOrUpdateObject(`${devId}.Player.radioStationId`, {common: {role: 'text', write: false, def: ''}}); // 's24885' | null\n                                    setOrUpdateObject(`${devId}.Player.service`, {common: {role: 'text', write: false, def: ''}}); // 'TUNE_IN' | 'CLOUD_PLAYER' | 'PRIME_STATION'\n                                    setOrUpdateObject(`${devId}.Player.providerName`, {common: {name: 'active provider', role: 'media.input', write: false, def: ''}}); // 'Amazon Music' | 'TuneIn Live-Radio'\n\n                                    setOrUpdateObject(`${devId}.Player.currentTitle`, {common: {name:'current title', type:'string', role:'media.title', write: false, def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.currentArtist`, {common: {name:'current artist', type:'string', role:'media.artist', write: false, def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.currentAlbum`, {common: {name:'current album', type:'string', role:'media.album', write: false, def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.mainArtUrl`, {common: {name:'current main Art', type:'string', role:'media.cover', write: false, def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.miniArtUrl`, {common: {name:'current mini Art', type:'string', role:'media.cover.small', write: false, def: ''}});\n\n                                    setOrUpdateObject(`${devId}.Player.mediaId`, {common: {name:'current mediaId', type:'string', role:'media.playid', write: true, def: ''}}, function (device, value) {\n                                        alexa.sendCommand(device, 'jump', value);\n                                    }.bind(alexa, device));\n                                    setOrUpdateObject(`${devId}.Player.queueId`, {common: {name:'current queueId', type:'string', role:'text', write: false, def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.quality`, {common: {name:'current media quality', type:'string', role:'text', write: false, def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.qualityCodec`, {common: {name:'current media codec', type:'string', write: false, role:'text', def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.qualityDataRate`, {common: {name:'current media bitrate', type:'number', write: false, role:'media.bitrate', def: null, unit: 'kbps'}});\n                                    setOrUpdateObject(`${devId}.Player.qualitySampleRate`, {common: {name:'current media sample rate', type:'number', write: false, role:'value', def: null, unit: 'Hz'}});\n                                    setOrUpdateObject(`${devId}.Player.allowNext`, {common: {name:'allow action Next', type:'boolean', role:'indicator', write: false, def: false}});\n                                    setOrUpdateObject(`${devId}.Player.allowPlayPause`, {common: {name:'allow action Play/Pause', type:'boolean', role:'indicator', write: false, def: false}});\n                                    setOrUpdateObject(`${devId}.Player.allowPrevious`, {common: {name:'allow action Previous', type:'boolean', role:'indicator', write: false, def: false}});\n                                    setOrUpdateObject(`${devId}.Player.allowRepeat`, {common: {name:'allow action Repeat', type:'boolean', role:'indicator', write: false, def: false}});\n                                    setOrUpdateObject(`${devId}.Player.allowShuffle`, {common: {name:'allow action Shuffle', type:'boolean', role:'indicator', write: false, def: false}});\n                                    setOrUpdateObject(`${devId}.Player.playingInGroup`, {common: {name:'is Playing in Group?', type:'boolean', role:'indicator', write: false, def: false}});\n                                    setOrUpdateObject(`${devId}.Player.playingInGroupId`, {common: {name:'Current playing group ID', type:'string', role:'text', write: false, def: ''}});\n\n                                    setOrUpdateObject(`${devId}.Player.mediaLength`, {common: {name:'active media length', type:'number', role:'media.duration', def: 0}});\n                                    setOrUpdateObject(`${devId}.Player.mediaLengthStr`, {common: {name:'active media length as (HH:)MM:SS', type:'string', role:'media.duration.text', def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.mediaProgress`,  {common: {name:'active media progress', type:'number', role:'media.elapsed', def: 0}});\n                                    setOrUpdateObject(`${devId}.Player.mediaProgressStr`, {common: {name:'active media progress as (HH:)MM:SS', type:'string', role:'media.elapsed.text', def: ''}});\n                                    setOrUpdateObject(`${devId}.Player.mediaProgressPercent`, {common: {name:'active media progress as percent', type:'number', role:'media.elapsed.percent', def: 0}});\n                                    setOrUpdateObject(`${devId}.Player.mediaRemaining`,  {common: {name:'active media remaining time', type:'number', role:'value', def: 0}});\n                                    setOrUpdateObject(`${devId}.Player.mediaRemainingStr`, {common: {name:'active media remaining time as (HH:)MM:SS', type:'string', role:'value', def: ''}});\n\n                                    for (const c of Object.keys(playerControls)) {\n                                        const obj = JSON.parse (JSON.stringify (playerControls[c]));\n                                        setOrUpdateObject(`${devId}.Player.${c}`, {common: obj.common}, obj.val, alexa.sendCommand.bind(alexa, device, obj.command));\n                                    }\n\n                                    if (device.capabilities.includes('VOLUME_SETTING')) {\n                                        setOrUpdateObject(`${devId}.Player.muted`, {common: {type: 'boolean', role: 'media.mute', write: false, def: false}});\n                                        setOrUpdateObject(`${devId}.Player.volume`, {common: {type: 'number', role: 'level.volume', min: 0, max: 100, def: null}}, undefined, function (device, value) {\n                                            if (device.isMultiroomDevice) {\n                                                alexa.sendCommand(device, 'volume', value, (err, res) => {\n                                                    // on unavailability {\"message\":\"No routes found\",\"userFacingMessage\":null}\n                                                    if (res && res.message === 'No routes found') {\n                                                        const volumeCommands = [];\n                                                        iterateMultiroom(device, (iteratorDevice, nextCallback) => {\n                                                            volumeCommands.push({\n                                                                command: 'volume',\n                                                                value,\n                                                                device: iteratorDevice.serialNumber\n                                                            });\n                                                            nextCallback && nextCallback();\n                                                        }, () => {\n                                                            alexa.sendMultiSequenceCommand(device.serialNumber, volumeCommands, 'ParallelNode', alexa.ownerCustomerId);\n                                                        });\n                                                    }\n                                                });\n                                            }\n                                            else {\n                                                try {\n                                                    alexa.sendSequenceCommand(device, 'volume', value, alexa.ownerCustomerId);\n                                                } catch (err) {\n                                                    adapter.log.error(`${device.serialNumber} Error setting volume: ${err}`);\n                                                }\n                                            }\n                                        }.bind(alexa, device));\n                                    }\n\n                                    if (device.hasMusicPlayer) {\n                                        for (const c of Object.keys(musicControls)) {\n                                            const obj = JSON.parse (JSON.stringify (musicControls[c]));\n                                            setOrUpdateObject(`${devId}.Player.${c}`, {common: obj.common}, obj.val, alexa.sendCommand.bind(alexa, device, obj.command));\n                                        }\n                                        setOrUpdateObject(`${devId}.Music-Provider`, {type: 'channel'});\n                                        for (const p in musicProviders) {\n                                            if (musicProviders[p].availability !== 'AVAILABLE') continue;\n                                            if (!musicProviders[p].supportedOperations.includes('Alexa.Music.PlaySearchPhrase')) continue;\n                                            const displayName = musicProviders[p].displayName.replace(adapter.FORBIDDEN_CHARS, '-').replace(/\\./g, '_').replace(/ /g, '-');\n                                            if (!displayName.length) {\n                                                musicProviders[p].id !== 'DEFAULT' && adapter.log.warn(`Music Provider has no name, ignoring! (${JSON.stringify(musicProviders[p])})`);\n                                                continue;\n                                            }\n\n                                            setOrUpdateObject(`${devId}.Music-Provider.${displayName}`, {common: {name:`Phrase to play with ${musicProviders[p].displayName}`, type:'string', role:'text', def: ''}}, '', playMusicProvider.bind(alexa, device, musicProviders[p].id));\n                                            setOrUpdateObject(`${devId}.Music-Provider.${displayName}-Playlist`, {common: {name:`Playlist to play with ${musicProviders[p].displayName}`, type:'string', role:'text', def: ''}}, '', function(device, providerId, value) {\n                                                if (value === '') return;\n                                                playMusicProvider(device, providerId, `playlist ${value}`);\n                                            }.bind(alexa, device, musicProviders[p].id));\n                                        }\n                                    }\n\n                                    if (device.capabilities.includes ('TUNE_IN')) {\n                                        setOrUpdateObject(`${devId}.Player.TuneIn-Station`, {common: {role: 'text', def: ''}}, '', function (device, query) {\n                                            if (query.match(/^s[0-9]+$/)) {\n                                                device.setTunein(query, 'station', (err, ret) => {\n                                                    if (!err) {\n                                                        adapter.setState(`${devId}.Player.TuneIn-Station`, query, true);\n                                                        schedulePlayerUpdate(device, 5000);\n                                                    }\n                                                });\n                                                /*} else if (query.match(/^p[0-9]+$/)) {\n                                                    device.setTunein(query, 'show', (err, ret) => {\n                                                        if (!err) {\n                                                            adapter.setState(`${devId}.Player.TuneIn-Station`, query, true);\n                                                            schedulePlayerUpdate(device, 5000);\n                                                        }\n                                                    });*/\n                                            } else if (query.match(/^t[0-9]+$/)) {\n                                                device.setTunein(query, 'topic', (err, ret) => {\n                                                    if (!err) {\n                                                        adapter.setState(`${devId}.Player.TuneIn-Station`, query, true);\n                                                        schedulePlayerUpdate(device, 5000);\n                                                    }\n                                                });\n                                            } else {\n                                                alexa.tuneinSearch(query, (err, res) => {\n                                                    setRequestResult(err, res);\n                                                    if (err || !res || !Array.isArray (res.browseList)) return;\n                                                    const station = res.browseList[0];\n                                                    device.setTunein(station.id, station.contentType, (err, ret) => {\n                                                        if (!err) {\n                                                            adapter.setState(`Echo-Devices.${device.serialNumber}.Player.TuneIn-Station`, station.name, true);\n                                                            schedulePlayerUpdate(device, 5000);\n                                                        }\n                                                    });\n                                                });\n                                            }\n                                        }.bind(alexa, device));\n                                    }\n\n                                    if (device.capabilities.includes ('AUDIBLE')) {\n                                        setOrUpdateObject(`${devId}.Music-Provider.Audible`, {common: {role: 'text', def: ''}}, '', function (device, query) {\n                                            device.playAudible(query, (err, ret) => {\n                                                if (!err) {\n                                                    adapter.setState(`${devId}.Music-Provider.Audible`, query, true);\n                                                    schedulePlayerUpdate(device, 5000);\n                                                }\n                                            });\n                                        }.bind(alexa, device));\n                                    }\n                                }\n                                createBluetoothStates(device);\n\n                                if (device.notifications) {\n                                    createNotificationStates(device);\n                                }\n\n                                if (!device.isMultiroomDevice && device.deviceFamily === 'FIRE_TV') {\n                                    setOrUpdateObject(`${devId}.FireTVCommands`, {type: 'channel'});\n                                    for (const c of Object.keys(fireTVCommands)) {\n                                        const obj = JSON.parse (JSON.stringify(fireTVCommands[c]));\n                                        setOrUpdateObject(`${devId}.FireTVCommands.${c}`, {common: obj.common}, obj.val, function (device, command, value) {\n                                            command = fireTVCommands[command].command || command;\n                                            try {\n                                                alexa.sendSequenceCommand(device, command, value, alexa.ownerCustomerId);\n                                            } catch (err) {\n                                                adapter.log.error(`${device.serialNumber} Error sending command ${command} to FireTV: ${err}`);\n                                            }\n                                        }.bind(alexa, device, c));\n                                    }\n                                    /*setOrUpdateObject(`${devId}.FireTVCommands.playPhrase`, {common: {role: 'text', def: '', write: true, read: false}}, '', function (device, query) {\n                                        alexa.playFireTV(device, query, (err, ret) => {\n                                            if (!err) {\n                                                adapter.setState(`${devId}.FireTVCommands.playPhrase`, query, true);\n                                            }\n                                        });\n                                    }.bind(alexa, device));*/\n                                }\n\n                                if (device.deviceTypeDetails && device.deviceTypeDetails.commandSupport) {\n                                    setOrUpdateObject(`${devId}.Commands`, {type: 'channel'});\n                                    for (const c of Object.keys(commands)) {\n                                        if (c === 'notification' && device.isMultiroomDevice) continue;\n                                        const obj = JSON.parse (JSON.stringify (commands[c]));\n                                        if (c === 'sound' && routineSounds && Object.keys(routineSounds).length) {\n                                            obj.common.states = routineSounds;\n                                        }\n                                        if (c === 'skillYours' && routineSkills && Object.keys(routineSkills).length) {\n                                            obj.common.states = routineSkills;\n                                        }\n                                        setOrUpdateObject(`${devId}.Commands.${c}`, {common: obj.common}, obj.val, function (device, command, value) {\n                                            command = commands[command].command || command;\n                                            const commandsToExecute = [];\n                                            const speakVolumeCommands = [];\n                                            const speakVolumeResetCommands = [];\n\n                                            iterateMultiroom(device, (iteratorDevice, nextCallback) => {\n                                                if (command === 'notification' && value && typeof value === 'string' && value.includes(';')) {\n                                                    const parts = value.split(';');\n                                                    const title = parts.shift();\n                                                    value = {\n                                                        title,\n                                                        text: parts.join(';')\n                                                    };\n                                                }\n\n                                                if (command === 'deviceStop' || command === 'textCommand' || command.startsWith('skill')) {\n                                                    commandsToExecute.push({\n                                                        command,\n                                                        value,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                    return nextCallback && nextCallback();\n                                                }\n                                                const speakVolume = iteratorDevice.speakVolume;\n                                                adapter.getState(`Echo-Devices.${iteratorDevice.serialNumber}.Player.volume`, (err, state) => {\n                                                    let speakVolumeReset = 0;\n                                                    if (!err && state && state.val !== false && state.val !== null) {\n                                                        speakVolumeReset = state.val;\n                                                    }\n                                                    if (speakVolume && speakVolume > 0 && speakVolume !== speakVolumeReset) {\n                                                        speakVolumeCommands.push({\n                                                            command: 'volume',\n                                                            value: speakVolume,\n                                                            device: iteratorDevice.serialNumber\n                                                        });\n                                                    }\n                                                    commandsToExecute.push({\n                                                        command,\n                                                        value,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                    if (speakVolume && speakVolume > 0 && speakVolumeReset && speakVolumeReset > 0 && speakVolume !== speakVolumeReset) {\n                                                        speakVolumeResetCommands.push({\n                                                            command: 'volume',\n                                                            value: speakVolumeReset,\n                                                            device: iteratorDevice.serialNumber\n                                                        });\n                                                    }\n                                                    nextCallback && nextCallback();\n                                                });\n                                            }, () => {\n                                                if (!commandsToExecute.length) return;\n                                                if (command === 'deviceStop' || command === 'textCommand') {\n                                                    alexa.sendMultiSequenceCommand(device.serialNumber, commandsToExecute, 'ParallelNode', alexa.ownerCustomerId);\n                                                } else {\n                                                    const allCommands = [];\n                                                    speakVolumeCommands.length && allCommands.push({sequenceType: 'ParallelNode', nodes: speakVolumeCommands});\n                                                    allCommands.push({sequenceType: 'ParallelNode', nodes: commandsToExecute});\n                                                    speakVolumeResetCommands.length && allCommands.push({sequenceType: 'ParallelNode', nodes: speakVolumeResetCommands});\n                                                    alexa.sendMultiSequenceCommand(device.serialNumber, allCommands, null, alexa.ownerCustomerId);\n                                                }\n                                            });\n                                        }.bind(alexa, device, c));\n                                    }\n                                    setOrUpdateObject(`${devId}.Commands.speak`, {common: { role: 'media.tts', def: ''}}, '', function (device, value) {\n                                        if (typeof value !== 'string') return;\n                                        if (!value) return;\n\n                                        const speakVolumeCommands = [];\n                                        const speakCommands = [];\n                                        const speakVolumeResetCommands = [];\n\n                                        iterateMultiroom(device, (iteratorDevice, nextCallback) => {\n                                            let valueArr = value.match(/^(([^;0-9]+);)?(([0-9]{1,3});)?(.+)$/);\n                                            if (!valueArr) valueArr = [];\n                                            const speakVolume = valueArr[4] || iteratorDevice.speakVolume;\n                                            value = valueArr[5] || value;\n                                            if (!valueArr[4] && valueArr[1]) value = valueArr[1] + value;\n                                            adapter.getState(`Echo-Devices.${iteratorDevice.serialNumber}.Player.volume`, (err, state) => {\n                                                let speakVolumeReset = 0;\n                                                if (!err && state && state.val !== false && state.val !== null) {\n                                                    speakVolumeReset = state.val;\n                                                }\n                                                if (speakVolume && speakVolume > 0 && speakVolume !== speakVolumeReset) {\n                                                    speakVolumeCommands.push({\n                                                        command: 'volume',\n                                                        value: speakVolume,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                }\n                                                const speakCommandsFromValue = [];\n                                                value.split(';').forEach((v) => {\n                                                    const value = v.trim();\n                                                    if (!value || !value.length) return;\n                                                    speakCommandsFromValue.push({\n                                                        command: 'speak',\n                                                        value,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                });\n                                                speakCommands.push({sequenceType: 'SerialNode', nodes: speakCommandsFromValue});\n                                                if (speakVolume && speakVolume > 0 && speakVolumeReset && speakVolumeReset > 0 && speakVolume !== speakVolumeReset) {\n                                                    speakVolumeResetCommands.push({\n                                                        command: 'volume',\n                                                        value: speakVolumeReset,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                }\n                                                nextCallback && nextCallback();\n                                            });\n                                        }, () => {\n                                            // Done\n                                            if (!speakCommands.length) return;\n                                            const allCommands = [];\n                                            speakVolumeCommands.length && allCommands.push({sequenceType: 'ParallelNode', nodes: speakVolumeCommands});\n                                            allCommands.push({sequenceType: 'ParallelNode', nodes: speakCommands});\n                                            speakVolumeResetCommands.length && allCommands.push({sequenceType: 'ParallelNode', nodes: speakVolumeResetCommands});\n                                            alexa.sendMultiSequenceCommand(device.serialNumber, allCommands, null, alexa.ownerCustomerId);\n                                        });\n                                    }.bind(alexa, device));\n                                    setOrUpdateObject(`${devId}.Commands.announcement`, {common: { role: 'media.tts', def: ''}}, '', function (device, value) {\n                                        if (typeof value !== 'string') return;\n                                        if (!value) return;\n\n                                        const speakVolumeCommands = [];\n                                        const speakVolumeResetCommands = [];\n\n                                        let speakValue = '';\n                                        iterateMultiroom(device, (iteratorDevice, nextCallback) => {\n                                            let valueArr = value.match(/^(([^;0-9]+);)?(([0-9]{1,3});)?(.+)$/);\n                                            if (!valueArr) valueArr= [];\n                                            const speakVolume = valueArr[4] || iteratorDevice.speakVolume;\n                                            value = valueArr[5] || value;\n                                            if (!valueArr[4] && valueArr[1]) value = valueArr[1] + value;\n                                            adapter.getState(`Echo-Devices.${iteratorDevice.serialNumber}.Player.volume`, (err, state) => {\n                                                let speakVolumeReset = 0;\n                                                if (!err && state && state.val !== false && state.val !== null) {\n                                                    speakVolumeReset = state.val;\n                                                }\n                                                if (speakVolume && speakVolume > 0 && speakVolume !== speakVolumeReset) {\n                                                    speakVolumeCommands.push({\n                                                        command: 'volume',\n                                                        value: speakVolume,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                }\n                                                if (!speakValue) speakValue = value;\n                                                if (speakVolume && speakVolume > 0 && speakVolumeReset && speakVolumeReset > 0 && speakVolume !== speakVolumeReset) {\n                                                    speakVolumeResetCommands.push({\n                                                        command: 'volume',\n                                                        value: speakVolumeReset,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                }\n                                                nextCallback && nextCallback();\n                                            });\n                                        }, () => {\n                                            const announceCommands = [];\n                                            let volumeResetDelay = 3000;\n                                            speakValue.split(';').forEach((v) => {\n                                                const value = v.trim();\n                                                if (!value || !value.length) return;\n                                                announceCommands.push({command: 'announcement', value});\n                                                const lengthValue = value.replace(/<.+?>/g, ' ').replace(/\\s+/g, ' ').trim();\n                                                volumeResetDelay += lengthValue.length * 150;\n                                            });\n                                            if (!announceCommands.length) return;\n\n                                            const allCommands = [];\n                                            speakVolumeCommands.length && allCommands.push({sequenceType: 'ParallelNode', nodes: speakVolumeCommands});\n                                            if (announceCommands.length === 1) {\n                                                allCommands.push(announceCommands[0]);\n                                            } else {\n                                                allCommands.push({sequenceType: 'SerialNode', nodes: announceCommands});\n                                            }\n                                            alexa.sendMultiSequenceCommand((device.isMultiroomDevice && device.clusterMembers) ? device.clusterMembers: device, allCommands, null, alexa.ownerCustomerId, () => {\n                                                // Amazon requires us to send volume down separately for announcement\n                                                if (speakVolumeResetCommands.length) {\n                                                    setTimeout(() => {\n                                                        alexa.sendMultiSequenceCommand((device.isMultiroomDevice && device.clusterMembers) ? device.clusterMembers: device, speakVolumeResetCommands, 'ParallelNode', alexa.ownerCustomerId);\n                                                    },volumeResetDelay);\n                                                }\n                                            });\n                                        });\n                                    }.bind(alexa, device));\n                                    setOrUpdateObject(`${devId}.Commands.ssml`, {common: { role: 'media.tts', def: ''}}, '', function (device, value) {\n                                        if (typeof value !== 'string') return;\n                                        value = value.trim();\n                                        if (!value) return;\n\n                                        const speakVolumeCommands = [];\n                                        const speakVolumeResetCommands = [];\n\n                                        iterateMultiroom(device, (iteratorDevice, nextCallback) => {\n                                            const speakVolume = iteratorDevice.speakVolume;\n                                            adapter.getState(`Echo-Devices.${iteratorDevice.serialNumber}.Player.volume`, (err, state) => {\n                                                let speakVolumeReset = 0;\n                                                if (!err && state && state.val !== false && state.val !== null) {\n                                                    speakVolumeReset = state.val;\n                                                }\n                                                if (speakVolume && speakVolume > 0 && speakVolume !== speakVolumeReset) {\n                                                    speakVolumeCommands.push({\n                                                        command: 'volume',\n                                                        value: speakVolume,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                }\n                                                if (speakVolume && speakVolume > 0 && speakVolumeReset && speakVolumeReset > 0 && speakVolume !== speakVolumeReset) {\n                                                    speakVolumeResetCommands.push({\n                                                        command: 'volume',\n                                                        value: speakVolumeReset,\n                                                        device: iteratorDevice.serialNumber\n                                                    });\n                                                }\n                                                nextCallback && nextCallback();\n                                            });\n                                        }, () => {\n                                            const allCommands = [];\n                                            speakVolumeCommands.length && allCommands.push({sequenceType: 'ParallelNode', nodes: speakVolumeCommands});\n                                            allCommands.push({command: 'ssml', value});\n                                            speakVolumeResetCommands.length && allCommands.push({sequenceType: 'ParallelNode', nodes: speakVolumeResetCommands});\n                                            alexa.sendMultiSequenceCommand((device.isMultiroomDevice && device.clusterMembers) ? device.clusterMembers: device, allCommands, null, alexa.ownerCustomerId);\n\n                                        });\n                                    }.bind(alexa, device));\n                                    if (!device.isMultiroomDevice) {\n                                        if (existingStates[`${devId}.Commands.speak-volume`]) {\n                                            adapter.getState(`${devId}.Commands.speak-volume`, function (device, err, state) {\n                                                if (!err && state && state.val && state.val > 0) {\n                                                    device.speakVolume = state.val;\n                                                    adapter.log.debug(`Initialize speak-Volume for ${device.serialNumber}: ${state.val}`);\n                                                }\n                                            }.bind(alexa, device));\n                                        }\n                                        setOrUpdateObject(`${devId}.Commands.speak-volume`, {common: {name: 'Volume to use for speak commands', type: 'number', role: 'level.volume', min: 0, max: 100, def: null}}, undefined, function (device, value) {\n                                            if (typeof value !== 'number') {\n                                                value = parseInt(value, 10);\n                                            }\n                                            if (isNaN(value)) return;\n                                            device.speakVolume = value;\n                                            adapter.log.debug(`Set speak-Volume for ${device.serialNumber}: ${value}`);\n                                            adapter.setState(`${devId}.Commands.speak-volume`, value, true);\n                                        }.bind(alexa, device));\n                                    }\n                                }\n                                setOrUpdateObject(`${devId}.Commands.doNotDisturb`, {common: {type: 'mixed', role: 'state'}}, undefined, function (device, value) {\n\n                                    if (value === 'true') {\n                                        value = true;\n                                    } else if (value === 'false') {\n                                        value = false;\n                                    } else if (value === '') {\n                                        return;\n                                    } else if (typeof value === 'string' && isFinite(value)) {\n                                        value = parseInt(value, 10);\n                                    }\n\n                                    const deviceList = [];\n                                    iterateMultiroom(device, (iteratorDevice, nextCallback) => {\n                                        deviceList.push(iteratorDevice.serialNumber);\n                                        return nextCallback && nextCallback();\n                                    }, () => {\n                                        if (!deviceList.length) return;\n                                        alexa.sendSequenceCommand(deviceList, 'deviceDoNotDisturb', value, alexa.ownerCustomerId, (err, res) => {\n                                            if (err) {\n                                                adapter.log.error(`Error setting doNotDisturb for ${JSON.stringify(deviceList)}: ${err}`);\n                                            } else {\n                                                deviceList.forEach(serialNumber => {\n                                                    adapter.setState(`Echo-Devices.${serialNumber}.Commands.doNotDisturb`, !!value, true);\n                                                });\n                                            }\n                                        });\n                                    });\n                                }.bind(alexa, device));\n\n                                if (!device.isMultiroomDevice && device.deviceTypeDetails && device.deviceTypeDetails.commandSupport) {\n                                    if (automationRoutines) {\n                                        setOrUpdateObject(`${devId}.Routines`, {type: 'channel'});\n                                        for (const i of Object.keys(automationRoutines)) {\n                                            setOrUpdateObject(`${devId}.Routines.${automationRoutines[i].friendlyAutomationId}`, {common: { type: 'boolean', role: 'indicator', read: true, write: true, name: automationRoutines[i].friendlyName}}, false, alexa.executeAutomationRoutine.bind(alexa, device, automationRoutines[i]));\n                                            if (automationRoutines[i].utteranceWords) {\n                                                if (!routineTriggerUtterances[device.serialNumber]) routineTriggerUtterances[device.serialNumber] = {};\n                                                automationRoutines[i].utteranceWords.forEach(utterance => {\n                                                    if (!utterance) return;\n                                                    routineTriggerUtterances[device.serialNumber][utterance.toLowerCase()] = `${devId}.Routines.${automationRoutines[i].friendlyAutomationId}`;\n                                                });\n                                            }\n                                        }\n                                    }\n                                }\n\n                                setTimeout(() => !stopped && resolve(), 750);\n                            });\n                        });\n                    });\n                });\n            });\n        });\n    });\n}\n\nfunction createStates(callback) {\n\n    alexa.getAscendingAlarmState(async (err, alarmStates) => {\n        const ascendingAlarmStates = {};\n        if (!err && alarmStates && Array.isArray(alarmStates.ascendingAlarmModelList)) {\n            alarmStates.ascendingAlarmModelList.forEach(alarmState => {\n                if (alarmState.serialNumber) {\n                    ascendingAlarmStates[alarmState.deviceSerialNumber] = alarmState.ascendingAlarmEnabled;\n                }\n            });\n        }\n\n        setOrUpdateObject('requestResult', {common: {name: 'Request Result', write: false, role: 'text'}}, '');\n        setOrUpdateObject('Echo-Devices', {type: 'folder', common: {name: 'Echo devices'}});\n\n        for (const n of Object.keys(alexa.serialNumbers)) {\n            const device = alexa.serialNumbers[n];\n            const additionalDeviceData = {};\n            if (ascendingAlarmStates[device.serialNumber] !== undefined) {\n                additionalDeviceData.ascendingAlarmState = ascendingAlarmStates[device.serialNumber];\n            }\n\n            await createStatesForDevice(device, additionalDeviceData);\n        }\n\n        setOrUpdateObject('Echo-Devices.CommandsAll', {type: 'channel', common: {name: 'Commands to all devices'}});\n        for (const c of Object.keys(allDevicesCommands)) {\n            const obj = JSON.parse (JSON.stringify(allDevicesCommands[c]));\n            setOrUpdateObject(`Echo-Devices.CommandsAll.${c}`, {common: obj.common}, obj.val, function (command, value) {\n                command = allDevicesCommands[command].command || command;\n\n                if (command === 'deviceDoNotDisturbAll') {\n                    if (value === 'true') {\n                        value = true;\n                    } else if (value === 'false') {\n                        value = false;\n                    } else if (value === '') {\n                        return;\n                    } else if (typeof value === 'string' && isFinite(value)) {\n                        value = parseInt(value, 10);\n                    }\n                }\n                try {\n                    alexa.sendSequenceCommand('', command, value, alexa.ownerCustomerId, (err, res) => {\n                        if (!err && res && command === 'deviceDoNotDisturbAll') {\n                            updateConfigurationTimer && clearTimeout(updateConfigurationTimer);\n                            updateConfigurationTimer = setTimeout(() => {\n                                updateDeviceConfigurationStates();\n                            }, 3000);\n                        }\n                    });\n                } catch (err) {\n                    adapter.log.error(`Error sending command ${command} to all devices: ${err}`);\n                }\n            }.bind(alexa, c));\n        }\n\n\n        setOrUpdateObject('History', {type: 'channel', common: {name: 'Last detected commands and devices'}});\n        setOrUpdateObject('History.#trigger', {common: { type: 'boolean', read: false, write: true, role: 'button', name: 'Trigger/Rescan', desc: 'Set to true, to start a request'}}, false,\n            (val) => updateHistory());\n        setOrUpdateObject('History.name', {common: {role: 'text', write: false, name: 'Echo Device name', desc: 'Device name of the last detected command'}}, '');\n        let now = new Date();\n        now = now.getTime() - now.getTimezoneOffset();\n        setOrUpdateObject('History.creationTime', {common: {role: 'value.time'}}, now);\n        setOrUpdateObject('History.serialNumber', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.summary', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.status', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.domainApplicationId', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.domainApplicationName', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.cardContent', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.cardJson', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.utteranceType', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.answerText', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.json', {common: {type: 'string', role: 'json', write: false}}, '');\n        setOrUpdateObject('History.domain', {common: {role: 'text', write: false}}, '');\n        setOrUpdateObject('History.intent', {common: {role: 'text', write: false}}, '');\n\n        processObjectQueue(callback);\n    });\n}\n\nfunction playMusicProvider(device, providerId, value) {\n    if (value === '') return;\n    if (device.isMultiroomDevice && device.clusterMembers.length) {\n        value += ` auf ${device._name} `;\n        device = alexa.find(device.clusterMembers[0]);\n    }\n    alexa.playMusicProvider(device, providerId, value.trim(), (err, res) => {\n        schedulePlayerUpdate(device, 5000);\n    });\n}\n\nasync function createDeviceStates(serialOrName, additionalDeviceData, callback) {\n    additionalDeviceData = additionalDeviceData || {};\n    const device = alexa.find(serialOrName);\n    const devId = `Echo-Devices.${device.serialNumber}`;\n\n    let preserveSettings;\n    if (device.parentDeviceSerialNumber) {\n        // App device, add it but adjust name\n        preserveSettings = { common: ['name'] };\n        device._name += ` (${device.parentDeviceSerialNumber})`;\n    }\n    if (device.deviceType === 'A2IVLV5VM2W81') {\n        if (!device.parentDeviceSerialNumber || !adapter.config.includeAppDevices) {\n            // App main device, ignore them!\n            adapter.log.debug(`Ignore Device ${device.serialNumber} because is App-Type`);\n            device.ignore = true;\n            device.deviceTypeDetails = {name: 'App', commandSupport: false};\n\n            if (device.appDeviceList.length) {\n                device.appDeviceList.forEach((app) => {\n                    appDevices[app.serialNumber] = app;\n                    appDevices[app.serialNumber].ownerDevice = device.serialNumber;\n                });\n            }\n            return callback && callback();\n        }\n    }\n\n    let deviceTypeDetails = knownDeviceType[device.deviceType];\n    const commonDevice = {name: device._name};\n    if (!deviceTypeDetails) {\n        deviceTypeDetails =  {name: 'Unknown', commandSupport: true};\n        if (!unknownDeviceWarnings[device.deviceType]) {\n            adapter.log.info('Unknown Device, but enabling commands, Try it and report back if commands work.');\n            adapter.log.info('Report to developer as GitHub issue with details for device. Please grab full next line pot. from logfile on disk if cutted');\n            adapter.log.info(`    Device-type:${device.deviceType} - ${device._name} (${device.capabilities.join(',')})`);\n            unknownDeviceWarnings[device.deviceType] = true;\n        }\n    } else if (deviceTypeDetails.icon) {\n        commonDevice.icon = deviceTypeDetails.icon;\n    }\n    device.deviceTypeDetails = deviceTypeDetails;\n\n    setOrUpdateObject(devId, {type: 'device', common: commonDevice, preserveSettings});\n    setOrUpdateObject(`${devId}.online`, {common: {role: 'indicator.reachable', type: 'boolean'}}, device.online);\n    //setOrUpdateObject(devId + '.delete', {common: {name: 'Delete (Log out of this device)', role: 'button'}}, false); TODO\n\n    setOrUpdateObject(`${devId}.Info`, {type: 'channel'});\n    setOrUpdateObject(`${devId}.Info.capabilities`, {common: {role: 'text', write: false}}, device.capabilities.join (','));\n    setOrUpdateObject(`${devId}.Info.softwareVersion`, {common: {role: 'text', write: false}}, device.softwareVersion);\n    setOrUpdateObject(`${devId}.Info.isMultiroomDevice`, {common: {type: 'boolean', role: 'indicator', write: false}}, device.isMultiroomDevice);\n    if (device.isMultiroomDevice) {\n        setOrUpdateObject(`${devId}.Info.multiroomMembers`, {common: {role: 'text', write: false}}, device.clusterMembers.join (','));\n    }\n    setOrUpdateObject(`${devId}.Info.isMultiroomMember`, {common: {type: 'boolean', role: 'indicator', write: false}}, device.isMultiroomMember);\n    if (device.isMultiroomMember) {\n        setOrUpdateObject(`${devId}.Info.multiroomParents`, {common: {role: 'text', write: false}}, device.parentClusters.join (','));\n    }\n    setOrUpdateObject(`${devId}.Info.deviceType`, {common: {name: 'deviceType', type: 'string', role: 'text'}}, device.deviceType || '');\n\n    setOrUpdateObject(`${devId}.Info.deviceTypeString`, {common: {name: 'deviceType string', type: 'string', role: 'text'}}, deviceTypeDetails.name);\n    setOrUpdateObject(`${devId}.Info.serialNumber`, {common: {name: 'serialNumber', type: 'string', role: 'text'}}, device.serialNumber);\n    setOrUpdateObject(`${devId}.Info.name`, {common: {name: 'name', type: 'string', role: 'info.name'}}, device._name);\n    setOrUpdateObject(`${devId}.Info.deviceFamily`, {common: {name: 'Device Family', type: 'string', role: 'info'}}, device.deviceFamily);\n\n    let numPreferences = 0;\n    if (device.capabilities.includes('EARCONS') && device.preferences && device.preferences.notificationEarconEnabled !== undefined) {\n        numPreferences++;\n        adapter.log.debug(`${devId} notificationEarconEnabled: ${device.preferences.notificationEarconEnabled}`);\n        setOrUpdateObject(`${devId}.Preferences.ringNotificationsEnabled`, {\n            common: {\n                type: 'boolean',\n                role: 'indicator',\n                write: true\n            }\n        }, device.preferences.notificationEarconEnabled, function (device, value) {\n            device.getDevicePreferences((err, res) => {\n                if (err || !res) {\n                    adapter.log.error(`Error getting preferences for ${device.serialNumber}: ${err.message}`);\n                    return;\n                }\n                res.notificationEarconEnabled = !!value;\n                device.setDevicePreferences(res, (err, res) => {\n                    if (err || !res) {\n                        adapter.log.error(`Error setting preferences for ${device.serialNumber}: ${err.message}`);\n                        return;\n                    }\n                    device.preferences = res;\n                    adapter.setState(`${devId}.Preferences.ringNotificationsEnabled`, !!res.notificationEarconEnabled, true);\n                });\n            });\n        }.bind(alexa, device));\n    }\n\n    if (device.capabilities.includes('TIMERS_ALARMS_NOTIFICATIONS_VOLUME') && additionalDeviceData.notificationVolume !== undefined) {\n        numPreferences++;\n        adapter.log.debug(`${devId} notificationVolume: ${additionalDeviceData.notificationVolume}`);\n        setOrUpdateObject(`${devId}.Preferences.notificationVolume`, {\n            common: {\n                type: 'number',\n                role: 'level.volume',\n                min: 0,\n                max: 100,\n                write: true\n            }\n        }, additionalDeviceData.notificationVolume, function (device, value) {\n            value = parseInt(value, 10);\n            if (isNaN(value) || value < 0 || value > 100) {\n                adapter.log.error(`Invalid alarm volume ${value} for ${device.serialNumber}`);\n                return;\n            }\n            device.setDeviceNotificationVolume(value, (err, res) => {\n                if (err || !res) {\n                    adapter.log.error(`Error setting alarm volume to ${value} for ${device.serialNumber}: ${err.message}`);\n                    return;\n                }\n                adapter.setState(`${devId}.Preferences.notificationVolume`, value, true);\n            });\n        }.bind(alexa, device));\n    }\n\n    if (device.capabilities.includes('ASCENDING_ALARM_VOLUME') && additionalDeviceData.ascendingAlarmState !== undefined) {\n        numPreferences++;\n        adapter.log.debug(`${devId} ascendingAlarmState: ${additionalDeviceData.ascendingAlarmState}`);\n        setOrUpdateObject(`${devId}.Preferences.ascendingAlarmState`, {\n            common: {\n                type: 'boolean',\n                role: 'switch',\n                write: true\n            }\n        }, additionalDeviceData.ascendingAlarmState, function (device, value) {\n            value = !!value;\n            device.setDeviceAscendingAlarmState(value, (err, res) => {\n                if (err || !res) {\n                    adapter.log.error(`Error setting ascending alarm state to ${value} for ${device.serialNumber}: ${err.message}`);\n                    return;\n                }\n                adapter.setState(`${devId}.Preferences.ascendingAlarmState`, value, true);\n            });\n        }.bind(alexa, device));\n    }\n\n    if (additionalDeviceData.defaultAlarmNotificationSound !== undefined) {\n        numPreferences++;\n        adapter.log.debug(`${devId} defaultAlarmNotificationSound: ${additionalDeviceData.defaultAlarmNotificationSound.id}`);\n        setOrUpdateObject(`${devId}.Preferences.defaultAlarmNotificationSound`, {\n            common: {\n                type: 'string',\n                role: 'text',\n                write: true,\n                states: device.alarmNotificationSoundsStateList\n            }\n        }, additionalDeviceData.defaultAlarmNotificationSound.id, function (device, value) {\n            const soundDef = device.alarmNotificationSounds.find(s => s.id === value);\n            if (!soundDef) {\n                adapter.log.error(`Invalid alarm sound ${value} for ${device.serialNumber}`);\n                return;\n            }\n            device.setDeviceNotificationDefaultSound('Alarm', soundDef.id, (err, res) => {\n                if (err || !res) {\n                    adapter.log.error(`Error setting default notification sound to ${soundDef.id} for ${device.serialNumber}: ${err.message}`);\n                    return;\n                }\n                adapter.setState(`${devId}.Preferences.defaultAlarmNotificationSound`, soundDef.id, true);\n            });\n        }.bind(alexa, device));\n    }\n\n    if (additionalDeviceData.defaultTimerNotificationSound !== undefined) {\n        numPreferences++;\n        adapter.log.debug(`${devId} defaultTimerNotificationSound: ${additionalDeviceData.defaultTimerNotificationSound.id}`);\n        setOrUpdateObject(`${devId}.Preferences.defaultTimerNotificationSound`, {\n            common: {\n                type: 'string',\n                role: 'text',\n                write: true,\n                states: device.alarmNotificationSoundsStateList\n            }\n        }, additionalDeviceData.defaultTimerNotificationSound.id, function (device, value) {\n            const soundDef = device.alarmNotificationSounds.find(s => s.id === value);\n            if (!soundDef) {\n                adapter.log.error(`Invalid alarm sound ${value} for ${device.serialNumber}`);\n                return;\n            }\n            device.setDeviceNotificationDefaultSound('Timer', soundDef.id, (err, res) => {\n                if (err || !res) {\n                    adapter.log.error(`Error setting default notification sound to ${soundDef.id} for ${device.serialNumber}: ${err.message}`);\n                    return;\n                }\n                adapter.setState(`${devId}.Preferences.defaultTimerNotificationSound`, soundDef.id, true);\n            });\n        }.bind(alexa, device));\n    }\n\n\n    numPreferences += await initEqualizerData(device);\n    numPreferences += await initDeviceAuxController(device);\n    numPreferences += await initDisplaySettings(device);\n\n    if (numPreferences > 0) {\n        setOrUpdateObject(`${devId}.Preferences`, {type: 'channel'});\n    }\n\n    if (device.deviceType === 'A2IVLV5VM2W81' || device.serialNumber.length === 32) {\n        return processObjectQueue(callback);\n    }\n    alexa.getDeviceWifiDetails(device, (err, res) => {\n        if (!err && res && typeof res.macAddress === 'string' && res.macAddress.length) {\n            setOrUpdateObject(`${devId}.Info.macAddress`, {\n                common: {\n                    name: 'name',\n                    type: 'string',\n                    role: 'info.mac'\n                }\n            }, res.macAddress.toUpperCase().replace(/(.{2})/g, '$1:'));\n        }\n        if (!err && res && res.essid) {\n            setOrUpdateObject(`${devId}.Info.WifiSSID`, {\n                common: {\n                    name: 'name',\n                    type: 'string',\n                    role: 'text'\n                }\n            }, res.essid);\n        }\n        processObjectQueue(callback);\n    });\n}\n\nfunction updateDeviceStatus(serialOrName, callback) {\n    if (typeof serialOrName === 'function') {\n        callback = serialOrName;\n        serialOrName = null;\n    }\n    if (serialOrName) serialOrName = alexa.find(serialOrName);\n\n    alexa.initDeviceState(async () => {\n        for (const devId of Object.keys(alexa.serialNumbers)) {\n            const device = alexa.find(devId);\n            if (serialOrName && serialOrName !== device) return;\n\n            await createDeviceStates(device);\n        }\n        if (callback) {\n            callback();\n        }\n        else {\n            processObjectQueue();\n        }\n    });\n}\n\n\nfunction createBluetoothStates(serialOrName) {\n    const device = alexa.find(serialOrName);\n    const devId = `Echo-Devices.${device.serialNumber}`;\n\n    if (device.bluetoothState && !device.isMultiroomDevice && device.deviceTypeDetails && device.deviceTypeDetails.commandSupport) {\n        setOrUpdateObject(`${devId}.Bluetooth`, {type: 'device'});\n        device.bluetoothState.pairedDeviceList.forEach ((bt) => {\n            setOrUpdateObject(`${devId}.Bluetooth.${bt.address}`, {type: 'channel', common: {name: bt.friendlyName}});\n            setOrUpdateObject(`${devId}.Bluetooth.${bt.address}.connected`, {common: {role: 'switch'}}, bt.connected, bt.connect);\n            setOrUpdateObject(`${devId}.Bluetooth.${bt.address}.unpair`, {common: { type: 'boolean', read: false, write: true, role: 'button'}}, false, bt.unpaire);\n        });\n    }\n}\n\nfunction updateBluetoothStatus(serialOrName, callback) {\n    if (typeof serialOrName === 'function') {\n        callback = serialOrName;\n        serialOrName = null;\n    }\n    if (!alexa._options.bluetooth) {\n        return callback && callback();\n    }\n    if (serialOrName) serialOrName = alexa.find(serialOrName);\n\n    alexa.initBluetoothState(() => {\n        Object.keys(alexa.serialNumbers).forEach ((n) => {\n            const device = alexa.find(n);\n            if (serialOrName && serialOrName !== device) return;\n\n            createBluetoothStates(device);\n        });\n        if (callback) {\n            callback();\n        }\n        else {\n            processObjectQueue();\n        }\n    });\n}\n\nfunction createNotificationStates(serialOrName) {\n    const device = alexa.find(serialOrName);\n    const devId = `Echo-Devices.${device.serialNumber}`;\n\n    if (device.notifications) {\n        if (device.capabilities.includes('REMINDERS')) {\n            setOrUpdateObject(`${devId}.Reminder`, {type: 'device'});\n            setOrUpdateObject(`${devId}.Reminder.New`, {common: {type: 'mixed', role: 'state', name: 'Add new Reminder'}}, '', function(device, value) {\n                if (typeof value !== 'string') {\n                    adapter.log.error('Invalid value for new Reminder, please provide a string');\n                    return;\n                }\n                const valueArr = value.split(';');\n                let time = valueArr.shift().trim();\n                if (parseInt(time, 10) == time) time = parseInt(time, 10);\n                let label = valueArr.shift();\n                if (label) label = label.trim();\n                let sound = valueArr.shift();\n                let recurring = valueArr.shift();\n                if (sound) {\n                    sound = sound.trim();\n                    if (sound.startsWith('DAILY') || sound.startsWith('WEEKLY')) {\n                        recurring = sound;\n                        sound = undefined;\n                    }\n                    if (sound && device.alarmNotificationSounds) {\n                        sound = device.alarmNotificationSounds.find(s => s.id === sound);\n                    } else {\n                        sound = undefined;\n                    }\n                }\n                if (recurring) {\n                    recurring = recurring.trim();\n                    if (recurring === 'DAILY') {\n                        recurring = {\n                            freq: 'DAILY',\n                            byDay: [],\n                            interval: 1\n                        };\n                    } else if (recurring.startsWith('WEEKLY=')) {\n                        recurring = {\n                            freq: 'WEEKLY',\n                            byDay: recurring.substring(7).split(',').map(d => d.trim()).filter(d => d.length),\n                            interval: 1\n                        };\n                    } else if (recurring.startsWith('{') && recurring.endsWith('}')) {\n                        try {\n                            recurring = JSON.parse(recurring);\n                        } catch(err) {\n                            adapter.log.error(`Invalid recurring JSON value: ${recurring}`);\n                            recurring = undefined;\n                        }\n                    } else {\n                        recurring = undefined;\n                    }\n                }\n                if (!label) {\n                    adapter.log.error('Can not create new Reminder without label');\n                    return;\n                }\n                adapter.log.debug(`New Reminder: ${time} ${label} ${JSON.stringify(sound)} ${JSON.stringify(recurring)}`);\n                const notification = alexa.createNotificationObject(device, 'Reminder', label, time, true, sound, recurring);\n                if (notification) {\n                    alexa.createNotification(notification, (err, res) => {\n                        scheduleNotificationUpdate(device, 2000, true);\n                    });\n                }\n            }.bind(alexa, device));\n        }\n        if (device.capabilities.includes('TIMERS_AND_ALARMS')) {\n            setOrUpdateObject(`${devId}.Alarm`, {type: 'device'});\n            setOrUpdateObject(`${devId}.Alarm.New`, {common: {type: 'mixed', role: 'state', name: 'Add new Alarm'}}, '', function(device, value) {\n                if (typeof value !== 'string') {\n                    adapter.log.error('Invalid value for new Alarm, please provide a string');\n                    return;\n                }\n                const valueArr = value.split(';');\n                let time = valueArr.shift().trim();\n                if (parseInt(time, 10) == time) time = parseInt(time, 10);\n                let label = valueArr.shift();\n                if (label) label = label.trim();\n                let sound = valueArr.shift();\n                let recurring = valueArr.shift();\n                if (sound) {\n                    sound = sound.trim();\n                    if (sound.startsWith('DAILY') || sound.startsWith('WEEKLY') || (sound.startsWith('{') && sound.endsWith('}'))) {\n                        recurring = sound;\n                        sound = undefined;\n                    }\n                    if (sound && device.alarmNotificationSounds) {\n                        sound = device.alarmNotificationSounds.find(s => s.id === sound);\n                    } else {\n                        sound = undefined;\n                    }\n                }\n                if (recurring) {\n                    recurring = recurring.trim();\n                    if (recurring === 'DAILY') {\n                        recurring = {\n                            freq: 'DAILY',\n                            byDay: [],\n                            interval: 1\n                        };\n                    } else if (recurring.startsWith('WEEKLY=')) {\n                        recurring = {\n                            freq: 'WEEKLY',\n                            byDay: recurring.substring(7).split(',').map(d => d.trim()).filter(d => d.length),\n                            interval: 1\n                        };\n                    } else if (recurring.startsWith('{') && recurring.endsWith('}')) {\n                        try {\n                            recurring = JSON.parse(recurring);\n                        } catch(err) {\n                            adapter.log.error(`Invalid recurring JSON value: ${recurring}`);\n                            recurring = undefined;\n                        }\n                    } else {\n                        recurring = undefined;\n                    }\n                }\n                adapter.log.debug(`New Alarm: ${time} ${label} ${JSON.stringify(sound)} ${JSON.stringify(recurring)}`);\n                const notification = alexa.createNotificationObject(device, 'Alarm', label, time, true, sound, recurring);\n                if (notification) {\n                    alexa.createNotification(notification, (err, res) => {\n                        scheduleNotificationUpdate(device, 2000, true);\n                    });\n                }\n            }.bind(alexa, device));\n            setOrUpdateObject(`${devId}.Alarm.triggered`, {common: {type: 'string', read: true, write: false, role: 'value', name: 'Alarm ID that got Triggered'}}, null);\n            setOrUpdateObject(`${devId}.Reminder.triggered`, {common: {type: 'string', read: true, write: false, role: 'value', name: 'Reminder ID that got Triggered'}}, null);\n            setOrUpdateObject(`${devId}.Timer`, {type: 'device'});\n            setOrUpdateObject(`${devId}.Timer.triggered`, {common: {type: 'boolean', read: true, write: false, role: 'indicator', name: 'A timer got Triggered'}}, false);\n\n            if (rememberedNotificationVolumeRestoreCallbacks[device.serialNumber] !== undefined) {\n                adapter.log.debug(`Restoring notification volume for ${device.serialNumber} because triggered flag cleared`);\n                rememberedNotificationVolumeRestoreCallbacks[device.serialNumber]();\n                delete rememberedNotificationVolumeRestoreCallbacks[device.serialNumber];\n            }\n        }\n        let nextTimerObject = null;\n        const activeTimerList = [];\n        for (const noti of device.notifications) {\n            if (notificationTimer[noti.id]) {\n                clearTimeout(notificationTimer[noti.id]);\n                notificationTimer[noti.id] = null;\n            }\n            let isMusicAlarm = false;\n            if (noti.type === 'MusicAlarm') {\n                noti.type = 'Alarm';\n                isMusicAlarm = true;\n            }\n            if (noti.type === 'Reminder' && !device.capabilities.includes('REMINDERS')) continue;\n            if (noti.type === 'Alarm' && !device.capabilities.includes('TIMERS_AND_ALARMS')) continue;\n            if (noti.type === 'Timer' && noti.status === 'ON' && noti.remainingTime > 0) {\n                let remainingTime = noti.remainingTime;\n                if (noti.triggerTime) {\n                    remainingTime = noti.triggerTime - Date.now();\n                    noti.remainingTime = remainingTime;\n                }\n                adapter.log.debug(`${noti.type} ${noti.id} triggered in ${Math.floor(remainingTime / 1000)}s`);\n                if (remainingTime > 0) {\n                    if (nextTimerObject === null || nextTimerObject.remainingTime > remainingTime) {\n                        nextTimerObject = noti;\n                    }\n                    activeTimerList.push({\n                        id: noti.notificationIndex,\n                        triggerTime: noti.triggerTime || Date.now() + remainingTime,\n                        label: noti.timerLabel\n                    });\n                    if (notificationTimer[noti.id]) {\n                        clearTimeout(notificationTimer[noti.id]);\n                        notificationTimer[noti.id] = null;\n                    }\n                    if (remainingTime < 14 * 24 * 60 * 60 * 1000) { // not longer then 14 days in future\n                        notificationTimer[noti.id] = setTimeout(function (noti) {\n                            notificationTimer[noti.id] = null;\n                            adapter.log.debug(`${noti.type} ${noti.id} triggered`);\n                            adapter.setState(`${devId}.Timer.triggered`, true, true);\n\n                            // Check again status after 1min if no stop was arrived\n                            notificationTimer[noti.id] = setTimeout(() => {\n                                notificationTimer[noti.id] = null;\n                                adapter.getState(`${devId}.Timer.triggered`, (err, state) => {\n                                    if (!err && state && state.val) {\n                                        updateNotificationStates(device);\n                                    }\n                                });\n                            }, 60000);\n                        }.bind(alexa, noti), remainingTime);\n                    } else {\n                        notificationTimer[noti.id] = setTimeout(function (noti) {\n                            notificationTimer[noti.id] = null;\n                            updateNotificationStates(device);\n                        }.bind(alexa, noti), 14 * 24 * 60 * 60 * 1000);\n                    }\n                }\n            } else if (noti.type !== 'Timer') {\n                const id = noti.notificationIndex;\n                const notiId = `${devId}.${noti.type}.${id}`;\n                let displayName = noti.reminderLabel;\n                let time = noti.originalTime;\n                if (time) {\n                    if (typeof time === 'string' && time.endsWith('.000')) {\n                        time = time.substring(0, time.length - 4);\n                    }\n                    displayName = displayName ? `${displayName} ${time}`: time;\n                }\n                if (!displayName) {\n                    displayName = noti.type;\n                }\n\n                let rRuleDaysList = '';\n                if (noti.rRuleData && noti.rRuleData.byWeekDays) {\n                    rRuleDaysList = noti.rRuleData.byWeekDays.join(',');\n                } else if (noti.recurringPattern && noti.recurringPattern.endsWith('-WE')) {\n                    rRuleDaysList = 'SA,SU';\n                } else if (noti.recurringPattern && noti.recurringPattern.endsWith('-WD')) {\n                    rRuleDaysList = 'MO,TU,WE,TH,FR';\n                } else if (noti.recurringPattern && noti.recurringPattern === 'P1D') {\n                    rRuleDaysList = 'MO,TU,WE,TH,FR,SA,SU';\n                } else if (!noti.recurringPattern && noti.rRuleData && noti.rRuleData.recurrenceRules && noti.rRuleData.recurrenceRules.length && noti.rRuleData.recurrenceRules[0].startsWith('FREQ=DAILY;')) {\n                    rRuleDaysList = 'MO,TU,WE,TH,FR,SA,SU';\n                }\n\n                setOrUpdateObject(notiId, {type: 'channel', common: {name: displayName}});\n                setOrUpdateObject(`${notiId}.date`, {common: {type: 'mixed', read: true, write: true, role: 'state', name: `${displayName} Date`}}, noti.originalDate, value => {\n                    noti.set(value, (err, res) => {\n                        if (err || !res) {\n                            adapter.log.warn(`Error setting ${notiId}.date to ${value}: ${err.message}`);\n                        }\n                    });\n                });\n                setOrUpdateObject(`${notiId}.time`, {common: {type: 'mixed', role: 'state', name: `${displayName} Time`}}, time,  value => {\n                    noti.set(value, (err, res) => {\n                        if (err || !res) {\n                            adapter.log.warn(`Error setting ${notiId}.time to ${value}: ${err.message}`);\n                        }\n                    });\n                });\n                setOrUpdateObject(`${notiId}.enabled`, {common: {type: 'boolean', role: 'switch.enable', name: `${displayName} Enabled`}}, (noti.status === 'ON' || noti.status === 'SNOOZED'), value => {\n                    noti.set(!!value, (err, res) => {\n                        if (err | !res) {\n                            adapter.log.error(`Error setting ${notiId}.enabled to ${value}: ${err.message}`);\n                        }\n                    });\n                });\n                setOrUpdateObject(`${notiId}.snoozed`, {common: {type: 'boolean', role: 'indicator', name: `${displayName} Snoozed`, write: false}}, (noti.status === 'SNOOZED'));\n                setOrUpdateObject(`${notiId}.delete`, {common: {type: 'boolean', role: 'button', read: false, name: `${displayName} Delete`}}, false, (value) => {\n                    if (value) {\n                        noti.delete(err => {\n                            if (err) {\n                                adapter.log.error(`Error deleting ${noti.type} ${noti.id}: ${err.message}`);\n                            } else {\n                                if (notificationTimer[noti.id]) {\n                                    clearTimeout(notificationTimer[noti.id]);\n                                    notificationTimer[noti.id] = null;\n                                }\n                                if (notificationTimer[`${noti.id}-customVolume`]) {\n                                    clearTimeout(notificationTimer[`${noti.id}-customVolume`]);\n                                    notificationTimer[`${noti.id}-customVolume`] = null;\n                                }\n                                if (notificationTimer[`${noti.id}-customVolumeReset`]) {\n                                    clearTimeout(notificationTimer[`${noti.id}-customVolumeReset`]);\n                                    notificationTimer[`${noti.id}-customVolumeReset`] = null;\n                                    if (rememberedNotificationVolumeRestoreCallbacks[device.serialNumber]) {\n                                        const callback = rememberedNotificationVolumeRestoreCallbacks[device.serialNumber];\n                                        delete rememberedNotificationVolumeRestoreCallbacks[device.serialNumber];\n                                        callback();\n                                    }\n                                }\n                                if (adapterObjects[notiId]) {\n                                    deleteObject(notiId);\n                                }\n                            }\n                        });\n                    }\n                });\n                setOrUpdateObject(`${notiId}.cancel`, {common: {type: 'boolean', role: 'button', read: false, name: `${displayName} Delete`}}, false, (value) => {\n                    if (value) {\n                        noti.cancel((err, res) => {\n                            if (err | !res) {\n                                adapter.log.error(`Error setting ${notiId}.cancel: ${err.message}`);\n                            }\n                        });\n                    }\n                });\n                setOrUpdateObject(`${notiId}.musicProvider`, {common: {type: 'string', role: 'state', name: `${displayName} Music Provider`}}, noti.provider || null);\n                setOrUpdateObject(`${notiId}.musicEntity`, {common: {type: 'string', role: 'state', name: `${displayName} Music Entity`}}, noti.musicEntity || null);\n                if (device.capabilities.includes('TIMERS_ALARMS_NOTIFICATIONS_VOLUME') || device.capabilities.includes('CUSTOM_ALARM_TONE') || isMusicAlarm) {\n                    setOrUpdateObject(`${notiId}.customVolume`, {\n                        common: {\n                            type: 'number',\n                            role: 'level.volume',\n                            min: 0,\n                            max: 100,\n                            read: true,\n                            write: true,\n                            name: `${displayName} Custom Notification Volume`\n                        }\n                    }, undefined, value => {\n                        let customVolume = parseInt(value, 10);\n                        if (isNaN(customVolume) || customVolume < 0 || customVolume > 100) {\n                            adapter.log.error(`Invalid custom volume value ${value}`);\n                            customVolume = null;\n                        }\n                        adapter.setState(`${notiId}.customVolume`, customVolume, true);\n                    });\n                }\n                setOrUpdateObject(`${notiId}.triggered`, {common: {type: 'boolean', read: true, write: false, role: 'indicator', name: `${displayName} Triggered`}}, false);\n                setOrUpdateObject(`${notiId}.recurringPattern`, {common: {type: 'string', read: true, write: false, role: 'state', name: `${displayName} Recurring pattern`}}, noti.recurringPattern || '0');\n                if (noti.reminderLabel !== undefined && noti.reminderLabel !== null) {\n                    setOrUpdateObject(`${notiId}.label`, {common: {type: 'string', read: true, write: true, role: 'text', name: `${displayName} Label`}}, noti.reminderLabel, function(noti, value) {\n                        if (!value) {\n                            adapter.log.error(`Label is empty for ${noti.id}`);\n                            return;\n                        }\n                        value = value.toString();\n                        alexa.getNotifications((err, res) => {\n                            if (!err && res && res.notifications && Array.isArray(res.notifications)) {\n                                let notificationData = null;\n                                for (const n of res.notifications) {\n                                    if (n.id === noti.id) {\n                                        notificationData = n;\n                                        break;\n                                    }\n                                }\n                                if (!notificationData) {\n                                    adapter.log.error(`Cannot find notification ${noti.id}`);\n                                    return;\n                                }\n\n                                notificationData.reminderLabel = value;\n\n                                alexa.setNotification(notificationData, (err, res) => {\n                                    if (err) {\n                                        adapter.log.error(`Cannot set label ${value} for notification ${noti.id}: ${err.message}`);\n                                    } else {\n                                        adapter.setState(`${notiId}.label`, value, true);\n                                    }\n                                });\n                            } else {\n                                adapter.log.error(`Cannot get notification to set sound: ${err.message}`);\n                            }\n                        });\n                    }.bind(alexa, noti));\n                }\n                if (noti.sound !== undefined && noti.sound !== null && noti.sound.id !== undefined) {\n                    const soundId = isMusicAlarm ? `MUSIC-${noti.musicAlarmId}`: noti.sound.id;\n                    setOrUpdateObject(`${notiId}.sound`, {common: {type: 'string', read: true, write: true, role: 'state', name: `${displayName} Sound`, states: device.alarmNotificationSoundsStateList}}, soundId, function(noti, value) {\n                        alexa.getNotifications((err, res) => {\n                            if (!err && res && res.notifications && Array.isArray(res.notifications)) {\n                                let notificationData = null;\n                                for (const n of res.notifications) {\n                                    if (n.id === noti.id) {\n                                        notificationData = n;\n                                        break;\n                                    }\n                                }\n                                if (!notificationData) {\n                                    adapter.log.error(`Cannot find notification ${noti.id}`);\n                                    return;\n                                }\n\n                                let soundData = null;\n                                for (const s of device.alarmNotificationSounds) {\n                                    if (s.id === value) {\n                                        soundData = s;\n                                        break;\n                                    }\n                                }\n                                if (!soundData) {\n                                    adapter.log.error(`Cannot find sound ${value}`);\n                                    return;\n                                }\n                                if (soundData.providerId === 'ECHO') {\n                                    notificationData.sound = soundData;\n                                    notificationData.musicEntity = null;\n                                    notificationData.musicAlarmId = null;\n                                    notificationData.provider = null;\n                                } else {\n                                    notificationData.musicEntity = soundData.displayName;\n                                    notificationData.musicAlarmId = soundData.origId;\n                                    notificationData.provider = soundData.providerID;\n                                }\n\n                                if (notificationData.type === 'Reminder') {\n                                    alexa.setNotification(notificationData, (err, res) => {\n                                        if (err) {\n                                            adapter.log.error(`Cannot set sound ${value} for notification ${noti.id}: ${err.message}`);\n                                        } else {\n                                            adapter.setState(`${notiId}.sound`, value, true);\n                                        }\n                                    });\n                                } else {\n                                    const finalNotification = alexa.convertNotificationToV2(notificationData);\n                                    alexa.setNotificationV2(notificationData.notificationIndex, finalNotification, (err, res) => {\n                                        if (err) {\n                                            adapter.log.error(`Cannot set sound ${value} for notification ${noti.id}: ${err.message}`);\n                                        } else {\n                                            adapter.setState(`${notiId}.sound`, value, true);\n                                        }\n                                    });\n                                }\n                            } else {\n                                adapter.log.error(`Cannot get notification to set sound: ${err.message}`);\n                            }\n                        });\n                    }.bind(alexa, noti));\n                }\n                setOrUpdateObject(`${notiId}.recurringDays`, {common: {type: 'string', read: true, write: false, role: 'state', name: `${displayName} RRuleDay List`}}, rRuleDaysList);\n\n                let nextTriggerTime = null;\n\n                if (noti.status === 'ON') {\n                    adapter.log.debug(`Handle notification ${noti.type} ${noti.id} ${noti.status} ${noti.originalDate} ${noti.originalTime}`);\n                    const now = Date.now();\n                    if (noti.originalDate && noti.originalTime) {\n                        const alarmTime = new Date((`${noti.originalDate} ${noti.originalTime}`).replace(/-/g, '/')).getTime();\n                        if ((alarmTime - now) > 0) {\n                            adapter.log.debug(`${noti.type} ${noti.id} set from original Data ${noti.originalDate} ${noti.originalTime} -> (${alarmTime}) ${(alarmTime - now) / 1000}s`);\n                            nextTriggerTime = alarmTime;\n                        }\n                    }\n                    adapter.log.debug(`${noti.type} ${noti.id} ${nextTriggerTime}`);\n                    if (!nextTriggerTime && noti.rRuleData && noti.rRuleData.nextTriggerTimes && Array.isArray(noti.rRuleData.nextTriggerTimes) && noti.rRuleData.nextTriggerTimes.length) {\n\n                        noti.rRuleData.nextTriggerTimes.forEach(rule => {\n                            const alarmDate = new Date(rule);\n                            if (alarmDate) {\n                                const alarmTime = new Date(alarmDate).getTime();\n                                adapter.log.debug(`${noti.type} ${noti.id} Check next trigger time from data: ${alarmDate.toString()} -> ${alarmTime}`);\n                                if (alarmTime && (alarmTime - now) > 0 && (alarmTime < nextTriggerTime || !nextTriggerTime)) {\n                                    adapter.log.debug(`${noti.type} ${noti.id} Set from next trigger time from data: ${alarmDate.toString()} -> (${alarmTime}) ${(alarmTime - now) / 1000}s`);\n                                    nextTriggerTime = alarmTime;\n                                }\n                            }\n                        });\n                    }\n\n                    if (!nextTriggerTime && (noti.recurringPattern !== '0' || noti.rRuleData)) {\n                        adapter.log.debug(`${noti.type} ${noti.id} Try to calculate ourself ...`);\n                        let recurStartDate = noti.originalDate;\n                        let recurStartTime = time || '00:00:00';\n                        let recurEndDate = null;\n                        let recurEndTime = null;\n                        if (noti.rRuleData) {\n                            if (noti.rRuleData.recurStartDate) {\n                                recurStartDate = noti.rRuleData.recurStartDate;\n                            }\n                            if (noti.rRuleData.recurStartTime) {\n                                recurStartTime = noti.rRuleData.recurStartTime;\n                            }\n                            if (noti.rRuleData.recurEndDate) {\n                                recurEndDate = noti.rRuleData.recurEndDate;\n                            }\n                            if (noti.rRuleData.recurEndTime) {\n                                recurEndTime = noti.rRuleData.recurEndTime;\n                            }\n                        }\n\n                        const notificationTimes = noti.rRuleData && Array.isArray(noti.rRuleData.notificationTimes) && noti.rRuleData.notificationTimes || [];\n                        if (!notificationTimes.length && noti.originalTime) {\n                            notificationTimes.push(noti.originalTime);\n                        }\n                        notificationTimes.forEach((time, index) => {\n                            if (typeof time === 'string' && time.endsWith('.000')) {\n                                notificationTimes[index] = time.substring(0, time.length - 4);\n                            }\n                        });\n\n                        adapter.log.debug(`${noti.id} - ${recurStartDate} ${recurStartTime}`);\n                        if (recurStartDate && recurStartTime) {\n                            const recurringStartDate = new Date((`${recurStartDate} ${recurStartTime}`).replace(/-/g,'/'));\n                            const recurringEndDate = (recurEndDate && recurEndTime) ? new Date((`${recurEndDate} ${recurEndTime}`).replace(/-/g,'/')) : null;\n                            const ruleStrAdd = recurringEndDate ? `;UNTIL=${recurringEndDate.toISOString().replace(/[-:]/g, '').replace(/\\.[0-9]{3}Z$/, 'Z')}` : '';\n                            const ruleStrBase = `DTSTART:${recurringStartDate.toISOString().replace(/[-:]/g, '').replace(/\\.[0-9]{3}Z$/, 'Z')}\\nRRULE:`;\n\n                            // We use the provided rules, else we add one ones\n                            const recurRules = noti.rRuleData && noti.rRuleData.recurrenceRules || [];\n                            if (!recurRules.length) {\n                                if (noti.recurringPattern === 'P1D') {\n                                    recurRules.push(`FREQ=DAILY;INTERVAL=1`);\n                                } else if (rRuleDaysList.length) {\n                                    recurRules.push(`FREQ=WEEKLY;INTERVAL=1;BYDAY=${rRuleDaysList}`);\n                                }\n                            }\n\n                            const nowDate = new Date(now);\n                            //nowDate.setMinutes(nowDate.getMinutes() + nowDate.getTimezoneOffset());\n                            recurRules.forEach(rule => {\n                                if (rule.endsWith(';')) {\n                                    rule = rule.substring(0, rule.length - 1);\n                                }\n                                const ruleStr = ruleStrBase + rule + ruleStrAdd;\n\n                                adapter.log.debug(`${noti.type} ${noti.id} Check recurring pattern: ${ruleStr}`);\n                                let alarmDate;\n                                try {\n                                    alarmDate = rrulestr(ruleStr).after(nowDate, false);\n                                } catch (err) {\n                                    adapter.log.warn(`${noti.type} ${noti.id} Error while calculating recurring pattern: ${ruleStr}. Please report together with a debug log.`);\n                                }\n                                if (alarmDate) {\n                                    const alarmTimeDate = new Date(alarmDate);\n                                    // If there was an hour given in the RRule, we need to fix the Timezone\n                                    if (rule.includes(';BYHOUR=') || rule.startsWith('BYHOUR=')) {\n                                        alarmTimeDate.setMinutes(alarmTimeDate.getMinutes() + alarmTimeDate.getTimezoneOffset());\n                                    }\n                                    const alarmTime = alarmTimeDate.getTime();\n                                    adapter.log.debug(`${noti.type} ${noti.id} Check recurring pattern: ${ruleStr} -> ${alarmTimeDate} / ${alarmTime}`);\n                                    if (alarmTime && (alarmTime - now) > 0 && (alarmTime < nextTriggerTime || !nextTriggerTime)) {\n                                        nextTriggerTime = alarmTime;\n                                    }\n                                }\n                            });\n                        }\n                        adapter.log.debug(`${noti.type} ${noti.id} now = ${new Date(now).toString()} / ${now} / next date: ${new Date(nextTriggerTime).toString()} / ${nextTriggerTime}`);\n                    }\n\n                    const alarmDelay = (nextTriggerTime || 0) - now;\n                    adapter.log.debug(`${noti.type} ${noti.id} trigger expected in ${Math.floor(alarmDelay / 1000)}s at ${new Date(nextTriggerTime).toString()}`);\n                    if (alarmDelay > 0) {\n                        if (alarmDelay < 14 * 24 * 60 * 60 * 1000) { // not longer then 14 days in future\n                            if (notificationTimer[`${noti.id}-customVolume`]) {\n                                clearTimeout(notificationTimer[`${noti.id}-customVolume`]);\n                                notificationTimer[`${noti.id}-customVolume`] = null;\n                            }\n                            if (device.capabilities.includes('TIMERS_ALARMS_NOTIFICATIONS_VOLUME') || device.capabilities.includes('CUSTOM_ALARM_TONE') || isMusicAlarm) {\n                                notificationTimer[`${noti.id}-customVolume`] = setTimeout(function (notiId, noti) {\n                                    notificationTimer[`${noti.id}-customVolume`] = null;\n\n                                    adapter.getState(`${notiId}.customVolume`, (err, state) => {\n                                        if (err || !state || state.val === null) return;\n                                        const customVolume = parseInt(state.val, 10);\n                                        if (isNaN(customVolume) || customVolume < 0 || customVolume > 100) return;\n\n                                        if (isMusicAlarm) {\n                                            try {\n                                                alexa.sendSequenceCommand(device, 'volume', customVolume, alexa.ownerCustomerId);\n                                            } catch (err) {\n                                                adapter.log.error(`${noti.type} ${noti.id} Error while setting volume: ${err}`);\n                                            }\n                                            return;\n                                        }\n                                        device.getDeviceNotificationState((err, origNotificationState) => {\n                                            if (err || !origNotificationState || origNotificationState.volumeLevel === undefined) return;\n\n                                            if (adapterObjects[`Echo-Devices.${device.serialNumber}.Preferences.notificationVolume`]) {\n                                                adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.notificationVolume`, origNotificationState.volumeLevel, true);\n                                            }\n                                            device.setDeviceNotificationVolume(customVolume, err => {\n                                                if (err) return;\n                                                if (adapterObjects[`Echo-Devices.${device.serialNumber}.Preferences.notificationVolume`]) {\n                                                    adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.notificationVolume`, customVolume, true);\n                                                }\n                                                if (rememberedNotificationVolumeRestoreCallbacks[device.serialNumber] === undefined) {\n                                                    rememberedNotificationVolumeRestoreCallbacks[device.serialNumber] = function (notiId, noti, volumeToRestore) {\n                                                        if (notificationTimer[`${noti.id}-customVolumeReset`]) {\n                                                            clearTimeout(notificationTimer[`${noti.id}-customVolumeReset`]);\n                                                            notificationTimer[`${noti.id}-customVolumeReset`] = null;\n                                                        }\n                                                        device.setDeviceNotificationVolume(volumeToRestore, err => {\n                                                            if (err) return;\n                                                            if (adapterObjects[`Echo-Devices.${device.serialNumber}.Preferences.notificationVolume`]) {\n                                                                adapter.setState(`Echo-Devices.${device.serialNumber}.Preferences.notificationVolume`, volumeToRestore, true);\n                                                            }\n                                                            adapter.log.debug(`${noti.type} ${noti.id} - Reset notification volume level: ${volumeToRestore}`);\n                                                            delete rememberedNotificationVolumeRestoreCallbacks[device.serialNumber];\n                                                        });\n                                                    }.bind(alexa, notiId, noti, origNotificationState.volumeLevel);\n\n                                                    adapter.log.debug(`${noti.type} ${noti.id} - Remember notification volume level: ${origNotificationState.volumeLevel}`);\n                                                } else {\n                                                    adapter.log.debug(`${noti.type} ${noti.id} - Do not remember notification value because already set!`);\n                                                }\n\n                                                if (notificationTimer[`${noti.id}-customVolumeReset`]) {\n                                                    clearTimeout(notificationTimer[`${noti.id}-customVolumeReset`]);\n                                                    notificationTimer[`${noti.id}-customVolumeReset`] = null;\n                                                }\n                                                notificationTimer[`${noti.id}-customVolumeReset`] = setTimeout(() => {\n                                                    notificationTimer[`${noti.id}-customVolumeReset`] = null;\n                                                    if (rememberedNotificationVolumeRestoreCallbacks[device.serialNumber]) {\n                                                        rememberedNotificationVolumeRestoreCallbacks[device.serialNumber](notiId, noti, origNotificationState.volumeLevel);\n                                                    }\n                                                }, 120000);\n\n                                            });\n                                        });\n                                    });\n\n                                    // Check again status after 1min if no stop was arrived\n                                    notificationTimer[noti.id] = setTimeout(() => {\n                                        notificationTimer[noti.id] = null;\n                                        adapter.getState(`${notiId}.triggered`, (err, state) => {\n                                            if (!err && state && state.val) {\n                                                updateNotificationStates(device);\n                                            }\n                                        });\n                                    }, 60000);\n                                }.bind(alexa, notiId, noti), alarmDelay > 2000 ? alarmDelay - 2000 : 0);\n                            }\n\n                            if (notificationTimer[noti.id]) {\n                                clearTimeout(notificationTimer[noti.id]);\n                                notificationTimer[noti.id] = null;\n                            }\n                            notificationTimer[noti.id] = setTimeout(function (notiId, noti) {\n                                notificationTimer[noti.id] = null;\n                                adapter.log.debug(`${noti.type} ${noti.id} triggered`);\n                                adapter.setState(`${notiId}.triggered`, true, true);\n                                if (noti.type === 'Alarm') {\n                                    adapter.setState(`${devId}.Alarm.triggered`, noti.notificationIndex, true);\n                                } else if (noti.type === 'Reminder') {\n                                    adapter.setState(`${devId}.Reminder.triggered`, noti.notificationIndex, true);\n                                }\n                                // Check again status after 1min if no stop was arrived\n                                notificationTimer[noti.id] = setTimeout(() => {\n                                    notificationTimer[noti.id] = null;\n                                    adapter.getState(`${notiId}.triggered`, (err, state) => {\n                                        if (!err && state && state.val) {\n                                            updateNotificationStates(device);\n                                        }\n                                    });\n                                }, 60000);\n                            }.bind(alexa, notiId, noti), alarmDelay);\n                        } else {\n                            notificationTimer[noti.id] = setTimeout(function (noti) {\n                                notificationTimer[noti.id] = null;\n                                updateNotificationStates(device);\n                            }.bind(alexa, noti), 14 * 24 * 60 * 60 * 1000);\n                        }\n                    }\n                }\n                setOrUpdateObject(`${notiId}.nextTriggerDate`, {common: {type: 'number', role: 'date', name: noti.reminderLabel ? noti.reminderLabel : `${displayName} Trigger Timestamp`}}, nextTriggerTime);\n            }\n        }\n        setOrUpdateObject(`${devId}.Timer.nextTimerDate`, {common: {type: 'number', role: 'date', name: 'Unix epoch timestamp for next timer'}}, nextTimerObject ? (nextTimerObject.triggerTime || (Date.now() + nextTimerObject.remainingTime)) : 0, nextTimerObject ? nextTimerObject.set : null);\n        setOrUpdateObject(`${devId}.Timer.activeTimerList`, {common: {type: 'string', role: 'array', name: 'Array of the active timers'}}, JSON.stringify(activeTimerList));\n        setOrUpdateObject(`${devId}.Timer.nextTimerId`, {common: {type: 'string', role: 'text', name: 'ID of the next triggered timer'}}, nextTimerObject ? nextTimerObject.notificationIndex : null);\n        setOrUpdateObject(`${devId}.Timer.stopTimerId`, {common: {type: 'string', role: 'text', name: 'ID of the timer to be stopped'}}, '', function(value) {\n            if (!value) {\n                return;\n            }\n            value = value.toString();\n            const noti = device.notifications.find(n => n.notificationIndex === value);\n\n            if (!noti) {\n                adapter.log.error(`${devId} - Cannot stop timer ${value} - not found`);\n                return;\n            }\n            noti.delete(err => {\n                if (err) {\n                    adapter.log.error(`${devId} - Cannot stop timer ${value} - ${err.message}`);\n                } else {\n                    adapter.setState(`${devId}.Timer.stopTimerId`, '', true);\n                }\n            });\n        }.bind(alexa) );\n    }\n}\n\nfunction updateNotificationStates(serialOrName, callback) {\n    if (typeof serialOrName === 'function') {\n        callback = serialOrName;\n        serialOrName = null;\n    }\n    if (!alexa._options.notifications) return callback && callback();\n    if (serialOrName) serialOrName = alexa.find(serialOrName);\n\n    alexa.initNotifications(() => {\n        Object.keys(alexa.serialNumbers).forEach ((n) => {\n            const device = alexa.find(n);\n            if ((serialOrName && serialOrName !== device) || (device && device.ignore)) return;\n\n            createNotificationStates(device);\n        });\n        if (callback) {\n            callback();\n        }\n        else {\n            processObjectQueue();\n        }\n    });\n}\n\nfunction updatePlayerStatus(serialOrName, callback) {\n    if (typeof serialOrName === 'function') {\n        callback = serialOrName;\n        serialOrName = null;\n    }\n    let serials;\n    if (serialOrName) {\n        serialOrName = alexa.find(serialOrName);\n        if (!serialOrName) return callback && callback();\n        serials = [serialOrName.serialNumber];\n    }\n    else {\n        serials = Object.keys(playerDevices);\n    }\n\n    let i = 0;\n    (function doIt() {\n        if (i >= serials.length) {\n            if (adapter.config.updateStateInterval > 0) {\n                scheduleStatesUpdate();\n            }\n            return processObjectQueue(callback);\n        }\n        const device = alexa.find(serials[i++]);\n        if (! device || !device.isControllable) return doIt();\n        const devId = `Echo-Devices.${device.serialNumber}`;\n\n        alexa.getPlayerInfo(device , (err, resPlayer) => {\n            if (stopped) return;\n            if (err || !resPlayer || !resPlayer.playerInfo) {\n                const lastKnownPlayerState = ((lastPlayerState[device.serialNumber] && lastPlayerState[device.serialNumber].resPlayer && lastPlayerState[device.serialNumber].resPlayer.playerInfo) ? lastPlayerState[device.serialNumber].resPlayer.playerInfo.state : 'UNKNOWN') || 'UNKNOWN';\n                // no player info means no player active\n                const devId = `Echo-Devices.${device.serialNumber}`;\n                adapter.setState(`${devId}.Player.controlPause`, lastKnownPlayerState === 'PAUSED' || (lastKnownPlayerState !== 'PLAYING' && lastKnownPlayerState !== 'FINISHED'), true);\n                adapter.setState(`${devId}.Player.controlPlay`, lastKnownPlayerState === 'PLAYING', true);\n                playingDevices[device.serialNumber] = lastKnownPlayerState === 'PLAYING';\n                adapter.setState(`${devId}.Player.currentState`, lastKnownPlayerState === 'PLAYING', true);\n\n                if ((lastKnownPlayerState !== 'PLAYING' && lastPlayerState[device.serialNumber] && lastPlayerState[device.serialNumber].resPlayer && lastPlayerState[device.serialNumber].resPlayer.playerInfo && lastPlayerState[device.serialNumber].resPlayer.playerInfo.isPlayingInLemur) && device.isMultiroomDevice && device.clusterMembers && playingDevices[device.serialNumber]) { // Played before but now no longer playing in group\n                    device.clusterMembers.forEach(member => {\n                        if (playingDevices[member]) {\n                            return; // Device is updated itself while playing\n                        }\n                        const memberDevice = alexa.find(member);\n                        if (memberDevice && memberDevice.isControllable) {\n                            adapter.setState(`Echo-Devices.${memberDevice.serialNumber}.Player.playingInGroup`, false, true);\n                            adapter.setState(`Echo-Devices.${memberDevice.serialNumber}.Player.playingInGroupId`, null, true);\n                        }\n                    });\n                }\n\n                if (initialDeviceVolumes[device.serialNumber] && device.capabilities.includes('VOLUME_SETTING')) {\n                    let volume = initialDeviceVolumes[device.serialNumber].speakerVolume;\n                    const muted = initialDeviceVolumes[device.serialNumber].speakerMuted;\n                    if (muted !== null) adapter.setState(`${devId}.Player.muted`, muted, true);\n                    if (volume === 0 && device.isMultiroomDevice) volume = null;\n                    if (volume !== null) adapter.setState(`${devId}.Player.volume`, volume, true);\n                    adapter.log.debug(`Initialize Volume for Device ${device.serialNumber}: volume=${volume} (muted=${muted})`);\n                    delete initialDeviceVolumes[device.serialNumber];\n                }\n                return setTimeout(doIt, Math.floor(Math.random() * 2000) + 500);\n            }\n\n            const playerData = {\n                providerName: '',\n                providerId: '',\n                radioStationId: '',\n                service: '',\n                contentType: '',\n                controlShuffle: null,\n                controlRepeat: null,\n                imageURL: '',\n                muted: null,\n                volume: null,\n                controlPause: false,\n                controlPlay: false,\n                currentState: false,\n                title : '',\n                artist : '',\n                album : '',\n                mainArtUrl : '',\n                miniArtUrl : '',\n                mediaLength : 0,\n                mediaProgress : 0,\n                mediaProgressPercent : 0,\n                mediaId: '',\n                queueId: '',\n                quality: '',\n                qualityCodec: '',\n                qualityDataRate: null,\n                qualitySampleRate: null,\n                allowNext: false,\n                allowPlayPause: false,\n                allowPrevious: false,\n                allowRepeat: false,\n                allowShuffle: false,\n                playingInGroup: false,\n                playingInGroupId: ''\n            };\n            if (initialDeviceVolumes[device.serialNumber]) {\n                playerData.volume = initialDeviceVolumes[device.serialNumber].speakerVolume;\n                playerData.muted = initialDeviceVolumes[device.serialNumber].speakerMuted;\n                adapter.log.debug(`Initialize Volume for Device ${device.serialNumber}: volume=${playerData.volume} (muted=${playerData.muted})`);\n                delete initialDeviceVolumes[device.serialNumber];\n            }\n\n            function finalize(resMedia) {\n                if (lastPlayerState[device.serialNumber] && lastPlayerState[device.serialNumber].timeout) {\n                    clearTimeout(lastPlayerState[device.serialNumber].timeout);\n                }\n                lastPlayerState[device.serialNumber] = {\n                    resPlayer,\n                    resMedia,\n                    ts: Date.now(),\n                    devId: devId,\n                    timeout: null\n                };\n\n                playerData.currentState = resPlayer.playerInfo.state === 'PLAYING';\n\n                let mediaRemaining = 0;\n                if (resPlayer.playerInfo) {\n                    if (resPlayer.playerInfo.infoText) {\n                        playerData.title = resPlayer.playerInfo.infoText.title;\n                        playerData.artist = resPlayer.playerInfo.infoText.subText1;\n                        playerData.album = resPlayer.playerInfo.infoText.subText2;\n                    }\n\n                    if (resPlayer.playerInfo.mainArt) {\n                        playerData.mainArtUrl = resPlayer.playerInfo.mainArt.url;\n                    }\n                    if (resPlayer.playerInfo.miniArt) {\n                        playerData.miniArtUrl = resPlayer.playerInfo.miniArt.url;\n                    }\n                    playerData.mediaId = resPlayer.playerInfo.mediaId;\n                    playerData.queueId = resPlayer.playerInfo.queueId;\n                    playerData.playingInGroup = resPlayer.playerInfo.isPlayingInLemur;\n                    playerData.playingInGroupId = resPlayer.playerInfo.playingInLemurId;\n\n                    if (resPlayer.playerInfo.progress) {\n                        playerData.mediaLength = parseInt(resPlayer.playerInfo.progress.mediaLength || '0', 10);\n                        playerData.mediaProgress = parseInt(resPlayer.playerInfo.progress.mediaProgress, 10);\n                        if (playerData.mediaLength > 0) {\n                            playerData.mediaProgressPercent = Math.round(((playerData.mediaProgress * 100) / playerData.mediaLength));\n                            mediaRemaining = playerData.mediaLength - playerData.mediaProgress;\n                        }\n                    }\n\n                    playerData.controlPause = resPlayer.playerInfo.state === 'PAUSED';\n                    playerData.controlPlay = resPlayer.playerInfo.state === 'PLAYING';\n\n                    if (resPlayer.playerInfo.quality) {\n                        playerData.quality = resPlayer.playerInfo.quality.name;\n                        if (resPlayer.playerInfo.quality.stats) {\n                            playerData.qualityCodec = resPlayer.playerInfo.quality.stats.codec;\n                            playerData.qualityDataRate = Math.round(resPlayer.playerInfo.quality.stats.dataRateInBitsPerSecond/1000);\n                            playerData.qualitySampleRate = resPlayer.playerInfo.quality.stats.samplingRateInHertz;\n                        }\n                    }\n\n                    if (resPlayer.playerInfo.transport) {\n                        playerData.allowNext = resPlayer.playerInfo.transport.next === 'ENABLED';\n                        playerData.allowPlayPause = resPlayer.playerInfo.transport.playPause === 'ENABLED';\n                        playerData.allowPrevious = resPlayer.playerInfo.transport.previous === 'ENABLED';\n                        playerData.allowRepeat = resPlayer.playerInfo.transport.repeat === 'ENABLED';\n                        playerData.allowShuffle = resPlayer.playerInfo.transport.shuffle === 'ENABLED';\n                    }\n                }\n                // Set States\n                adapter.setState(`${devId}.Player.providerName`, playerData.providerName, true); // 'Amazon Music' | 'TuneIn Live-Radio'\n                adapter.setState(`${devId}.Player.providerId`, playerData.providerId, true);\n                adapter.setState(`${devId}.Player.radioStationId`, playerData.radioStationId, true); // 's24885' | null\n                adapter.setState(`${devId}.Player.service`, playerData.service, true);\n                adapter.setState(`${devId}.Player.contentType`, playerData.contentType, true); // 'LIVE_STATION' | 'TRACKS' | 'CUSTOM_STATION\n                if (playerData.controlShuffle !== null) adapter.setState(`${devId}.Player.controlShuffle`, playerData.controlShuffle, true);\n                if (playerData.controlRepeat !== null) adapter.setState(`${devId}.Player.controlRepeat`, playerData.controlRepeat, true);\n\n                //let muted = res.playerInfo.volume.muted;\n                adapter.setState(`${devId}.Player.controlPause`, playerData.controlPause, true);\n                adapter.setState(`${devId}.Player.controlPlay`, playerData.controlPlay, true);\n\n                adapter.setState(`${devId}.Player.imageURL`, playerData.imageURL, true);\n\n                adapter.setState(`${devId}.Player.mediaId`, playerData.mediaId, true);\n                adapter.setState(`${devId}.Player.queueId`, playerData.queueId, true);\n                adapter.setState(`${devId}.Player.quality`, playerData.quality, true);\n                adapter.setState(`${devId}.Player.qualityCodec`, playerData.qualityCodec, true);\n                adapter.setState(`${devId}.Player.qualityDataRate`, playerData.qualityDataRate, true);\n                adapter.setState(`${devId}.Player.qualitySampleRate`, playerData.qualitySampleRate, true);\n                adapter.setState(`${devId}.Player.allowNext`, playerData.allowNext, true);\n                adapter.setState(`${devId}.Player.allowPlayPause`, playerData.allowPlayPause, true);\n                adapter.setState(`${devId}.Player.allowPrevious`, playerData.allowPrevious, true);\n                adapter.setState(`${devId}.Player.allowRepeat`, playerData.allowRepeat, true);\n                adapter.setState(`${devId}.Player.allowShuffle`, playerData.allowShuffle, true);\n                adapter.setState(`${devId}.Player.playingInGroup`, playerData.playingInGroup, true);\n                adapter.setState(`${devId}.Player.playingInGroupId`, playerData.playingInGroupId, true);\n\n                if (playerData.playingInGroup && playerData.currentState && playerData.playingInGroupId && device.isMultiroomDevice && device.clusterMembers) {\n                    device.clusterMembers.forEach(member => {\n                        const memberDevice = alexa.find(member);\n                        if (memberDevice && memberDevice.isControllable) {\n                            adapter.setState(`Echo-Devices.${memberDevice.serialNumber}.Player.playingInGroup`, true, true);\n                            adapter.setState(`Echo-Devices.${memberDevice.serialNumber}.Player.playingInGroupId`, playerData.playingInGroupId, true);\n                        }\n                    });\n                } else if ((!playerData.playingInGroup || !playerData.currentState) && device.isMultiroomDevice && device.clusterMembers && playingDevices[device.serialNumber]) { // Played before but now no longer playing in group\n                    device.clusterMembers.forEach(member => {\n                        if (playingDevices[member]) {\n                            return; // Device is updated itself while playing\n                        }\n                        const memberDevice = alexa.find(member);\n                        if (memberDevice && memberDevice.isControllable) {\n                            adapter.setState(`Echo-Devices.${memberDevice.serialNumber}.Player.playingInGroup`, false, true);\n                            adapter.setState(`Echo-Devices.${memberDevice.serialNumber}.Player.playingInGroupId`, null, true);\n                        }\n                    });\n                }\n                playingDevices[device.serialNumber] = playerData.currentState;\n\n                if (device.capabilities.includes('VOLUME_SETTING')) {\n                    if (playerData.muted !== null) adapter.setState(`${devId}.Player.muted`, playerData.muted, true);\n                    if (playerData.volume === 0 && device.isMultiroomDevice) playerData.volume = null;\n                    if (playerData.volume !== null) adapter.setState(`${devId}.Player.volume`, playerData.volume, true);\n                }\n\n                adapter.setState(`${devId}.Player.currentState`, playerData.currentState, true);\n\n                adapter.setState(`${devId}.Player.currentTitle`, playerData.title || '', true);\n                adapter.setState(`${devId}.Player.currentArtist`, playerData.artist || '', true);\n                adapter.setState(`${devId}.Player.currentAlbum`, playerData.album || '', true);\n\n                adapter.setState(`${devId}.Player.mainArtUrl`, playerData.mainArtUrl || '', true);\n                adapter.setState(`${devId}.Player.miniArtUrl`, playerData.miniArtUrl || '', true);\n\n                adapter.setState(`${devId}.Player.mediaLength`, parseInt(playerData.mediaLength || '0', 10), true);\n                adapter.setState(`${devId}.Player.mediaLengthStr`, sec2HMS(playerData.mediaLength), true);\n                adapter.setState(`${devId}.Player.mediaProgress`, playerData.mediaProgress, true);\n                adapter.setState(`${devId}.Player.mediaProgressStr`, sec2HMS(playerData.mediaProgress), true);\n                adapter.setState(`${devId}.Player.mediaProgressPercent`, playerData.mediaProgressPercent, true);\n                adapter.setState(`${devId}.Player.mediaRemaining`, mediaRemaining, true);\n                adapter.setState(`${devId}.Player.mediaLengthStr`, sec2HMS(mediaRemaining), true);\n\n                // Check Progress\n                if (resPlayer.playerInfo.state === 'PLAYING') {\n                    lastPlayerState[device.serialNumber].timeout = setTimeout( () => {\n                        lastPlayerState[device.serialNumber].timeout = null;\n                        updateMediaProgress(device.serialNumber);\n                    }, 2000);\n                }\n                setTimeout(doIt, Math.floor(Math.random() * 2000) + 500);\n            }\n\n            if (resPlayer.playerInfo !== undefined && resPlayer.playerInfo.provider) {\n                playerData.providerName = resPlayer.playerInfo.provider.providerName;\n            }\n\n            if (resPlayer.mainArt && resPlayer.mainArt.url !== undefined) playerData.mainArtUrl = resPlayer.mainArt.url;\n\n            if (resPlayer.volume !== undefined) playerData.muted = !!resPlayer.volume.muted;\n            if (resPlayer.playerInfo && resPlayer.playerInfo.volume && resPlayer.playerInfo.volume.volume !== null) {\n                playerData.volume = ~~resPlayer.playerInfo.volume.volume;\n            }\n\n            if (playerData.providerName !== 'Spotify' && playerData.providerName !== '') { // Spotify Podcast -> empty providerName\n                alexa.getMedia(device, (err, resMedia) => {\n                    if (stopped) return;\n                    if (err || !resMedia) return setTimeout(doIt, Math.floor(Math.random() * 2000) + 500);\n\n                    if (resMedia.shuffling !== undefined) playerData.controlShuffle = resMedia.shuffling;\n                    if (resMedia.looping !== undefined) playerData.controlRepeat = resMedia.looping;\n                    playerData.contentType = resMedia.contentType || ''; // 'LIVE_STATION' | 'TRACKS' | 'CUSTOM_STATION'\n                    if (resMedia.imageURL && resMedia.imageURL.endsWith('.')) resMedia.imageURL += 'png'; // Handle Amazon errors\n                    playerData.imageURL = resMedia.imageURL || '';\n                    playerData.muted = !!resMedia.muted;\n                    playerData.providerId = resMedia.providerId || ''; // 'TUNE_IN' | 'CLOUD_PLAYER' | 'ROBIN'\n                    playerData.radioStationId = resMedia.radioStationId || ''; // 's24885' | null\n                    playerData.service = resMedia.service || ''; // 'TUNE_IN' | 'CLOUD_PLAYER' | 'PRIME_STATION'\n                    if (resMedia && resMedia.volume !== undefined && resMedia.volume !== null) {\n                        playerData.volume = ~~resMedia.volume;\n                    }\n                    finalize(resMedia);\n                });\n            } else {\n                finalize();\n            }\n        });\n    })();\n}\n\nfunction getLists(listId, callback) {\n    if (typeof listId === 'function') {\n        callback = listId;\n        listId = null;\n    }\n\n    if (!adapter.config.synchronizeLists) {\n        return callback && callback();\n    }\n    const allListItems = [];\n    const node = 'Lists';\n    alexa.getLists((err, lists) => {\n        if (stopped) return;\n        !listId && setOrUpdateObject(node, {type: 'device', common: { 'name': 'Lists' }});\n\n        if (Array.isArray(lists)) {\n            lists.forEach(list => {\n                if (listId && list.listId !== listId) return;\n                // modify states\n                list.name = list.name || list.type;\n                list.id = list.name.replace(adapter.FORBIDDEN_CHARS, '-').replace(/ /g, '_').replace(/\\./g, '_');\n                delete list.listIds;\n                delete list.itemId;\n\n                listMap[list.listId] = list.id;\n                // create channel\n                setOrUpdateObject(`${node}.${list.id}`, {\n                    type: 'channel',\n                    common: {name: `${ucFirst(list.name.toLowerCase().replace('list', '').replace(/_/g, ' '))} List`}\n                });\n\n                // create state for new item\n                //adapter.subscribeStates(node + '.' + list.id + '.#New');\n                setOrUpdateObject(`${node}.${list.id}.#New`, {\n                    common: {\n                        type: 'mixed',\n                        role: 'state',\n                        name: 'Add new list item'\n                    }\n                }, '', (value) => {\n                    if (value) {\n                        adapter.setState(`${node}.${list.id}.#New`, '', true);\n                        addListItem(list, typeof value === 'string' ? {'value': value} : JSON.parse(value));\n                    }\n                });\n\n                // write list contents as states\n                for (const key in list) {\n                    if (list[key] !== null) {\n                        setOrUpdateObject(`${node}.${list.id}.${key}`, {common: listObjects[key] ? listObjects[key] : {'role': 'text'}}, list[key]);\n                    }\n                }\n\n                // read list items\n                adapter.log.debug(`Updating list ${list.name}...`);\n                allListItems.push(updateListItems(list));\n            });\n        }\n\n        Promise.all(allListItems).then(() => callback && callback());\n    });\n}\n\nfunction addListItem(list, item) {\n    adapter.log.info(`Adding item \"${item.value}\" (${JSON.stringify(item)}) to the list ${list.name}.`);\n    alexa.addListItem(list.listId, item); // , (err, res) => updateListItems(list)\n}\n\nfunction updateListItem(list, item) {\n    adapter.log.info(`Updating item \"${item.value}\" (${JSON.stringify(item)}) of the list ${list.name}.`);\n    alexa.updateListItem(list.listId, item.id, item); // , (err, res) => updateListItems(list)\n}\n\nfunction deleteListItem(list, item) {\n    adapter.log.info(`Deleting item \"${item.value}\" from the list ${list.name}.`);\n    alexa.deleteListItem(list.listId, item.id, item); // , (err, res) => updateListItems(list)\n}\n\nfunction updateListItems(list, callback) {\n    let node = `Lists.${list.id}`;\n    return new Promise(resolve => {\n        alexa.getListItems(list.listId || list.itemId, (err, items) => {\n            if (stopped) return;\n            setOrUpdateObject(`${node}.json`, {common: {name: 'List as json', role: 'json'}}, JSON.stringify(items));\n\n            node = `${node}.items`;\n            setOrUpdateObject(node, {type: 'channel', common: {name: 'All list items'}});\n\n            if (Array.isArray(items)) {\n                items.forEach((item, index) => {\n                    item.index = index;\n                    item['#delete'] = false;\n                    item.listName = list.name;\n\n                    /*\n                     * EXAMPLE PAYLOAD\n                     *\n                    {\n                        completed: true,\n                        createdDateTime: 1574597641331,\n                        customerId: null,\n                        id: '8140fa62-XXXX-XXXX-XXXX-7380a2ccd6ab',\n                        listId: 'YW16bjEuYWNXXXXXXXXXZCWFhRLVRBU0s=',\n                        shoppingListItem: false,\n                        updatedDateTime: 1574619085063,\n                        value: 'Q',\n                        version: 2\n                    }\n                    */\n\n                    setOrUpdateObject(`${node}.${item.id}`, {\n                        type: 'channel',\n                        common: {name: `List item ${index + 1}`}\n                    });\n                    for (const key in item) {\n\n                        if (item[key] !== null) {\n                            if (key === '#delete') {\n                                //adapter.subscribeStates(node + '.' + item.id + '.' + key);\n                                setOrUpdateObject(`${node}.${item.id}.${key}`, {common: listItemsObjects[key] ? listItemsObjects[key] : {'role': 'text'}}, item[key], () => deleteListItem(list, item));\n                            } else {\n                                setOrUpdateObject(`${node}.${item.id}.${key}`, {common: listItemsObjects[key] ? listItemsObjects[key] : {'role': 'text'}}, item[key], ['completed', 'value'].indexOf(key) === -1 ? null : (value) => updateListItem(list, {\n                                    ...item,\n                                    [key]: value\n                                }));\n                            }\n                        }\n                    }\n                });\n            }\n\n            callback && callback();\n            resolve(true);\n        });\n    });\n}\n\nfunction initRoutines(callback) {\n    alexa.getAutomationRoutines((err, routines) => {\n        if (stopped) return;\n        automationRoutines = [];\n        routineTriggerUtterances = {};\n        if (!err && routines && Array.isArray(routines)) {\n            for (let i = 0; i < routines.length; i++) {\n                const routine = routines[i];\n                if (routine['@type'] !== 'com.amazon.alexa.behaviors.model.Automation') {\n                    adapter.log.debug(`Ignore unknown type of Automation Routine ${routine['@type']}`);\n                    continue;\n                }\n                if (!routine.sequence) {\n                    adapter.log.debug(`Automation Routine has no sequence ${JSON.stringify(routine)}`);\n                    continue;\n                }\n                let name = routine.name;\n                let utteranceWords = [];\n                if (routine.triggers && routine.triggers[0].payload && routine.triggers[0].payload.utterance) {\n                    name = name || routine.triggers[0].payload.utterance;\n                    utteranceWords = routine.triggers[0].payload.utterances || [routine.triggers[0].payload.utterance];\n                }\n                else if (routine.triggers && routine.triggers[0].payload && routine.triggers[0].payload.schedule && routine.triggers[0].payload.schedule.triggerTime) {\n                    if (!name) {\n                        name = name || routine.triggers[0].payload.schedule.triggerTime;\n                        if (name.length === 6) name = name.replace(/^({0-9}{2})({0-9}{2})({0-9}{2})$/, '$1:$2:$3');\n                        if (routine.triggers[0].payload.schedule.recurrence) name += ` ${routine.triggers[0].payload.schedule.recurrence}`;\n                    }\n                }\n                else if (!name && routine.triggers && routine.triggers[0] && routine.triggers[0].type) {\n                    name = name || routine.triggers[0].type.replace(/^Alexa\\.Trigger\\./, '');\n                }\n                else {\n                    adapter.log.debug(`Ignore unknown type of Automation Routine Trigger${JSON.stringify(routine.triggers[0].payload)}`);\n                    name = 'Unknown';\n                }\n                routine.friendlyName = name;\n                const idSplit = routine.automationId.split('.');\n                routine.friendlyAutomationId = idSplit[idSplit.length - 1];\n                routine.utteranceWords = utteranceWords;\n                automationRoutines.push(routine);\n            }\n        }\n        callback && callback();\n    });\n}\n\nfunction initCommUsers(callback) {\n    alexa.getAccount((err, commOwnAccount) => {\n        alexa.getHomeGroup((err, commHomeGroup) => {\n            if (stopped) return;\n            if (commHomeGroup && commHomeGroup.commsId) {\n                alexa.commsId = commHomeGroup.commsId;\n            }\n            if (!commHomeGroup || !commHomeGroup.homeGroupId) {\n                processObjectQueue(callback);\n                return;\n            }\n            alexa.getContacts({homeGroupId: commHomeGroup.homeGroupId}, (err, commContacts) => {\n                if (stopped) return;\n                if (err || !commContacts || !Array.isArray(commContacts)) {\n                    processObjectQueue(callback);\n                    return;\n                }\n                setOrUpdateObject('Contacts', {type: 'device', common: {name: 'Communication contacts'}});\n\n                commContacts.forEach((comEntry) => {\n                    if (!comEntry.commsId || !comEntry.commsId.length || !comEntry.alexaEnabled || comEntry.commsId[0] === commHomeGroup.homeGroupId) return;\n\n                    const contactId = comEntry.commsId[0].substr(comEntry.commsId[0].lastIndexOf('.') + 1);\n                    let contactName = comEntry.name.firstName;\n                    if (!contactName) {\n                        contactName = comEntry.company;\n                    }\n                    else if (comEntry.name.lastName) {\n                        contactName += ` ${comEntry.name.lastName}`;\n                    }\n                    if (comEntry.commsId[0] === alexa.commsId) {\n                        contactName += ' (Self)';\n                    }\n                    contactName = contactName || 'Unknown';\n\n                    setOrUpdateObject(`Contacts.${contactId}`, {type: 'channel', common: {name: contactName}});\n\n                    setOrUpdateObject(`Contacts.${contactId}.textMessage`, {common: {role: 'text', def: ''}}, '', function (value) {\n                        if (value === '') return;\n\n                        alexa.sendTextMessage(comEntry.commsId[0], value, (err, res) => {\n                            // Alexa-Remote: Response: {\"conversationId\":\"amzn1.comms.messaging.id.conversationV2~b3e030bd-3ca7-4921-9084-ab16832fd1ca\",\"messageIds\":[1],\"sequenceIds\":[1],\"time\":\"2019-07-21T08:49:13.522Z\"}\n                            //if (!err && res && res.messageIds && Array.isArray(res.messageIds) && res.messageIds.length === 1) {\n\n                            //}\n                        });\n                    });\n\n                    if (comEntry.commsId[0] === alexa.commsId) {\n                        setOrUpdateObject(`Contacts.${contactId}.#clearOwnMessages`, {common: {role: 'button', type: 'boolean', read: false, write: true, def: false}}, false, function (value) {\n                            alexa.getConversations((err, res) => {\n                                if (stopped) return;\n                                if (!err && res && res.conversations && Array.isArray(res.conversations)) {\n                                    res.conversations.forEach((conversation) => {\n                                        if (!conversation.participants || !conversation.participants.length || conversation.participants[0] !== alexa.commsId) return;\n                                        adapter.log.debug(`Delete Conversation with ID ${conversation.conversationId}`);\n                                        alexa.deleteConversation(conversation.conversationId, conversation.lastMessageId, (err, res) => {\n                                            //TODO\n                                        });\n                                    });\n                                }\n                            });\n                        });\n                    }\n\n                    adapter.log.debug(`Create contact \"${contactName}\" (${contactId})`);\n                });\n\n                processObjectQueue(callback);\n            });\n        });\n    });\n}\n\n\nfunction loadExistingAccessories(callback) {\n    adapter.getAdapterObjects((res) => {\n        const objectKeys = Object.keys(res);\n        for (let i = 0; i < objectKeys.length; i++) {\n            if (objectKeys[i].indexOf(`${adapter.namespace}.info`) === 0) continue;\n            existingStates[objectKeys[i].substr(adapter.namespace.length + 1)] = res[objectKeys[i]];\n        }\n        //adapter.log.debug('Existing States: ' + JSON.stringify(Object.keys(existingStates), null, 4));\n\n        // devId + '.Bluetooth' = device , ChannelsOd = MACs\n        // devId + '.Notifications' = channel, statesOf ??\n        // devId + '.Routines' = channel, statesOf\n\n        if (callback) callback();\n    });\n}\n\n\nfunction main() {\n    adapter.log.info('Starting Alexa2 adapter ... it can take several minutes to initialize all data. Please be patient! A done message is logged.');\n    crashCheckFileName = path.join(__dirname, `crashCheck-${adapter.namespace}.json`);\n    if (useCrashCheck) {\n        let crashCheck = {\n            startCounter: 0\n        };\n        try {\n            if (fs.existsSync(crashCheckFileName)) {\n                crashCheck = JSON.parse(fs.readFileSync(crashCheckFileName, 'utf8'));\n            }\n        } catch (err) {\n            adapter.log.error(`Error reading crashCheck file: ${err.message}`);\n        }\n        crashCheck.startCounter++;\n        try {\n            fs.writeFileSync(crashCheckFileName, JSON.stringify(crashCheck));\n        } catch (err) {\n            adapter.log.error(`Error writing crashCheck file: ${err.message}`);\n        }\n\n        if (crashCheck.startCounter > 3) {\n            crashCheck.startCounter = 2;\n            try {\n                fs.writeFileSync(crashCheckFileName, JSON.stringify(crashCheck));\n            } catch (err) {\n                adapter.log.error(`Error writing crashCheck file: ${err.message}`);\n            }\n            adapter.log.error('Adapter seems to have issues and crashed three times in a row. Disabling!! Please start again and check logs.');\n            setTimeout(() => {\n                isCrashStop = true; // Do not remove crash file\n                adapter.terminate && adapter.terminate(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);\n            }, 1000);\n            return;\n        }\n    }\n\n    if (!adapter.config.proxyOwnIp) {\n        const ifaces = os.networkInterfaces();\n        for (const eth of Object.keys(ifaces)) {\n            for (let num = 0; num < ifaces[eth].length; num++) {\n                if (ifaces[eth][num].family !== 'IPv6' && ifaces[eth][num].address !== '127.0.0.1' && ifaces[eth][num].address !== '0.0.0.0') {\n                    adapter.config.proxyOwnIp = ifaces[eth][num].address;\n                    adapter.log.info(`Proxy IP not set, use first network interface (${adapter.config.proxyOwnIp}) instead`);\n                    break;\n                }\n            }\n            if (adapter.config.proxyOwnIp) break;\n        }\n    }\n\n    let proxyOwnIp = adapter.config.proxyOwnIp;\n    if (adapter.config.proxyOverrideIp) {\n        proxyOwnIp = adapter.config.proxyOverrideIp;\n        adapter.log.info(`Use Override IP ${adapter.config.proxyOverrideIp} for Proxy`);\n    }\n\n    if (adapter.config.resetCookies) {\n        adapter.config.cookieData = '';\n        adapter.config.cookie = '';\n    }\n\n    const options = {\n        cookie: adapter.config.cookieData || adapter.config.cookie, // cookie if there is already one\n        macDms: adapter.config.macDms || (adapter.config.cookieData && adapter.config.cookieData.macDms) || undefined,\n        email: '', // Amazon email for login\n        password: '', // Amazon password for Login\n        bluetooth: true, // fetch Bluetooth devices\n        notifications: true, // fetch notifications (false because not works so far)\n        userAgent: adapter.config.userAgent, // overwrite userAgent\n        acceptLanguage: adapter.config.acceptLanguage, // overwrite acceptLanguage\n        amazonPage: adapter.config.cookieLoginUrl, // overwrite amazonPage\n        alexaServiceHost: adapter.config.alexaServiceHost, // overwrite alexa Servcie Host\n        logger: adapter.log.debug, // Logger with detailed debug only in debug\n        setupProxy: true,          // optional: should the library setup a proxy to get cookie when automatic way did not worked? Default false!\n        proxyOwnIp: proxyOwnIp, // required if proxy enabled: provide own IP or hostname to later access the proxy. needed to setup all rewriting and proxy stuff internally\n        proxyPort: adapter.config.proxyPort,           // optional: use this port for the proxy, default is 0 means random port is selected\n        proxyListenBind: adapter.config.proxyListenBind,// optional: set this to bind the proxy to a special IP, default is '0.0.0.0'\n        proxyLogLevel: null,      // optional: Loglevel of Proxy, default 'warn'\n        usePushConnection: false,\n        //deviceAppName: 'Amazon Alexa',\n        formerDataStorePath: path.join(__dirname, `formerDataStore${adapter.namespace}.json`),\n        apiUserAgentPostfix: `iBrokAlxa2/${require(path.join(__dirname, 'package.json')).version}`,\n        usePushConnectType: 2,\n        autoQueryActivityOnTrigger: adapter.config.autoQueryActivityOnTrigger,\n    };\n    adapter.config.updateHistoryInterval = parseInt(adapter.config.updateHistoryInterval, 10);\n    if (!adapter.config.updateHistoryInterval || isNaN(adapter.config.updateHistoryInterval) || adapter.config.updateHistoryInterval < 0) {\n        adapter.config.updateHistoryInterval = 0;\n    }\n    if (adapter.config.updateHistoryInterval !== 0 && adapter.config.updateHistoryInterval < 60) {\n        adapter.config.updateHistoryInterval = 60 + Math.floor(Math.random() * 60);\n        adapter.log.info(`Update History Interval is too low, set to ${adapter.config.updateHistoryInterval}s`);\n    } else if (adapter.config.updateHistoryInterval !== 0 && adapter.config.updateHistoryInterval >= 60) {\n        adapter.config.updateStateInterval += Math.floor(Math.random() * 10);\n    } else {\n        adapter.config.updateHistoryInterval = 0;\n    }\n    if (adapter.config.updateHistoryInterval !== 0) {\n        adapter.config.updateHistoryInterval = Math.max(adapter.config.updateHistoryInterval, 60);\n    }\n    if (adapter.config.updateHistoryInterval > 2147482) { // max 2147483647/1000 --> 2147482\n        adapter.config.updateHistoryInterval = 0;\n    }\n    adapter.log.debug(`Update History Interval: ${adapter.config.updateHistoryInterval !== 0 ? `${adapter.config.updateHistoryInterval}s` : 'disabled'}`);\n\n    adapter.config.updateStateInterval = parseInt(adapter.config.updateStateInterval, 10);\n    if (!adapter.config.updateStateInterval || isNaN(adapter.config.updateStateInterval) || adapter.config.updateStateInterval < 0) {\n        adapter.config.updateStateInterval = 0;\n    }\n    if (adapter.config.updateStateInterval !== 0 && adapter.config.updateStateInterval < 300) {\n        adapter.config.updateStateInterval = 300 + Math.floor(Math.random() * 60);\n        adapter.log.info(`Update Device State Interval is too low, set to ${adapter.config.updateStateInterval}s`);\n    } else if (adapter.config.updateStateInterval !== 0 && adapter.config.updateStateInterval >= 300) {\n        adapter.config.updateStateInterval += Math.floor(Math.random() * 10);\n    } else {\n        adapter.config.updateStateInterval = 0;\n    }\n    if (adapter.config.updateStateInterval !== 0) {\n        adapter.config.updateStateInterval = Math.max(adapter.config.updateStateInterval, 300);\n    }\n    if (adapter.config.updateStateInterval > 2147482) { // max 2147483647/1000 --> 2147482\n        adapter.config.updateStateInterval = 0;\n    }\n    adapter.log.debug(`Update Device State Interval: ${adapter.config.updateStateInterval !== 0 ? `${adapter.config.updateStateInterval}s` : 'disabled'}`);\n\n    adapter.config.updateConfigurationInterval = parseInt(adapter.config.updateConfigurationInterval, 10);\n    if (!adapter.config.updateConfigurationInterval || isNaN(adapter.config.updateConfigurationInterval) || adapter.config.updateConfigurationInterval < 0) {\n        adapter.config.updateConfigurationInterval = 0;\n    }\n    if (adapter.config.updateConfigurationInterval !== 0 && adapter.config.updateConfigurationInterval < 300) {\n        adapter.config.updateConfigurationInterval = 3600 + Math.floor(Math.random() * 60);\n        adapter.log.info(`Update Devices Configuration Interval is too low, set to ${adapter.config.updateConfigurationInterval}s`);\n    } else if (adapter.config.updateConfigurationInterval !== 0 && adapter.config.updateConfigurationInterval >= 300) {\n        adapter.config.updateConfigurationInterval += Math.floor(Math.random() * 10);\n    } else {\n        adapter.config.updateConfigurationInterval = 0;\n    }\n    if (adapter.config.updateConfigurationInterval !== 0) {\n        adapter.config.updateConfigurationInterval = Math.max(adapter.config.updateConfigurationInterval, 300);\n    }\n    if (adapter.config.updateConfigurationInterval > 2147482) { // max 2147483647/1000 --> 2147482\n        adapter.config.updateConfigurationInterval = 0;\n    }\n    adapter.log.debug(`Update Devices Configuration Interval: ${adapter.config.updateConfigurationInterval !== 0 ? `${adapter.config.updateConfigurationInterval}s` : 'disabled'}`);\n\n    if (adapter.config.synchronizeLists === undefined) {\n        adapter.config.synchronizeLists = true;\n    }\n    if (adapter.config.synchronizeSmartHomeDevices === undefined) {\n        adapter.config.synchronizeSmartHomeDevices = true;\n    }\n\n    let connectAfterDisconnect = false;\n\n    alexa = new Alexa();\n\n    const origSHDQueryFunc = alexa.querySmarthomeDevices.bind(alexa);\n    alexa.querySmarthomeDevices = function (toQuery, entityType, maxTimeout, callback) {\n        toQuery.forEach(q => {\n            if (q.entityId && q.entityId.startsWith('SKILL')) {\n                shdCounters.getSkill++;\n            } else {\n                shdCounters.get++;\n            }\n        });\n        origSHDQueryFunc(toQuery, entityType, maxTimeout, callback);\n    };\n    const origSHDExecFunc = alexa.executeSmarthomeDeviceAction.bind(alexa);\n    alexa.executeSmarthomeDeviceAction = function (entityIds, parameters, entityType, callback) {\n        if (!Array.isArray(entityIds)) entityIds = [entityIds];\n        entityIds.forEach(id => {\n            if (id.startsWith('SKILL')) {\n                shdCounters.setSkill++;\n            } else {\n                shdCounters.set++;\n            }\n        });\n        origSHDExecFunc(entityIds, parameters, entityType, callback);\n    };\n\n    alexa.on('ws-connect', () => {\n        if (initDone && connectAfterDisconnect) {\n            scheduleHistoryUpdate(2000);\n            scheduleStatesUpdate(2000);\n        }\n        connectAfterDisconnect = false;\n        pushConnected = true;\n        adapter.log.info(`Alexa-Push-Connection (macDms = ${!!options.macDms}) established. Disable Polling`);\n    });\n\n    alexa.on('ws-disconnect', (retries, msg) => {\n        adapter.log.info(`Alexa-Push-Connection disconnected${retries ? ' - retry' : ' - fallback to poll data'}: ${msg}`);\n        if (initDone && pushConnected) {\n            scheduleHistoryUpdate(2000);\n            scheduleStatesUpdate(2000);\n        }\n        if (pushConnected) {\n            connectAfterDisconnect = true;\n            pushConnected = false;\n        }\n    });\n\n    alexa.on('ws-error', (error) => {\n        adapter.log.info(`Alexa-Push-Connection Error: ${error && error.message ? error.message :  error}`);\n    });\n\n    alexa.on('ws-unknown-message', (incomingMsg) => {\n        adapter.log.info(`Alexa-Push-Connection Unknown Message - send to Developer: ${incomingMsg}`);\n    });\n\n    alexa.on('ws-device-connection-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Device Connection change for ${data.deviceSerialNumber} -> ${data.connectionState}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            adapter.log.debug(`Please Restart Adapter. Non-Existing Device was returned: ${data.deviceSerialNumber}`);\n            return;\n        }\n        if (!device) return;\n\n        const devId = `Echo-Devices.${device.serialNumber}`;\n        adapter.setState(`${devId}.online`, data.connectionState === 'ONLINE', true);\n    });\n\n    alexa.on('ws-bluetooth-state-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Bluetooth State change for ${data.deviceSerialNumber} -> ${data.bluetoothEvent}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            //adapter.log.info('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n\n        updateBluetoothStatus(device);\n    });\n\n    alexa.on('ws-audio-player-state-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Audio Player State change for ${data.deviceSerialNumber} -> ${data.audioPlayerState}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            //adapter.log.info('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n\n        if (data.audioPlayerState === 'INTERRUPTED' && lastPlayerState[device.serialNumber] && lastPlayerState[device.serialNumber].timeout) {\n            clearTimeout(lastPlayerState[device.serialNumber].timeout);\n            lastPlayerState[device.serialNumber].timeout = null;\n        }\n\n        if (lastPlayerState[device.serialNumber] && lastPlayerState[device.serialNumber].resPlayer && lastPlayerState[device.serialNumber].resPlayer.playerInfo) {\n            lastPlayerState[device.serialNumber].resPlayer.playerInfo.state = data.audioPlayerState;\n        }\n        const devId = `Echo-Devices.${device.serialNumber}`;\n        adapter.setState(`${devId}.Player.controlPause`, data.audioPlayerState !== 'PLAYING' && data.audioPlayerState !== 'FINISHED', true);\n        adapter.setState(`${devId}.Player.controlPlay`, data.audioPlayerState === 'PLAYING', true);\n        playingDevices[device.serialNumber] = data.audioPlayerState === 'PLAYING';\n        adapter.setState(`${devId}.Player.currentState`, data.audioPlayerState === 'PLAYING', true);\n\n        schedulePlayerUpdate(device.serialNumber, 1000);\n    });\n\n    alexa.on('ws-media-queue-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Media Queue change for ${data.deviceSerialNumber} -> ${data.changeType}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            //adapter.log.info('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n\n        schedulePlayerUpdate(device.serialNumber, 1000);\n    });\n\n    alexa.on('ws-media-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Media change for ${data.deviceSerialNumber}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device) {\n            //adapter.log.debug('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n\n        schedulePlayerUpdate(device.serialNumber, 1000);\n    });\n\n    alexa.on('ws-media-progress-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Media Progress change for ${data.deviceSerialNumber}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            //adapter.log.info('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n\n        schedulePlayerUpdate(device.serialNumber, 1000);\n    });\n\n    alexa.on('ws-volume-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Device Volume change for ${data.deviceSerialNumber} -> ${data.volume}/${data.isMuted}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            //adapter.log.info('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n        if (!device.isControllable || !device.capabilities.includes('VOLUME_SETTING')) return;\n\n        const devId = `Echo-Devices.${device.serialNumber}`;\n        let muted = !!data.isMuted;\n        if (data.isMuted === null && data.volume === 0) muted = true;\n        if (!muted) adapter.setState(`${devId}.Player.volume`, data.volume, true);\n        adapter.setState(`${devId}.Player.muted`, muted, true);\n\n        //schedulePlayerUpdate(device.serialNumber, 15000, true);\n    });\n\n    alexa.on('ws-content-focus-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Content Focus change for ${data.deviceSerialNumber}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            //adapter.log.info('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n\n        schedulePlayerUpdate(device.serialNumber, 1000);\n    });\n\n    alexa.on('ws-device-activity', (activity) => {\n        adapter.log.debug(`device-activity: ${JSON.stringify(activity)}`);\n        if (activity.description.summary && routineTriggerUtterances && routineTriggerUtterances[activity.deviceSerialNumber] && routineTriggerUtterances[activity.deviceSerialNumber][activity.description.summary.toLowerCase()]) {\n            adapter.setState(routineTriggerUtterances[activity.deviceSerialNumber][activity.description.summary.toLowerCase()], true, true);\n        }\n        updateHistoryStates(activity);\n    });\n\n    alexa.on('ws-equilizer-state-change', (data) => {\n        adapter.log.debug(`Alexa-Push-Connection Equalizer change for ${data.deviceSerialNumber} -> ${data.bass}/${data.midrange}/${data.treble}`);\n        const device = alexa.find(data.deviceSerialNumber);\n        if (!device && !appDevices[data.deviceSerialNumber]) {\n            //adapter.log.info('Please Restart Adapter. Non-Existing Device was returned: ' + data.deviceSerialNumber);\n            return;\n        }\n        if (!device) return;\n\n        const devId = `Echo-Devices.${device.serialNumber}`;\n        if (device.capabilities.includes('EQUALIZER_CONTROLLER_BASS')) {\n            adapter.setState(`${devId}.Preferences.equalizerBass`, data.bass, true);\n        }\n        if (device.capabilities.includes('EQUALIZER_CONTROLLER_MIDRANGE')) {\n            adapter.setState(`${devId}.Preferences.equalizerMidRange`, data.midrange, true);\n        }\n        if (device.capabilities.includes('EQUALIZER_CONTROLLER_TREBLE')) {\n            adapter.setState(`${devId}.Preferences.equalizerTreble`, data.treble, true);\n        }\n    });\n\n    alexa.on('ws-unknown-command', (command, payload) => {\n        adapter.log.info(`Alexa-Push-Connection Unknown Command ${command} - send to Developer: ${JSON.stringify(payload)}`);\n    });\n\n    const listsInProgress = {};\n    alexa.on('ws-todo-change', (payload) => {\n        if (!adapter.config.synchronizeLists) return;\n        adapter.log.debug(`Received updated list (${listsInProgress[payload.listId]}): ${JSON.stringify(payload)}`);\n\n        if (listsInProgress[payload.listId]) return;\n        listsInProgress[payload.listId] = true;\n\n        if (payload.eventType === 'listCreated' || payload.eventType === 'listUpdated') {\n            getLists(payload.listId, () => {\n                processObjectQueue(() => {\n                    delete listsInProgress[payload.listId];\n                });\n            });\n            return;\n        }\n\n        if (payload.eventType === 'listDeleted') {\n            if (listMap[payload.listId]) {\n                const node = `Lists.${listMap[payload.listId]}`;\n                deleteObject(node);\n            }\n            delete listsInProgress[payload.listId];\n            return;\n        }\n\n        alexa.getList(payload.listId, (err, list) => {\n            if (stopped) return;\n            if (!list || typeof list !== 'object') {\n                delete listsInProgress[payload.listId];\n                return;\n            }\n            // modify states\n            list.name = list.name || list.type;\n            if (!list.name) {\n                delete listsInProgress[payload.listId];\n                return;\n            }\n            list.id = list.name.replace(adapter.FORBIDDEN_CHARS, '-').replace(/ /g, '_').replace(/\\./g, '_');\n            list.listId = list.itemId;\n            delete list.listIds;\n            delete list.itemId;\n\n            // always update list\n            updateListItems(list, () => {\n                processObjectQueue(() => {\n\n                    // eventType: deleted\n                    if (payload.eventType === 'itemDeleted') {\n\n                        // delete objects\n                        const node = `Lists.${list.id}.items.${payload.listItemId}`;\n                        /*adapter.getObjectList({startkey: node, endkey: node + '.\\u9999'}, (err, objects) => {\n\n                            adapter.log.debug('Object list for delete for ' + node + '* : ' + JSON.stringify(objects));\n                            if (objects && objects.rows) {\n                                objects.rows.forEach(object => deleteObject(object.id));\n                            }\n                            delete listsInProgress[payload.listId];\n                        });*/\n                        deleteObject(node);\n                    }\n                    delete listsInProgress[payload.listId];\n                });\n            });\n        });\n    });\n\n    alexa.on('ws-notification-change', (data) => {\n        adapter.log.debug(`notification-change: ${JSON.stringify(data)}`);\n        if (data.eventType === 'DELETE') {\n            const device = alexa.find(data.deviceSerialNumber);\n            if (device && device.notifications) {\n                for (let i = 0; i < device.notifications.length; i++) {\n                    if (notificationTimer[data.notificationId]) {\n                        clearTimeout(notificationTimer[data.notificationId]);\n                        notificationTimer[data.notificationId] = null;\n                    }\n                    if (notificationTimer[`${data.notificationId}-customVolume`]) {\n                        clearTimeout(notificationTimer[`${data.notificationId}-customVolume`]);\n                        notificationTimer[`${data.notificationId}-customVolume`] = null;\n                    }\n                    if (notificationTimer[`${data.notificationId}-customVolumeReset`]) {\n                        clearTimeout(notificationTimer[`${data.notificationId}-customVolumeReset`]);\n                        notificationTimer[`${data.notificationId}-customVolumeReset`] = null;\n                    }\n                    if (device.notifications[i].notificationIndex === data.notificationId) {\n                        if (notificationTimer[device.notifications[i].id]) { // Remove Timer, will be reset if neeed in 2s\n                            clearTimeout(notificationTimer[device.notifications[i].id]);\n                            notificationTimer[device.notifications[i].id] = null;\n                        }\n                        if (notificationTimer[`${device.notifications[i].id}-customVolume`]) { // Remove Timer, will be reset if neeed in 2s\n                            clearTimeout(notificationTimer[`${device.notifications[i].id}-customVolume`]);\n                            notificationTimer[`${device.notifications[i].id}-customVolume`] = null;\n                        }\n                        if (notificationTimer[`${device.notifications[i].id}-customVolumeReset`]) { // Remove Timer, will be reset if neeed in 2s\n                            clearTimeout(notificationTimer[`${device.notifications[i].id}-customVolumeReset`]);\n                            notificationTimer[`${device.notifications[i].id}-customVolumeReset`] = null;\n                        }\n                        let type = device.notifications[i].type;\n                        if (type === 'MusicAlarm') {\n                            type = 'Alarm';\n                        }\n                        if (adapterObjects[`Echo-Devices.${data.deviceSerialNumber}.${type}.${data.notificationId}`]) {\n                            if (rememberedNotificationVolumeRestoreCallbacks[device.serialNumber]) {\n                                const callback = rememberedNotificationVolumeRestoreCallbacks[device.serialNumber];\n                                delete rememberedNotificationVolumeRestoreCallbacks[device.serialNumber];\n                                callback();\n                            }\n                            deleteObject(`Echo-Devices.${data.deviceSerialNumber}.${type}.${data.notificationId}`);\n                        }\n                        break;\n                    }\n                }\n            }\n        }\n        scheduleNotificationUpdate(data.deviceSerialNumber, 2000, true);\n    });\n\n    alexa.on('cookie', (cookie, csrf, macDms) => {\n        adapter.setState('info.cookie', alexa.cookie, true);\n        adapter.setState('info.csrf', alexa.csrf, true);\n\n        if (cookie !== adapter.config.cookie) {\n            adapter.log.info('Update cookie in adapter configuration ... restarting ...');\n            adapter.extendForeignObject(`system.adapter.${adapter.namespace}`, {\n                native: {\n                    cookie: alexa.cookie,\n                    csrf: alexa.csrf,\n                    macDms: macDms,\n                    cookieData: alexa.cookieData,\n                    email: '',\n                    password: '',\n                    resetCookies: false\n                }\n            });\n        }\n    });\n\n    alexa.init(options, err => {\n        if (err) {\n            adapter.log.debug(`Error from Alexa init: ${err.message}`);\n            let restartAdapter = true;\n            if (err.message === 'no csrf found') {\n                adapter.log.error('Error: no csrf found. Check configuration of cookie');\n                restartAdapter = false;\n            }\n            let lines = err.message.split('Please open ');\n            if (lines[1]) {\n                lines[1] = `Please open ${lines[1]}`;\n                proxyUrl = lines[1].substring(lines[1].indexOf('http://'), lines[1].lastIndexOf('/') + 1);\n                restartAdapter = false;\n            } else {\n                lines = err.message.split('\\n');\n            }\n            lines.forEach(line => adapter.log.error(`Error: ${line}`));\n\n            adapter.setState('info.connection', false, true);\n\n            if (!initDone && restartAdapter) {\n                adapter.terminate ? adapter.terminate(utils.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP) : process.exit(utils.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);\n            }\n            return;\n        }\n\n        if (alexa.cookie !== adapter.config.cookie) {\n            return;\n        }\n\n        adapter.setState('info.connection', true, true);\n\n        alexa.getMusicProviders((err, providers) => {\n            musicProviders = [];\n            if (!err && providers) {\n                musicProviders = providers;\n            }\n\n            initRoutines(() => {\n                getLists(() => {\n                    alexa.getAllDeviceVolumes((err, deviceVolumes) => {\n                        if (!err && deviceVolumes && deviceVolumes.volumes) {\n                            deviceVolumes.volumes.forEach(vol => initialDeviceVolumes[vol.dsn] = vol);\n                        }\n                        alexa.getRoutineSoundList((err, soundList) => {\n                            routineSounds = {};\n                            if (!err && soundList && Array.isArray(soundList)) {\n                                soundList.forEach(sound => {\n                                    if (sound.availability !== 'AVAILABLE') return;\n                                    routineSounds[sound.id] = sound.displayName;\n                                });\n                            }\n                            alexa.getRoutineSkillCatalog('YourSkills', (err, skillCatalog) => {\n                                routineSkills = {};\n                                if (!err && skillCatalog && skillCatalog.catalog && skillCatalog.catalog.catalogItems && Array.isArray(skillCatalog.catalog.catalogItems)) {\n                                    skillCatalog.catalog.catalogItems.forEach(skill => {\n                                        if (skill.itemType !== 'entry' || skill.disabled || skill.catalogType !== 'action') return;\n                                        routineSkills[skill.catalogId] = skill.title;\n                                    });\n                                }\n\n                                adapter.log.info('Initialize all Device states ...');\n                                createStates(() => {\n                                    createSmarthomeStates(() => {\n                                        queryAllSmartHomeDevices(true, false, () => {\n                                            initCommUsers(() => {\n                                                if (!initDone) {\n                                                    adapter.log.info('Subscribing to states...');\n                                                    adapter.subscribeStates('*');\n                                                    adapter.subscribeObjects('*');\n                                                    initDone = true;\n\n                                                    updateHistory(() => {\n                                                        updateDeviceConfigurationStates(() => {\n                                                            if (adapter.config.usePushConnection) {\n                                                                alexa.initPushConnection();\n                                                            }\n                                                            updatePlayerStatus( () => {\n                                                                adapter.log.info('Initialization Done ...');\n                                                                scheduleStatesUpdate();\n\n                                                                if (useCrashCheck) {\n                                                                    resetCrashCheckerTimeout = setTimeout(() => {\n                                                                        try {\n                                                                            fs.unlinkSync(crashCheckFileName);\n                                                                        } catch (err) {\n                                                                            adapter.log.error(`Error deleting crashCheck file: ${err.message}`);\n                                                                        }\n                                                                    }, 10 * 60 * 1000);\n                                                                }\n                                                            });\n                                                        });\n                                                    });\n                                                    const delIds = Object.keys(existingStates);\n                                                    if (delIds.length) {\n                                                        adapter.log.info(`Deleting the following states: ${JSON.stringify(delIds)}`);\n                                                        for (let i = 0; i < delIds.length; i++) {\n                                                            /*if (delIds[i].startsWith('Smart-Home-Devices.') && !delIds[i].endsWith('.#includeInIntervalQuery')) continue;*/ // TODO Do not cleanup for now, change later\n                                                            try {\n                                                                adapter.delObject(delIds[i], err => {\n                                                                    if (err) adapter.log.info(`Can not delete object ${delIds[i]}: ${err.message}`);\n                                                                });\n                                                            } catch (err) {\n                                                                adapter.log.info(`Can not delete object ${delIds[i]}: ${err.message}`);\n                                                            }\n                                                            delete existingStates[delIds[i]];\n                                                        }\n                                                    }\n                                                    checkerInterval = setInterval(() => {\n                                                        let allCalls = 0;\n                                                        let allSkills = 0;\n                                                        adapter.log.debug(`shdCounters: ${JSON.stringify(shdCounters)}`);\n                                                        Object.keys(shdCounters).forEach(key => {\n                                                            allCalls += shdCounters[key];\n                                                            if (key.endsWith('Skill')) allSkills += shdCounters[key];\n                                                        });\n                                                        if (allCalls > 1000 || allSkills > 500) {\n                                                            adapter.log.error(`Too many calls for Smart-Home-Devices in the last hour: ${allCalls} (via Skills: ${allSkills})`);\n                                                            adapter.log.error('The Alexa2 Smart Home devices are NOT designed to make mass calls and generate costs at the Skill developer for each call!');\n                                                            adapter.log.error('The Alexa2 Adapter will be deactivated now to prevent issues for your Amazon account, other ioBroker users of the adapter and also the Skill developers because of these mass calls.');\n                                                            adapter.log.error('Please contact iobroker@fischer-ka.de to find the reason for this behaviour of your installation!');\n                                                            Sentry && Sentry.withScope(scope => {\n                                                                scope.setLevel('info');\n                                                                scope.setExtra('requestCounters', `${JSON.stringify(shdCounters)}`);\n                                                                Sentry.captureMessage(`Too many SHD requests ${installationUuid}`, 'info');\n                                                            });\n\n                                                            setTimeout(() => adapter.disable(), 2000);\n                                                        }\n                                                        Object.keys(shdCounters).forEach(key => shdCounters[key] = 0);\n                                                    }, 60 * 60 * 1000);\n                                                }\n                                            });\n                                        });\n                                    });\n                                });\n                            });\n                        });\n                    });\n                });\n            });\n        });\n    });\n}\n\n// If started as allInOne mode => return function to create instance\nif (module.parent) {\n    module.exports = startAdapter;\n} else {\n    // or start the instance directly\n    startAdapter();\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"iobroker.alexa2\",\n  \"version\": \"3.27.4\",\n  \"description\": \"Remote control for Alexa (Amazon Echo)\",\n  \"author\": {\n    \"name\": \"Apollon77\",\n    \"email\": \"ingo@fischer-ka.de\"\n  },\n  \"contributors\": [\n    {\n      \"name\": \"soef\",\n      \"email\": \"soef@gmx.net\"\n    },\n    {\n      \"name\": \"Apollon77\",\n      \"email\": \"iobroker@fischer-ka.de\"\n    }\n  ],\n  \"homepage\": \"https://github.com/Apollon77/ioBroker.alexa2\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ioBroker\",\n    \"Alexa\",\n    \"iot\"\n  ],\n  \"engines\": {\n    \"node\": \">=16.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/Apollon77/ioBroker.alexa2\"\n  },\n  \"dependencies\": {\n    \"alexa-remote2\": \"^8.0.4\",\n    \"https\": \"^1.0.0\",\n    \"nearest-color\": \"^0.4.4\",\n    \"@iobroker/adapter-core\": \"^3.3.2\",\n    \"rrule\": \"^2.8.1\"\n  },\n  \"devDependencies\": {\n    \"@alcalzone/release-script\": \"^5.0.0\",\n    \"@alcalzone/release-script-plugin-iobroker\": \"^4.0.0\",\n    \"@alcalzone/release-script-plugin-license\": \"^4.0.0\",\n    \"@iobroker/dev-server\": \"^0.8.0\",\n    \"@iobroker/adapter-dev\": \"^1.5.0\",\n    \"eslint\": \"^8.57.1\",\n    \"mocha\": \"^11.7.5\",\n    \"chai\": \"^4.5.0\"\n  },\n  \"main\": \"main.js\",\n  \"scripts\": {\n    \"test\": \"node node_modules/mocha/bin/mocha --exit\",\n    \"release\": \"release-script\",\n    \"translate\": \"translate-adapter\",\n    \"dev-server\": \"dev-server\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/Apollon77/ioBroker.alexa2/issues\"\n  },\n  \"readmeFilename\": \"README.md\"\n}\n"
  },
  {
    "path": "test/lib/setup.js",
    "content": "/* jshint -W097 */// jshint strict:false\n/*jslint node: true */\n// check if tmp directory exists\nconst fs            = require('fs');\nconst path          = require('path');\nconst child_process = require('child_process');\nconst rootDir       = path.normalize(__dirname + '/../../');\nconst pkg           = require(rootDir + 'package.json');\nconst debug         = typeof v8debug === 'object';\npkg.main = pkg.main || 'main.js';\n\nlet JSONLDB;\n\nlet adapterName = path.normalize(rootDir).replace(/\\\\/g, '/').split('/');\nadapterName = adapterName[adapterName.length - 2];\nlet adapterStarted = false;\n\nfunction getAppName() {\n    const parts = __dirname.replace(/\\\\/g, '/').split('/');\n    return parts[parts.length - 3].split('.')[0];\n}\n\nfunction loadJSONLDB() {\n    if (!JSONLDB) {\n        const dbPath = require.resolve('@alcalzone/jsonl-db', {\n            paths: [rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller']\n        });\n        console.log('JSONLDB path: ' + dbPath);\n        try {\n            const { JsonlDB } = require(dbPath);\n            JSONLDB = JsonlDB;\n        } catch (err) {\n            console.log('Jsonl require error: ' + err);\n        }\n    }\n}\n\nconst appName = getAppName().toLowerCase();\n\nlet objects;\nlet states;\n\nlet pid = null;\n\nlet systemConfig = null;\n\nfunction copyFileSync(source, target) {\n\n    let targetFile = target;\n\n    //if target is a directory a new file with the same name will be created\n    if (fs.existsSync(target)) {\n        if ( fs.lstatSync( target ).isDirectory() ) {\n            targetFile = path.join(target, path.basename(source));\n        }\n    }\n\n    try {\n        fs.writeFileSync(targetFile, fs.readFileSync(source));\n    }\n    catch (err) {\n        console.log('file copy error: ' +source +' -> ' + targetFile + ' (error ignored)');\n    }\n}\n\nfunction copyFolderRecursiveSync(source, target, ignore) {\n    let files = [];\n\n    let base = path.basename(source);\n    if (base === adapterName) {\n        base = pkg.name;\n    }\n    //check if folder needs to be created or integrated\n    const targetFolder = path.join(target, base);\n    if (!fs.existsSync(targetFolder)) {\n        fs.mkdirSync(targetFolder);\n    }\n\n    //copy\n    if (fs.lstatSync(source).isDirectory()) {\n        files = fs.readdirSync(source);\n        files.forEach(function (file) {\n            if (ignore && ignore.indexOf(file) !== -1) {\n                return;\n            }\n\n            const curSource = path.join(source, file);\n            const curTarget = path.join(targetFolder, file);\n            if (fs.lstatSync(curSource).isDirectory()) {\n                // ignore grunt files\n                if (file.indexOf('grunt') !== -1) return;\n                if (file === 'chai') return;\n                if (file === 'mocha') return;\n                copyFolderRecursiveSync(curSource, targetFolder, ignore);\n            } else {\n                copyFileSync(curSource, curTarget);\n            }\n        });\n    }\n}\n\nif (!fs.existsSync(rootDir + 'tmp')) {\n    fs.mkdirSync(rootDir + 'tmp');\n}\n\nasync function storeOriginalFiles() {\n    console.log('Store original files...');\n    const dataDir = rootDir + 'tmp/' + appName + '-data/';\n\n    if (fs.existsSync(dataDir + 'objects.json')) {\n        const f = fs.readFileSync(dataDir + 'objects.json');\n        const objects = JSON.parse(f.toString());\n        if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) {\n            objects['system.adapter.admin.0'].common.enabled = false;\n        }\n        if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) {\n            objects['system.adapter.admin.1'].common.enabled = false;\n        }\n\n        fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects));\n        console.log('Store original objects.json');\n    }\n\n    if (fs.existsSync(dataDir + 'states.json')) {\n        try {\n            const f = fs.readFileSync(dataDir + 'states.json');\n            fs.writeFileSync(dataDir + 'states.json.original', f);\n            console.log('Store original states.json');\n        } catch (err) {\n            console.log('no states.json found - ignore');\n        }\n    }\n\n    if (fs.existsSync(dataDir + 'objects.jsonl')) {\n        loadJSONLDB();\n        const db = new JSONLDB(dataDir + 'objects.jsonl');\n        await db.open();\n\n        const admin0 = db.get('system.adapter.admin.0');\n        if (admin0) {\n            if (admin0.common) {\n                admin0.common.enabled = false;\n                db.set('system.adapter.admin.0', admin0);\n            }\n        }\n\n        const admin1 = db.get('system.adapter.admin.1');\n        if (admin1) {\n            if (admin1.common) {\n                admin1.common.enabled = false;\n                db.set('system.adapter.admin.1', admin1);\n            }\n        }\n        await db.close();\n\n        const f = fs.readFileSync(dataDir + 'objects.jsonl');\n        fs.writeFileSync(dataDir + 'objects.jsonl.original', f);\n        console.log('Store original objects.jsonl');\n    }\n\n    if (fs.existsSync(dataDir + 'states.jsonl')) {\n        const f = fs.readFileSync(dataDir + 'states.jsonl');\n        fs.writeFileSync(dataDir + 'states.jsonl.original', f);\n        console.log('Store original states.jsonl');\n    }\n}\n\nfunction restoreOriginalFiles() {\n    console.log('restoreOriginalFiles...');\n    const dataDir = rootDir + 'tmp/' + appName + '-data/';\n\n    if (fs.existsSync(dataDir + 'objects.json.original')) {\n        const f = fs.readFileSync(dataDir + 'objects.json.original');\n        fs.writeFileSync(dataDir + 'objects.json', f);\n    }\n    if (fs.existsSync(dataDir + 'objects.json.original')) {\n        const f = fs.readFileSync(dataDir + 'states.json.original');\n        fs.writeFileSync(dataDir + 'states.json', f);\n    }\n\n    if (fs.existsSync(dataDir + 'objects.jsonl.original')) {\n        const f = fs.readFileSync(dataDir + 'objects.jsonl.original');\n        fs.writeFileSync(dataDir + 'objects.jsonl', f);\n    }\n    if (fs.existsSync(dataDir + 'objects.jsonl.original')) {\n        const f = fs.readFileSync(dataDir + 'states.jsonl.original');\n        fs.writeFileSync(dataDir + 'states.jsonl', f);\n    }\n}\n\nasync function checkIsAdapterInstalled(cb, counter, customName) {\n    customName = customName || pkg.name.split('.').pop();\n    counter = counter || 0;\n    const dataDir = rootDir + 'tmp/' + appName + '-data/';\n    console.log('checkIsAdapterInstalled...');\n\n    try {\n        if (fs.existsSync(dataDir + 'objects.json')) {\n            const f = fs.readFileSync(dataDir + 'objects.json');\n            const objects = JSON.parse(f.toString());\n            if (objects['system.adapter.' + customName + '.0']) {\n                console.log('checkIsAdapterInstalled: ready!');\n                setTimeout(function () {\n                    if (cb) cb();\n                }, 100);\n                return;\n            } else {\n                console.warn('checkIsAdapterInstalled: still not ready');\n            }\n        } else if (fs.existsSync(dataDir + 'objects.jsonl')) {\n            loadJSONLDB();\n            const db = new JSONLDB(dataDir + 'objects.jsonl');\n            try {\n                await db.open();\n            } catch (err) {\n                if (err.message.includes('Failed to lock DB file')) {\n                    console.log('checkIsAdapterInstalled: DB still opened ...');\n                }\n                throw err;\n            }\n\n            const obj = db.get('system.adapter.' + customName + '.0');\n            await db.close();\n\n            if (obj) {\n                console.log('checkIsAdapterInstalled: ready!');\n                setTimeout(function () {\n                    if (cb) cb();\n                }, 100);\n                return;\n            } else {\n                console.warn('checkIsAdapterInstalled: still not ready');\n            }\n        } else {\n            console.error('checkIsAdapterInstalled: No objects file found in datadir ' + dataDir);\n        }\n\n    } catch (err) {\n        console.log('checkIsAdapterInstalled: catch ' + err);\n    }\n\n    if (counter > 20) {\n        console.error('checkIsAdapterInstalled: Cannot install!');\n        if (cb) cb('Cannot install');\n    } else {\n        console.log('checkIsAdapterInstalled: wait...');\n        setTimeout(function() {\n            checkIsAdapterInstalled(cb, counter + 1);\n        }, 1000);\n    }\n}\n\nasync function checkIsControllerInstalled(cb, counter) {\n    counter = counter || 0;\n    const dataDir = rootDir + 'tmp/' + appName + '-data/';\n\n    console.log('checkIsControllerInstalled...');\n    try {\n        if (fs.existsSync(dataDir + 'objects.json')) {\n            const f = fs.readFileSync(dataDir + 'objects.json');\n            const objects = JSON.parse(f.toString());\n            if (objects['system.certificates']) {\n                console.log('checkIsControllerInstalled: installed!');\n                setTimeout(function () {\n                    if (cb) cb();\n                }, 100);\n                return;\n            }\n        } else if (fs.existsSync(dataDir + 'objects.jsonl')) {\n            loadJSONLDB();\n            const db = new JSONLDB(dataDir + 'objects.jsonl');\n            try {\n                await db.open();\n            } catch (err) {\n                if (err.message.includes('Failed to lock DB file')) {\n                    console.log('checkIsControllerInstalled: DB still opened ...');\n                }\n                throw err;\n            }\n\n            const obj = db.get('system.certificates');\n            await db.close();\n\n            if (obj) {\n                console.log('checkIsControllerInstalled: installed!');\n                setTimeout(function () {\n                    if (cb) cb();\n                }, 100);\n                return;\n            }\n\n        } else {\n            console.error('checkIsControllerInstalled: No objects file found in datadir ' + dataDir);\n        }\n    } catch (err) {\n\n    }\n\n    if (counter > 20) {\n        console.log('checkIsControllerInstalled: Cannot install!');\n        if (cb) cb('Cannot install');\n    } else {\n        console.log('checkIsControllerInstalled: wait...');\n        setTimeout(function() {\n            checkIsControllerInstalled(cb, counter + 1);\n        }, 1000);\n    }\n}\n\nfunction installAdapter(customName, cb) {\n    if (typeof customName === 'function') {\n        cb = customName;\n        customName = null;\n    }\n    customName = customName || pkg.name.split('.').pop();\n    console.log('Install adapter...');\n    const startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js';\n    // make first install\n    if (debug) {\n        child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', {\n            cwd:   rootDir + 'tmp',\n            stdio: [0, 1, 2]\n        });\n        checkIsAdapterInstalled(function (error) {\n            if (error) console.error(error);\n            console.log('Adapter installed.');\n            if (cb) cb();\n        });\n    } else {\n        // add controller\n        const _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], {\n            cwd:   rootDir + 'tmp',\n            stdio: [0, 1, 2, 'ipc']\n        });\n\n        waitForEnd(_pid, function () {\n            checkIsAdapterInstalled(function (error) {\n                if (error) console.error(error);\n                console.log('Adapter installed.');\n                if (cb) cb();\n            });\n        });\n    }\n}\n\nfunction waitForEnd(_pid, cb) {\n    if (!_pid) {\n        cb(-1, -1);\n        return;\n    }\n    _pid.on('exit', function (code, signal) {\n        if (_pid) {\n            _pid = null;\n            cb(code, signal);\n        }\n    });\n    _pid.on('close', function (code, signal) {\n        if (_pid) {\n            _pid = null;\n            cb(code, signal);\n        }\n    });\n}\n\nfunction installJsController(cb) {\n    console.log('installJsController...');\n    if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') ||\n        !fs.existsSync(rootDir + 'tmp/' + appName + '-data')) {\n        // try to detect appName.js-controller in node_modules/appName.js-controller\n        // travis CI installs js-controller into node_modules\n        if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) {\n            console.log('installJsController: no js-controller => copy it from \"' + rootDir + 'node_modules/' + appName + '.js-controller\"');\n            // copy all\n            // stop controller\n            console.log('Stop controller if running...');\n            let _pid;\n            if (debug) {\n                // start controller\n                _pid = child_process.exec('node ' + appName + '.js stop', {\n                    cwd: rootDir + 'node_modules/' + appName + '.js-controller',\n                    stdio: [0, 1, 2]\n                });\n            } else {\n                _pid = child_process.fork(appName + '.js', ['stop'], {\n                    cwd:   rootDir + 'node_modules/' + appName + '.js-controller',\n                    stdio: [0, 1, 2, 'ipc']\n                });\n            }\n\n            waitForEnd(_pid, function () {\n                // copy all files into\n                if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp');\n                if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules');\n\n                if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){\n                    console.log('Copy js-controller...');\n                    copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/');\n                }\n\n                console.log('Setup js-controller...');\n                let __pid;\n                if (debug) {\n                    // start controller\n                    _pid = child_process.exec('node ' + appName + '.js setup first --console', {\n                        cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',\n                        stdio: [0, 1, 2]\n                    });\n                } else {\n                    __pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], {\n                        cwd:   rootDir + 'tmp/node_modules/' + appName + '.js-controller',\n                        stdio: [0, 1, 2, 'ipc']\n                    });\n                }\n                waitForEnd(__pid, function () {\n                    checkIsControllerInstalled(function () {\n                        // change ports for object and state DBs\n                        const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');\n                        config.objects.port = 19001;\n                        config.states.port  = 19000;\n\n                        // TEST WISE!\n                        //config.objects.type = 'jsonl';\n                        //config.states.type = 'jsonl';\n                        fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));\n                        console.log('Setup finished.');\n\n                        copyAdapterToController();\n\n                        installAdapter(async function () {\n                            await storeOriginalFiles();\n                            if (cb) cb(true);\n                        });\n                    });\n                });\n            });\n        } else {\n            // check if port 9000 is free, else admin adapter will be added to running instance\n            const client = new require('net').Socket();\n            client.on('error', () => {});\n            client.connect(9000, '127.0.0.1', function() {\n                console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.');\n                process.exit(0);\n            });\n\n            setTimeout(function () {\n                client.destroy();\n                if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) {\n                    console.log('installJsController: no js-controller => install dev build from npm');\n\n                    child_process.execSync('npm install ' + appName + '.js-controller@dev --prefix ./ --production', {\n                        cwd:   rootDir + 'tmp/',\n                        stdio: [0, 1, 2]\n                    });\n                } else {\n                    console.log('Setup js-controller...');\n                    let __pid;\n                    if (debug) {\n                        // start controller\n                        child_process.exec('node ' + appName + '.js setup first', {\n                            cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',\n                            stdio: [0, 1, 2]\n                        });\n                    } else {\n                        child_process.fork(appName + '.js', ['setup', 'first'], {\n                            cwd:   rootDir + 'tmp/node_modules/' + appName + '.js-controller',\n                            stdio: [0, 1, 2, 'ipc']\n                        });\n                    }\n                }\n\n                // let npm install admin and run setup\n                checkIsControllerInstalled(function () {\n                    let _pid;\n\n                    if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) {\n                        _pid = child_process.fork(appName + '.js', ['stop'], {\n                            cwd:   rootDir + 'node_modules/' + appName + '.js-controller',\n                            stdio: [0, 1, 2, 'ipc']\n                        });\n                    }\n\n                    waitForEnd(_pid, function () {\n                        // change ports for object and state DBs\n                        const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');\n                        config.objects.port = 19001;\n                        config.states.port  = 19000;\n\n                        // TEST WISE!\n                        //config.objects.type = 'jsonl';\n                        //config.states.type = 'jsonl';\n                        fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));\n\n                        copyAdapterToController();\n\n                        installAdapter(async function () {\n                            await storeOriginalFiles();\n                            if (cb) cb(true);\n                        });\n                    });\n                });\n            }, 1000);\n        }\n    } else {\n        setTimeout(function () {\n            console.log('installJsController: js-controller installed');\n            if (cb) cb(false);\n        }, 0);\n    }\n}\n\nfunction copyAdapterToController() {\n    console.log('Copy adapter...');\n    // Copy adapter to tmp/node_modules/appName.adapter\n    copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']);\n    console.log('Adapter copied.');\n}\n\nfunction clearControllerLog() {\n    const dirPath = rootDir + 'tmp/log';\n    let files;\n    try {\n        if (fs.existsSync(dirPath)) {\n            console.log('Clear controller log...');\n            files = fs.readdirSync(dirPath);\n        } else {\n            console.log('Create controller log directory...');\n            files = [];\n            fs.mkdirSync(dirPath);\n        }\n    } catch(e) {\n        console.error('Cannot read \"' + dirPath + '\"');\n        return;\n    }\n    if (files.length > 0) {\n        try {\n            for (let i = 0; i < files.length; i++) {\n                const filePath = dirPath + '/' + files[i];\n                fs.unlinkSync(filePath);\n            }\n            console.log('Controller log cleared');\n        } catch (err) {\n            console.error('cannot clear log: ' + err);\n        }\n    }\n}\n\nfunction clearDB() {\n    const dirPath = rootDir + 'tmp/iobroker-data/sqlite';\n    let files;\n    try {\n        if (fs.existsSync(dirPath)) {\n            console.log('Clear sqlite DB...');\n            files = fs.readdirSync(dirPath);\n        } else {\n            console.log('Create controller log directory...');\n            files = [];\n            fs.mkdirSync(dirPath);\n        }\n    } catch(e) {\n        console.error('Cannot read \"' + dirPath + '\"');\n        return;\n    }\n    if (files.length > 0) {\n        try {\n            for (let i = 0; i < files.length; i++) {\n                const filePath = dirPath + '/' + files[i];\n                fs.unlinkSync(filePath);\n            }\n            console.log('Clear sqlite DB');\n        } catch (err) {\n            console.error('cannot clear DB: ' + err);\n        }\n    }\n}\n\nfunction setupController(cb) {\n    installJsController(async function (isInited) {\n        try {\n            clearControllerLog();\n            clearDB();\n\n            if (!isInited) {\n                restoreOriginalFiles();\n                copyAdapterToController();\n            }\n            // read system.config object\n            const dataDir = rootDir + 'tmp/' + appName + '-data/';\n\n            if (fs.existsSync(dataDir + 'objects.json')) {\n                let objs;\n                try {\n                    objs = fs.readFileSync(dataDir + 'objects.json');\n                    objs = JSON.parse(objs);\n                } catch (e) {\n                    console.log('ERROR reading/parsing system configuration. Ignore');\n                    objs = {'system.config': {}};\n                }\n                if (!objs || !objs['system.config']) {\n                    objs = {'system.config': {}};\n                }\n\n                systemConfig = objs['system.config'];\n                if (cb) cb(objs['system.config']);\n            } else if (fs.existsSync(dataDir + 'objects.jsonl')) {\n                loadJSONLDB();\n                const db = new JSONLDB(dataDir + 'objects.jsonl');\n                await db.open();\n\n                let config = db.get('system.config');\n                systemConfig = config || {};\n\n                await db.close();\n\n                if (cb) cb(systemConfig);\n            } else {\n                console.error('read SystemConfig: No objects file found in datadir ' + dataDir);\n            }\n        } catch (err) {\n            console.error('setupController: ' + err);\n        }\n    });\n}\n\nasync function getSecret() {\n    var dataDir = rootDir + 'tmp/' + appName + '-data/';\n\n    if (systemConfig) {\n        return systemConfig.native.secret;\n    }\n    if (fs.existsSync(dataDir + 'objects.json')) {\n        let objs;\n        try {\n            objs = fs.readFileSync(dataDir + 'objects.json');\n            objs = JSON.parse(objs);\n        }\n        catch (e) {\n            console.warn(\"Could not load secret. Reason: \" + e);\n            return null;\n        }\n        if (!objs || !objs['system.config']) {\n            objs = {'system.config': {}};\n        }\n\n        return objs['system.config'].native.secre;\n    } else if (fs.existsSync(dataDir + 'objects.jsonl')) {\n        loadJSONLDB();\n        const db = new JSONLDB(dataDir + 'objects.jsonl');\n        await db.open();\n\n        let config = db.get('system.config');\n        config = config || {};\n\n        await db.close();\n\n        return config.native.secret;\n    } else {\n        console.error('read secret: No objects file found in datadir ' + dataDir);\n    }\n\n}\n\nfunction encrypt (key, value) {\n    var result = '';\n    for (var i = 0; i < value.length; ++i) {\n        result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));\n    }\n    return result;\n}\n\nfunction startAdapter(objects, states, callback) {\n    if (adapterStarted) {\n        console.log('Adapter already started ...');\n        if (callback) callback(objects, states);\n        return;\n    }\n    adapterStarted = true;\n    console.log('startAdapter...');\n    if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) {\n        try {\n            if (debug) {\n                // start controller\n                pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', {\n                    cwd: rootDir + 'tmp',\n                    stdio: [0, 1, 2]\n                });\n            } else {\n                // start controller\n                pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], {\n                    cwd:   rootDir + 'tmp',\n                    stdio: [0, 1, 2, 'ipc']\n                });\n            }\n        } catch (error) {\n            console.error(JSON.stringify(error));\n        }\n    } else {\n        console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main);\n    }\n    if (callback) callback(objects, states);\n}\n\nfunction startController(isStartAdapter, onObjectChange, onStateChange, callback) {\n    if (typeof isStartAdapter === 'function') {\n        callback = onStateChange;\n        onStateChange = onObjectChange;\n        onObjectChange = isStartAdapter;\n        isStartAdapter = true;\n    }\n\n    if (onStateChange === undefined) {\n        callback  = onObjectChange;\n        onObjectChange = undefined;\n    }\n\n    if (pid) {\n        console.error('Controller is already started!');\n    } else {\n        console.log('startController...');\n        try {\n            const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');\n\n            adapterStarted = false;\n            let isObjectConnected;\n            let isStatesConnected;\n\n            // rootDir + 'tmp/node_modules\n            const objPath = require.resolve(`@iobroker/db-objects-${config.objects.type}`, {\n                paths: [ rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller']\n            });\n            console.log('Objects Path: ' + objPath);\n            const Objects = require(objPath).Server;\n            objects = new Objects({\n                connection: {\n                    'type': config.objects.type,\n                    'host': '127.0.0.1',\n                    'port': 19001,\n                    'user': '',\n                    'pass': '',\n                    'noFileCache': false,\n                    'connectTimeout': 2000\n                },\n                logger: {\n                    silly: function (msg) {\n                        console.log(msg);\n                    },\n                    debug: function (msg) {\n                        console.log(msg);\n                    },\n                    info: function (msg) {\n                        console.log(msg);\n                    },\n                    warn: function (msg) {\n                        console.warn(msg);\n                    },\n                    error: function (msg) {\n                        console.error(msg);\n                    }\n                },\n                connected: function () {\n                    isObjectConnected = true;\n                    if (isStatesConnected) {\n                        console.log('startController: started!');\n                        if (isStartAdapter) {\n                            startAdapter(objects, states, callback);\n                        } else {\n                            if (callback) {\n                                callback(objects, states);\n                                callback = null;\n                            }\n                        }\n                    }\n                },\n                change: onObjectChange\n            });\n\n            // Just open in memory DB itself\n            const statePath = require.resolve(`@iobroker/db-states-${config.states.type}`, {\n                paths: [ rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller']\n            });\n            console.log('States Path: ' + statePath);\n            const States = require(statePath).Server;\n            states = new States({\n                connection: {\n                    type: config.states.type,\n                    host: '127.0.0.1',\n                    port: 19000,\n                    options: {\n                        auth_pass: null,\n                        retry_max_delay: 15000\n                    }\n                },\n                logger: {\n                    silly: function (msg) {\n                        console.log(msg);\n                    },\n                    debug: function (msg) {\n                        console.log(msg);\n                    },\n                    info: function (msg) {\n                        console.log(msg);\n                    },\n                    warn: function (msg) {\n                        console.log(msg);\n                    },\n                    error: function (msg) {\n                        console.log(msg);\n                    }\n                },\n                connected: function () {\n                    isStatesConnected = true;\n                    if (isObjectConnected) {\n                        console.log('startController: started!!');\n                        if (isStartAdapter) {\n                            startAdapter(objects, states, callback);\n                        } else {\n                            if (callback) {\n                                callback(objects, states);\n                                callback = null;\n                            }\n                        }\n                    }\n                },\n                change: onStateChange\n            });\n        } catch (err) {\n            console.log(err);\n        }\n    }\n}\n\nfunction stopAdapter(cb) {\n    if (!pid) {\n        console.error('Controller is not running!');\n        if (cb) {\n            setTimeout(function () {\n                cb(false);\n            }, 0);\n        }\n    } else {\n        adapterStarted = false;\n        pid.on('exit', function (code, signal) {\n            if (pid) {\n                console.log('child process terminated due to receipt of signal ' + signal);\n                if (cb) cb();\n                pid = null;\n            }\n        });\n\n        pid.on('close', function (code, signal) {\n            if (pid) {\n                if (cb) cb();\n                pid = null;\n            }\n        });\n\n        pid.kill('SIGTERM');\n    }\n}\n\nfunction _stopController() {\n    if (objects) {\n        objects.destroy();\n        objects = null;\n    }\n    if (states) {\n        states.destroy();\n        states = null;\n    }\n}\n\nfunction stopController(cb) {\n    let timeout;\n    if (objects) {\n        console.log('Set system.adapter.' + pkg.name + '.0');\n        objects.setObject('system.adapter.' + pkg.name + '.0', {\n            common:{\n                enabled: false\n            }\n        });\n    }\n\n    stopAdapter(function () {\n        if (timeout) {\n            clearTimeout(timeout);\n            timeout = null;\n        }\n\n        _stopController();\n\n        if (cb) {\n            cb(true);\n            cb = null;\n        }\n    });\n\n    timeout = setTimeout(function () {\n        timeout = null;\n        console.log('child process NOT terminated');\n\n        _stopController();\n\n        if (cb) {\n            cb(false);\n            cb = null;\n        }\n        pid = null;\n    }, 5000);\n}\n\n// Setup the adapter\nasync function setAdapterConfig(common, native, instance) {\n    const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);\n    if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.json')) {\n        const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());\n        if (common) objects[id].common = common;\n        if (native) objects[id].native = native;\n        fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects));\n    } else if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.jsonl')) {\n        loadJSONLDB();\n        const db = new JSONLDB(rootDir + 'tmp/' + appName + '-data/objects.jsonl');\n        await db.open();\n\n        let obj = db.get(id);\n        if (common) obj.common = common;\n        if (native) obj.native = native;\n        db.set(id, obj);\n\n        await db.close();\n    } else {\n        console.error('setAdapterConfig: No objects file found in datadir ' + rootDir + 'tmp/' + appName + '-data/');\n    }\n}\n\n// Read config of the adapter\nasync function getAdapterConfig(instance) {\n    const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);\n    if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.json')) {\n        const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());\n        return objects[id];\n    } else if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.jsonl')) {\n        loadJSONLDB();\n        const db = new JSONLDB(rootDir + 'tmp/' + appName + '-data/objects.jsonl');\n        await db.open();\n\n        let obj = db.get(id);\n\n        await db.close();\n        return obj;\n    } else {\n        console.error('getAdapterConfig: No objects file found in datadir ' + rootDir + 'tmp/' + appName + '-data/');\n    }\n}\n\nif (typeof module !== undefined && module.parent) {\n    module.exports.getAdapterConfig = getAdapterConfig;\n    module.exports.setAdapterConfig = setAdapterConfig;\n    module.exports.startController  = startController;\n    module.exports.stopController   = stopController;\n    module.exports.setupController  = setupController;\n    module.exports.stopAdapter      = stopAdapter;\n    module.exports.startAdapter     = startAdapter;\n    module.exports.installAdapter   = installAdapter;\n    module.exports.appName          = appName;\n    module.exports.adapterName      = adapterName;\n    module.exports.adapterStarted   = adapterStarted;\n    module.exports.getSecret        = getSecret;\n    module.exports.encrypt          = encrypt;\n}\n"
  },
  {
    "path": "test/mocha.setup.js",
    "content": "process.on(\"unhandledRejection\", (r) => { throw r; });\n"
  },
  {
    "path": "test/testAdapter.js",
    "content": "/* jshint -W097 */// jshint strict:false\n/*jslint node: true */\nvar expect = require('chai').expect;\nvar setup  = require(__dirname + '/lib/setup');\n\nvar objects = null;\nvar states  = null;\nvar onStateChanged = null;\nvar onObjectChanged = null;\nvar sendToID = 1;\n\nvar adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);\n\nfunction checkConnectionOfAdapter(cb, counter) {\n    counter = counter || 0;\n    console.log('Try check #' + counter);\n    if (counter > 30) {\n        if (cb) cb('Cannot check connection');\n        return;\n    }\n\n    states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {\n        if (err) console.error(err);\n        if (state && state.val) {\n            if (cb) cb();\n        } else {\n            setTimeout(function () {\n                checkConnectionOfAdapter(cb, counter + 1);\n            }, 1000);\n        }\n    });\n}\n\nfunction checkValueOfState(id, value, cb, counter) {\n    counter = counter || 0;\n    if (counter > 20) {\n        if (cb) cb('Cannot check value Of State ' + id);\n        return;\n    }\n\n    states.getState(id, function (err, state) {\n        if (err) console.error(err);\n        if (value === null && !state) {\n            if (cb) cb();\n        } else\n        if (state && (value === undefined || state.val === value)) {\n            if (cb) cb();\n        } else {\n            setTimeout(function () {\n                checkValueOfState(id, value, cb, counter + 1);\n            }, 500);\n        }\n    });\n}\n\nfunction sendTo(target, command, message, callback) {\n    onStateChanged = function (id, state) {\n        if (id === 'messagebox.system.adapter.test.0') {\n            callback(state.message);\n        }\n    };\n\n    states.pushMessage('system.adapter.' + target, {\n        command:    command,\n        message:    message,\n        from:       'system.adapter.test.0',\n        callback: {\n            message: message,\n            id:      sendToID++,\n            ack:     false,\n            time:    (new Date()).getTime()\n        }\n    });\n}\n\ndescribe('Test ' + adapterShortName + ' adapter', function() {\n    before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) {\n        this.timeout(600000); // because of first install from npm\n\n        setup.setupController(async function () {\n            var config = await setup.getAdapterConfig();\n            // enable adapter\n            config.common.enabled  = true;\n            config.common.loglevel = 'debug';\n\n            //config.native.dbtype   = 'sqlite';\n\n            await setup.setAdapterConfig(config.common, config.native);\n\n            setup.startController(true, function(id, obj) {}, function (id, state) {\n                    if (onStateChanged) onStateChanged(id, state);\n                },\n                function (_objects, _states) {\n                    objects = _objects;\n                    states  = _states;\n                    _done();\n                });\n        });\n    });\n\n/*\n    ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY\n*/\n    it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) {\n        this.timeout(60000);\n        checkConnectionOfAdapter(function (res) {\n            if (res) console.log(res);\n            expect(res).not.to.be.equal('Cannot check connection');\n            objects.setObject('system.adapter.test.0', {\n                    common: {\n\n                    },\n                    type: 'instance'\n                },\n                function () {\n                    states.subscribeMessage('system.adapter.test.0');\n                    done();\n                });\n        });\n    });\n\n    it('Test ' + adapterShortName + ' adapter: wait a bit', function (done) {\n        this.timeout(60000);\n        setTimeout(done, 50000);\n    });\n\n    after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) {\n        this.timeout(10000);\n\n        setup.stopController(function (normalTerminated) {\n            console.log('Adapter normal terminated: ' + normalTerminated);\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/testPackageFiles.js",
    "content": "/* jshint -W097 */\n/* jshint strict:false */\n/* jslint node: true */\n/* jshint expr: true */\n'use strict';\n\nconst expect = require('chai').expect;\nconst fs     = require('fs');\n\ndescribe('Test package.json and io-package.json', () => {\n    it('Test package files', done => {\n        console.log();\n\n        const fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');\n        const ioPackage = JSON.parse(fileContentIOPackage);\n\n        const fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');\n        const npmPackage = JSON.parse(fileContentNPMPackage);\n\n        expect(ioPackage).to.be.an('object');\n        expect(npmPackage).to.be.an('object');\n\n        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;\n        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;\n\n        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);\n\n        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {\n            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');\n            console.log();\n        }\n\n        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;\n        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;\n\n        expect(ioPackage.common.licenseInformation, 'ERROR: License missing in io-package in common.license').to.exist;\n\n        if (ioPackage.common.name.indexOf('template') !== 0) {\n            if (Array.isArray(ioPackage.common.authors)) {\n                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);\n                if (ioPackage.common.authors.length === 1) {\n                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');\n                }\n            }\n            else {\n                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');\n            }\n        }\n        else {\n            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');\n            console.log();\n        }\n        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;\n        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {\n            console.log('WARNING: titleLang is not existing in io-package.json. Please add');\n            console.log();\n        }\n        if (\n            ioPackage.common.title.indexOf('iobroker') !== -1 ||\n            ioPackage.common.title.indexOf('ioBroker') !== -1 ||\n            ioPackage.common.title.indexOf('adapter') !== -1 ||\n            ioPackage.common.title.indexOf('Adapter') !== -1\n        ) {\n            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');\n            console.log();\n        }\n\n        if (!ioPackage.common.controller && !ioPackage.common.onlyWWW && !ioPackage.common.noConfig) {\n            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html')) {\n                console.log('WARNING: Admin3 support is missing! Please add it');\n                console.log();\n            }\n            if (ioPackage.common.materialize) {\n                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;\n            }\n        }\n\n        const licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');\n        const fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');\n        if (fileContentReadme.indexOf('## Changelog') === -1) {\n            console.log('Warning: The README.md should have a section ## Changelog');\n            console.log();\n        }\n        expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;\n        if (!licenseFileExists) {\n            console.log('Warning: The License should also exist as LICENSE file');\n            console.log();\n        }\n        if (fileContentReadme.indexOf('## License') === -1) {\n            console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');\n            console.log();\n        }\n        done();\n    });\n});\n"
  }
]