[
  {
    "path": ".esformatter",
    "content": "{\n  \"plugins\": [\n    \"esformatter-quotes\",\n    \"esformatter-braces\",\n    \"esformatter-semicolons\"\n  ],\n  \"quotes\": {\n    \"type\": \"single\",\n    \"avoidEscape\": false\n  },\n  \"whiteSpace\": {\n    \"before\": {\n      \"ParameterList\": -1,\n      \"ParameterComma\": -1,\n      \"FunctionDeclarationOpeningBrace\": -1,\n      \"FunctionDeclarationClosingBrace\": -1,\n      \"ForStatementExpressionOpening\": -1\n    },\n    \"after\": {\n      \"FunctionName\": -1,\n      \"ParameterComma\": 1,\n      \"FunctionReservedWord\": -1,\n      \"ParameterList\": -1,\n      \"FunctionDeclarationOpeningBrace\": -1,\n      \"PropertyName\": -1\n    }\n  },\n  \"lineBreak\": {\n    \"before\": {\n      \"EndOfFile\": 1\n    }\n  }\n}\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n    \"root\": true,\n    \"env\": {\n        \"es6\": true,\n        \"node\": true\n    },\n    \"parserOptions\": {\n        \"ecmaVersion\": 9,\n        \"ecmaFeatures\": {\n            \"jsx\": true\n        },\n        \"sourceType\": \"module\"\n    },\n    \"rules\": {\n        \"semi\": \"error\",\n        \"eqeqeq\": \"error\",\n        \"no-const-assign\": \"warn\",\n        \"no-this-before-super\": \"warn\",\n        \"no-undef\": \"warn\",\n        \"no-unreachable\": \"warn\",\n        \"no-unused-vars\": \"warn\",\n        \"constructor-super\": \"warn\",\n        \"valid-typeof\": \"warn\"\n    },\n    \"extends\": [\n        \"eslint:recommended\",\n        \"prettier\"\n    ]\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RVFJTG8H86SK8&source=url\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug Report\nabout: Create a report to help us improve\ntitle: '(BUG) {ENTER TITLE HERE} '\nlabels: 'bug'\nassignees: 'tonesto7'\n\n---\n\n## Verify the following before opening an trouble issue\n\n**Go over all the following points, and put an `x` in all the boxes that apply.\nIf you're unsure about any of these, don't hesitate to ask. We're here to help!**\n\n-  [ ] That _OAuth_ is Enabled for the SmartApp under the IDE.\n-  [ ] The SmartApp and Device Handler are using the latest code available.\n-  [ ] That Both the SmartApps and Device Handlers have been _Published for You_ in the IDE.\n\n---\n## About Your Setup\n-  How many devices are detected?:\n-  Mobile App Version(Not required):\n-  SmartApp Version:\n-  Device Handler Version:\n-  Homebridge Version:\n-  NodeJS Version:\n\n## Expected Behavior\n**Tell us what you think should be happening**\n\n## Current Behavior\n**What happens instead of the expected behavior?**\n\n## Steps to Reproduce (for bugs)\n**Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code to reproduce, if relevant**\n\n1.\n2.\n3.\n4.\n\n## Context\n**How has this issue affected you? What are you trying to accomplish?\n**Providing context helps us come up with a solution that is most useful in the real world**\n\n---\nPlease include a copy of any relevant log output to assist in tracking down the bug\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature Request\nabout: Suggest an idea for this project\ntitle: '[Feature Request]'\nlabels: 'enhancement'\nassignees: 'tonesto7'\n\n---\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/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--- Provide a general summary of your changes in the Title above -->\n\n## Checklist:\n\n<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->\n<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->\n\n-   [ ] My code follows the code style of this project.\n-   [ ] My change requires a change to the documentation.\n\n## Types of changes\n\n<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->\n\n-   [ ] Bug fix (non-breaking change which fixes an issue)\n-   [ ] New feature (non-breaking change which adds functionality)\n-   [ ] Breaking change (fix or feature that would cause existing functionality to change)\n\n## Description of Changes\n\n<!--- Describe your changes in detail -->\n\n## Reason for Change\n\n<!--- Why is this change required? What problem does it solve? -->\n<!--- If it fixes an open issue, please link to the issue here. -->\n\n## How Has This Been Tested?\n\n<!--- Please describe how you tested your changes. -->\n\n## Screenshots (if appropriate):\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a stale issue is closed\ndaysUntilClose: 7\n# Issues with these labels will never be considered stale\nexemptLabels:\n  - pinned\n  - security\n# Label to use when marking an issue as stale\nstaleLabel: wontfix\n# Comment to post when marking an issue 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. Thank you\n  for your contributions.\n# Comment to post when closing a stale issue. Set to `false` to disable\ncloseComment: true\n\n\n"
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "content": "name: Node-CI\n\non: [push, pull_request]\n\njobs:\n  build:\n\n    strategy:\n      matrix:\n        node-version: [10.x, 12.x, 13.x]\n        os: [ubuntu-latest, macOS-latest]\n\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - uses: actions/checkout@v1\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v1\n        with:\n          node-version: ${{ matrix.node-version }}\n      - name: npm install, build and test\n        run: |\n          npm ci\n          npm run build --if-present\n          npm test\n        env:\n          CI: true\n\n  publish-npm:\n    # publish should only ran on 'push' event and if a version tag was created\n    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')\n\n    needs: build # only run if build succeeds\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v1\n      - uses: actions/setup-node@v1\n        with:\n          node-version: 10 # use the minimum required version\n          registry-url: https://registry.npmjs.org/\n      - run: npm ci\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": ".vscode/*\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\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\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# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\ntempCodeRunnerFile.js\nAWS_Files/Testing/input.json\n.vscode/settings.json\nwebsite/\n\nconfig.json\naccessories\nauth.json\npersist\n.uix-secrets\nauth.json\n\n.uix-secrets\nsmartthings_rsa.pub\nlogaudit.json\nhomebridge-smartthings-v2-logaudit.json\naccessories/cachedAccessories\n"
  },
  {
    "path": ".snyk",
    "content": "# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.\nversion: v1.14.1\nignore: {}\n# patches apply the minimum changes required to fix a vulnerability\npatch:\n  SNYK-JS-LODASH-567746:\n    - lodash:\n        patched: '2020-05-01T01:44:59.218Z'\n    - winston > async > lodash:\n        patched: '2020-05-01T01:44:59.218Z'\n    - portfinder-sync > portfinder > async > lodash:\n        patched: '2020-05-01T01:44:59.218Z'\n"
  },
  {
    "path": "CHANGELOG-app.md",
    "content": "# Changelog\n\n## v2.3.3\n\n- [FIX] Minor bugs and icons squashed.\n\n## v2.3.2\n\n- [NEW] Added support for bringing acceleration sensors into homekit as motion sensors.\n- [FIX] Fixed issue with new Garage and Thermostat define type inputs from actually bringing in the devices.\n\n## v2.3.1\n\n- [FIX] Typo `?.` in code preventing saving in IDE.\n- [NEW] Fixed new version info when using beta version of plugin.\n\n## v2.3.0\n\n- [REMOVE] Support for Local Commands removed (It doesn't really speed up anything anyways)\n- [NEW] Added garage door, thermostat inputs to define device types\n- [FIX] Minor bugfixes and optimizations.\n\n## v2.2.1\n\n- [FIX] Minor tweaks to support shades fixes in the plugin.\n\n## v2.2.0\n\n- [UPDATE] Added support for passing the pressed button number when provided.\n- [FIX] Other minor bugfixes and optimizations.\n- [REMOVE] Support for Energy and Power capabilities removed (for now).\n\n## v2.1.1\n\n- [UPDATE] The app now validates the appId on all local commands made to ST app so if you have more than one instance of the homebridge smartapp it doesn't start sending events to wrong plugin.\n\n## v2.1.0\n\n- [NEW] Added a Device Event and Command history page to review events and commands sent and received by the plugin.\n- [UPDATE] Cleaned up some of the unnecessary attributes from the subscription logic.\n- [FIX] Refactored the accessToken logic to be more consistent. #38\n- [UPDATE] Modified the device event subscription process to reduce timeouts.\n- [FIX] Other bug fixes, cleanups, and optimizations.\n\n## v2.0.3\n\n- [NEW] Added a new device data input where you can select a device and see all available attributes, capabilities, commands, and the last 30 events.\n- [FIX] Other bug fixes and cleanups.\n\n## v2.0.1\n\n- [UPDATE] Reworked and cleaned up the UI so it's now more organized and easier to follow.\n- [NEW] Added new capability filter options.\n- [UPDATE] Optimized the command/event streaming system to perform faster and more reliably.\n- [NEW] Added duplicate device detection cleanups so Homekit doesn't try to create duplicate devices and throw an error.\n- [FIX] Many, many other bug fixes and cleanups.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## v2.3.8\n\n- [UPDATE] Updated dependencies.\n\n## v2.3.4\n\n- [REMOVE] Removing Sentry error reporting module prior to submitting plugin for verification under homebridge.\n\n## v2.3.3\n\n- [FIX] Packages updates.\n\n## v2.3.2\n\n- [NEW] Added support for bringing acceleration sensors into homekit as motion sensors.\n\n## v2.3.1\n\n- [FIX] Plugin wasn't sending pluginstatus and enable direct messages to SmartApp, so device events weren't being sent to the plugin\n- [NEW] Changed some of the plugin version check logic. I also runs after every device refresh (~1 hour)\n\n## v2.3.0\n\n- [REMOVE] Support for Local Commands removed (It doesn't really speed up anything anyways)\n- [NEW] Rate-Limiting of commands (debounce)\n- [UPDATE] Command optimizations.\n- [NEW] Switched web request library from Request-Promise to Axios.\n- [FIX] StatusActive characteristic now reports correctly.\n- [FIX] Minor bugfixes and optimizations.\n\n## v2.2.1\n\n- [FIX] Resolved the issue with Window Shades not working #71.\n- [FIX] Resolve null Service types issue #74.\n\n## v2.2.0\n\n- [UPDATE] Button logic now generates the push/held actions for every button available on the remote now. Meaning you can select the parent remote and have it show actions for each button on the remote. NOTE: I've noticed that I need to open the home app once after adding the buttons to create the event connection.\n- [FIX] Buttons should now work 100% again. Sorry about the issues.\n- [FIX] Fixed the cannot read property of 'includes' and '\\_events' errors.\n- [FIX] Fixed some rare issues with requestPromises on device commands.\n- [FIX] Lot's of other minor cleanup.\n- [NEW] Direct port is now selected automatically using the direct_port config value as the start point for available port detection.\n- [NEW] Logs now alert you when your local ST hub endpoint can't be reached.\n- [NEW] Added a new config item to define your ST Community username in the error reporting so if you want me to be able to review your issues.\n- [NEW] Added config item to allow you to disable error reporting.\n- [UPDATE] Modified the point when the Sentry IO Error collector is loaded so it doesn't collect other plugin exceptions.replace\n- [UPDATE] Updated Sentry.IO library to v5.11.1.\n- [UPDATE] Changed the plugin to not list every single device loaded from cache and every device updated in the logs. They are only visible when debug option is enabled.\n- [REMOVE] Support for Energy and Power capabilities removed (for now).\n\n## v2.1.14 - v2.1.16\n\n- [FIX] Thermostats should now update the state correctly and also auto mode is working again.\n\n## v2.1.13\n\n- [NEW] Added Sentry library to help collect/report anonymous error/exception data (absolutely no person data is shared with the exception of maybe a device label in the logs).\n- [FIX] Thermostats should now update the state correctly.\n- [FIX] Resolved the issue with Buttons crashing your entire HomeKit Instance.\n\n## v2.1.1 - v2.1.12\n\n- [UPDATE] Updated winston logger from v2 to v3 to help with issues running on Hoobs.\n- [UPDATE] Added app id header to all local commands made to ST app so if you have more than one instance of the homebridge smartapp it doesn't start sending events to wrong plugin.\n- [UPDATE] Updated the app config to allow setting the local_commands value.\n\n## v2.1.0\n\n- [UPDATE] Refactored the device service and characteristic logic so it's cleaner, more modular, and easier to maintain.\n- [NEW] Device services and characteristics are now cleaned up when they are no longer used.\n- [FIX] Lot's of fixes for device state updates and device commands.\n- [FIX] Button events should now work again.\n- [FIX] Updated the Hoobs config file (Plugin will be undergoing review by Hoobs to be certified soon) (@mkellsy)\n- [FIX] Added support for AirPurifier & AirQuality (@danielskowronski)\n- [FIX] Delays on device event updates resolved. (@devarshi) #33 #40\n- [FIX] Thermostat Mode fixes (@torandreroland)\n- [FIX] Dozens of other minor bugfixes and tweaks.\n\n## v2.0.5 - v2.0.10\n\n- [FIX] Fixed thermostat temp unit error.\n- [FIX] removed token/id validation by default to prevent error with mismatched access_token | app_id.\n- [FIX] Other minor bugfixes and tweaks.\n\n## v2.0.4\n\n- [FIX] Fixed AlarmStatus updates not being shown in the Home app when changed from ST side.\n- [FIX] Fixed issues with local_commands option.\n- [FIX] Fix for Celcius temperature conversions.\n- [NEW] Added support for new 'temperature_unit' config option using either the smartapp or config.json file.\n- [FIX] Other minor bugfixes and tweaks.\n\n## v2.0.1\n\n- [NEW] Completely rewrote the entire plugin using modern javascript structure.\n- [NEW] The code is now much cleaner, easier to update/maintain, and easier for others to follow.\n- [NEW] This translates into a faster/leaner and way more stable plugin than previous versions.\n- [NEW] The plugin now uses the Homebridge Dynamic platform API, meaning it no longer requires a restart of the Homebridge service for device changes to occur.\n- [NEW] The plugin now utilizes the device cache on service restart to prevent losing all of your devices when the plugin fails to start for an extended period of time.\n- [NEW] It will now remove devices no longer selected under SmartThings.\n- [NEW] Introduced an all-new logging system to provide more insight into issues and status, as well as write them to a file.\n- [NEW] I used all of the issues from my existing plugin to repair this new version.\n- [NEW] Many, many other bug fixes for devices, commands and many other items.\n- [NEW] **_Important NOTICE:_**\n- **Due to the changes in the plugin API you can not directly update the plugin from v1, you will need to add as a new accessory and setup your devices/automations/scenes again.\n  On a positive note, you can use the same SmartApp instance though as long as you update to the latest code.**\n"
  },
  {
    "path": "README.md",
    "content": "This Plugin is no longer being maintained.  The ST platform removed all of the greatness that made it fun to develop for and I will not rewrite my years of code to adapt.  \nI have moved to Hubitat and all of my code has lived on.\nI recommend you move over if possible.  You won't regret it!\n\nThanks for the many years of support.\n"
  },
  {
    "path": "appData.json",
    "content": "{\n    \"appDataVer\": \"1.1\",\n    \"versions\": {\n        \"mainApp\": \"2.3.3\",\n        \"plugin\": \"2.3.4\"\n    },\n    \"settings\": {\n        \"disableSentry\": true,\n        \"allowLocalCmds\": false\n    }\n}"
  },
  {
    "path": "config.schema.json",
    "content": "{\n    \"pluginAlias\": \"SmartThings-v2\",\n    \"pluginType\": \"platform\",\n    \"singular\": true,\n    \"footerDisplay\": \"If you need help or have issues visit: [issues](https://github.com/tonesto7/homebridge-smartthings-v2/issues)\",\n    \"schema\": {\n        \"name\": {\n            \"title\": \"Name\",\n            \"description\": \"This should default to SmartThings-v2\",\n            \"type\": \"string\",\n            \"default\": \"SmartThings-v2\",\n            \"required\": true\n        },\n        \"app_url\": {\n            \"title\": \"App Url\",\n            \"description\": \"To get this information, open Homebridge (SmartThings) SmartApp in your SmartThings Classic Mobile App, and tap on 'View Configuration Data for Homebridge'\",\n            \"type\": \"string\",\n            \"required\": true\n        },\n        \"app_id\": {\n            \"title\": \"App ID\",\n            \"description\": \"To get this information, open Homebridge (SmartThings) SmartApp in your SmartThings Classic Mobile App, and tap on 'View Configuration Data for Homebridge'\",\n            \"type\": \"string\",\n            \"required\": true\n        },\n        \"access_token\": {\n            \"title\": \"App Token\",\n            \"description\": \"To get this information, open Homebridge (SmartThings) SmartApp in your SmartThings Classic Mobile App, and tap on 'View Configuration Data for Homebridge'\",\n            \"type\": \"string\",\n            \"required\": true\n        },\n        \"communityUserName\": {\n            \"title\": \"SmartThings Community Username\",\n            \"description\": \"Only need to set this when you are having issues with the plugin and you want me to be able to identify your reported exception errors.\",\n            \"type\": \"string\",\n            \"required\": false\n        },\n        \"direct_ip\": {\n            \"title\": \"Direct IP\",\n            \"description\": \"Most installations won't need this, but if for any reason it can't identify your ip address correctly, use this setting to force the IP presented to SmartThings for the hub to send to.\",\n            \"type\": \"string\",\n            \"required\": false\n        },\n        \"direct_port\": {\n            \"title\": \"Direct Port\",\n            \"description\": \"This is the port that the plugin will listen on for traffic from your hub. Make sure your firewall allows incoming traffic on this port from your hub's IP address. (This is now a dynamic port selection)\",\n            \"type\": \"integer\",\n            \"maximum\": 65535,\n            \"default\": 8000,\n            \"required\": false\n        },\n        \"temperature_unit\": {\n            \"title\": \"Define Temperature Unit\",\n            \"type\": \"string\",\n            \"default\": \"F\",\n            \"oneOf\": [{\n                    \"title\": \"Fahrenheit\",\n                    \"enum\": [\n                        \"F\"\n                    ]\n                },\n                {\n                    \"title\": \"Celcius\",\n                    \"enum\": [\n                        \"C\"\n                    ]\n                }\n            ]\n        },\n        \"validateTokenId\": {\n            \"title\": \"Validate SmartApp Access Token and AppID?\",\n            \"description\": \"This will help secure your plugin by validating that the plugin is receiving data from the correct smartapp if you have multiple instances of the SmartApp.\",\n            \"type\": \"boolean\",\n            \"required\": true,\n            \"default\": false\n        },\n        \"logConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"debug\": {\n                    \"title\": \"Enable Debug logging?\",\n                    \"description\": \"This will show just about every log output available.\",\n                    \"type\": \"boolean\",\n                    \"required\": false,\n                    \"default\": false\n                },\n                \"showChanges\": {\n                    \"title\": \"Show Device Events in the Logs?\",\n                    \"description\": \"This will log device event changes received by SmartThings.\",\n                    \"type\": \"boolean\",\n                    \"required\": false,\n                    \"default\": true\n                },\n                \"hideTimestamp\": {\n                    \"title\": \"Hide TimeStamp Prefix inLogs?\",\n                    \"description\": \"This will remove the prefix from all console log output.\",\n                    \"type\": \"boolean\",\n                    \"required\": false,\n                    \"default\": true\n                },\n                \"hideNamePrefix\": {\n                    \"title\": \"Hide Plugin Name Prefix in Logs?\",\n                    \"description\": \"This will remove the prefix from all console log output.\",\n                    \"type\": \"boolean\",\n                    \"required\": false,\n                    \"default\": true\n                },\n                \"file\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"enabled\": {\n                            \"title\": \"Enable Logging to file\",\n                            \"description\": \"This log will be created as homebridge-smartthings-v2.log in the same folder as this config.json file.\",\n                            \"type\": \"boolean\",\n                            \"required\": false,\n                            \"default\": true\n                        },\n                        \"level\": {\n                            \"title\": \"Log File Output Level\",\n                            \"type\": \"string\",\n                            \"default\": \"good\",\n                            \"oneOf\": [{\n                                    \"title\": \"Debug\",\n                                    \"enum\": [\n                                        \"debug\"\n                                    ]\n                                },\n                                {\n                                    \"title\": \"Good\",\n                                    \"enum\": [\n                                        \"good\"\n                                    ]\n                                },\n                                {\n                                    \"title\": \"Notice\",\n                                    \"enum\": [\n                                        \"pink\"\n                                    ]\n                                },\n                                {\n                                    \"title\": \"Alert\",\n                                    \"enum\": [\n                                        \"alert\"\n                                    ]\n                                },\n                                {\n                                    \"title\": \"Warnings\",\n                                    \"enum\": [\n                                        \"warn\"\n                                    ]\n                                },\n                                {\n                                    \"title\": \"Errors\",\n                                    \"enum\": [\n                                        \"error\"\n                                    ]\n                                }\n                            ],\n                            \"required\": false\n                        }\n\n                    }\n                }\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "installerManifest.json",
    "content": "{\n    \"namespace\": \"tonesto7\",\n    \"repoOwner\": \"tonesto7\",\n    \"repoName\": \"homebridge-smartthings\",\n    \"repoBranch\": \"master\",\n    \"name\": \"Homebridge\",\n    \"author\": \"Anthony S.\",\n    \"description\": \"Provides the API interface between Homebridge (HomeKit) and SmartThings\",\n    \"category\": \"My Apps\",\n    \"bannerUrl\": \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings/master/images/hb_tonesto7@2x.png\",\n    \"forumUrl\": \"https://community.smartthings.com/t/release-homebridge-smartthings-v2-0/\",\n    \"docUrl\": \"https://github.com/tonesto7/homebridge-smartthings/#readme\",\n    \"releaseType\": \"production\",\n    \"keywords\": [\n        \"smartthings\",\n        \"homekit\",\n        \"homebridge\"\n    ],\n    \"notes\": \"Nothing to show here (yet)\",\n    \"smartApps\": {\n        \"parent\": {\n            \"name\": \"Homebridge\",\n            \"iconUrl\": \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings/master/images/hb_tonesto7@2x.png\",\n            \"published\": true,\n            \"oAuth\": true,\n            \"version\": \"2.3.4\",\n            \"appSettings\": {},\n            \"appUrl\": \"smartapps/tonesto7/homebridge-v2.src/homebridge.groovy\"\n        },\n        \"children\": []\n    },\n    \"deviceHandlers\": []\n}"
  },
  {
    "path": "jsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"ES6\"\n    },\n    \"exclude\": [\n        \"node_modules\"\n    ]\n}"
  },
  {
    "path": "package.json",
    "content": "{\n    \"dependencies\": {\n        \"axios\": \"^0.19.2\",\n        \"body-parser\": \"^1.19.0\",\n        \"chalk\": \"^4.1.0\",\n        \"compare-versions\": \"^3.6.0\",\n        \"express\": \"^4.17.1\",\n        \"hap-nodejs-community-types\": \"0.3.1\",\n        \"lodash\": \"^4.17.20\",\n        \"node-machine-id\": \"^1.1.12\",\n        \"os\": \"0.1.1\",\n        \"portfinder-sync\": \"^0.0.2\",\n        \"request\": \"^2.88.2\",\n        \"winston\": \"^3.3.3\",\n        \"winston-daily-rotate-file\": \"^4.5.0\",\n        \"snyk\": \"^1.380.0\"\n    },\n    \"devDependencies\": {\n        \"eslint\": \"^7.7.0\",\n        \"eslint-config-prettier\": \"^6.11.0\",\n        \"eslint-plugin-prettier\": \"^3.1.4\",\n        \"lint-staged\": \"^10.2.11\",\n        \"prettier\": \"^2.0.5\"\n    },\n    \"description\": \"SmartThings plugin for HomeBridge\",\n    \"engines\": {\n        \"homebridge\": \">=0.4.46\",\n        \"node\": \">=0.12.0\"\n    },\n    \"lint-staged\": {\n        \"*.js\": [\n            \"prettier --write\",\n            \"git add\"\n        ]\n    },\n    \"homepage\": \"https://github.com/tonesto7/homebridge-smartthings/#readme\",\n    \"keywords\": [\n        \"homebridge-plugin\",\n        \"smartthings\",\n        \"homekit\",\n        \"homebridge\",\n        \"category_climate\",\n        \"category_hubs\",\n        \"category_lighting\"\n    ],\n    \"funding\": {\n        \"type\": \"paypal\",\n        \"url\": \"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RVFJTG8H86SK8&source=url\"\n    },\n    \"scripts\": {\n        \"test\": \"eslint .\",\n        \"prettier\": \"prettier --write src/**/*.js\",\n        \"freeport\": \"npx kill-port 8000\",\n        \"start\": \"homebridge -D -P . -U .\",\n        \"snyk-protect\": \"snyk protect\",\n        \"prepare\": \"npm run snyk-protect\"\n    },\n    \"main\": \"src/index.js\",\n    \"license\": \"ISC\",\n    \"name\": \"homebridge-smartthings\",\n    \"preferGlobal\": true,\n    \"repository\": \"github:https://github.com/tonesto7/homebridge-smartthings\",\n    \"bugs\": {\n        \"url\": \"http://github.com/tonesto7/homebridge-smartthings/issues\"\n    },\n    \"version\": \"2.3.8\",\n    \"snyk\": true\n}\n"
  },
  {
    "path": "platform.schema.json",
    "content": "{\n    \"plugin_alias\": \"SmartThings-v2\",\n    \"schema\": {\n        \"type\": \"object\",\n        \"properties\": {\n            \"name\": {\n                \"title\": \"Name\",\n                \"description\": \"This should default to SmartThings-v2\",\n                \"type\": \"string\",\n                \"default\": \"SmartThings-v2\",\n                \"required\": true,\n                \"readOnly\": true\n            },\n            \"app_url\": {\n                \"title\": \"App Url\",\n                \"description\": \"To get this information, open Homebridge (SmartThings) SmartApp in your SmartThings Classic Mobile App, and tap on 'View Configuration Data for Homebridge'\",\n                \"type\": \"string\",\n                \"required\": true\n            },\n            \"app_id\": {\n                \"title\": \"App ID\",\n                \"description\": \"To get this information, open Homebridge (SmartThings) SmartApp in your SmartThings Classic Mobile App, and tap on 'View Configuration Data for Homebridge'\",\n                \"type\": \"string\",\n                \"required\": true\n            },\n            \"access_token\": {\n                \"title\": \"App Token\",\n                \"description\": \"To get this information, open Homebridge (SmartThings) SmartApp in your SmartThings Classic Mobile App, and tap on 'View Configuration Data for Homebridge'\",\n                \"type\": \"string\",\n                \"required\": true\n            },\n            \"communityUserName\": {\n                \"title\": \"SmartThings Community Username\",\n                \"description\": \"Only need to set this when you are having issues with the plugin and you want me to be able to identify your reported exception errors.\",\n                \"type\": \"string\",\n                \"required\": false\n            },\n            \"direct_port\": {\n                \"title\": \"Direct Port\",\n                \"description\": \"This is the port that the plugin will listen on for traffic from your hub. Make sure your firewall allows incoming traffic on this port from your hub's IP address. (This is now a dynamic port selection)\",\n                \"type\": \"integer\",\n                \"maximum\": 65535,\n                \"default\": 8000,\n                \"required\": false\n            },\n            \"temperature_unit\": {\n                \"title\": \"Define Temperature Unit\",\n                \"type\": \"string\",\n                \"default\": \"F\",\n                \"enum\": [{\n                        \"text\": \"Fahrenheit\",\n                        \"value\": \"F\"\n                    },\n                    {\n                        \"text\": \"Celcius\",\n                        \"value\": \"C\"\n                    }\n                ],\n                \"required\": true\n            },\n            \"validateTokenId\": {\n                \"title\": \"Validate SmartApp Access Token and AppID?\",\n                \"description\": \"This will help make sure your plugin is receiving data from the correct smartapp if you have multiple instances of the SmartApp.\",\n                \"type\": \"boolean\",\n                \"required\": false,\n                \"default\": false\n            },\n            \"logConfig\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"debug\": {\n                        \"title\": \"Enable Debug logging?\",\n                        \"description\": \"This will show just about every log output available.\",\n                        \"type\": \"boolean\",\n                        \"required\": false,\n                        \"default\": false\n                    },\n                    \"showChanges\": {\n                        \"title\": \"Show Device Events in the Logs?\",\n                        \"description\": \"This will log device event changes received by SmartThings.\",\n                        \"type\": \"boolean\",\n                        \"required\": false,\n                        \"default\": true\n                    },\n                    \"hideTimestamp\": {\n                        \"title\": \"Hide TimeStamp Prefix inLogs?\",\n                        \"description\": \"This will remove the prefix from all console log output.\",\n                        \"type\": \"boolean\",\n                        \"required\": false,\n                        \"default\": true\n                    },\n                    \"hideNamePrefix\": {\n                        \"title\": \"Hide Plugin Name Prefix in Logs?\",\n                        \"description\": \"This will remove the prefix from all console log output.\",\n                        \"type\": \"boolean\",\n                        \"required\": false,\n                        \"default\": true\n                    },\n                    \"file\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                            \"enabled\": {\n                                \"title\": \"Enable Logging to file\",\n                                \"description\": \"This log will be created as homebridge-smartthings-v2.log in the same folder as this config.json file.\",\n                                \"type\": \"boolean\",\n                                \"required\": false,\n                                \"default\": true\n                            },\n                            \"level\": {\n                                \"title\": \"Log File Output Level\",\n                                \"type\": \"string\",\n                                \"default\": \"Good\",\n                                \"enum\": [{\n                                        \"text\": \"Debug\",\n                                        \"value\": \"debug\"\n                                    },\n                                    {\n                                        \"text\": \"Good\",\n                                        \"value\": \"good\"\n                                    },\n                                    {\n                                        \"text\": \"Notice\",\n                                        \"value\": \"pink\"\n                                    },\n                                    {\n                                        \"text\": \"Alert\",\n                                        \"value\": \"alert\"\n                                    },\n                                    {\n                                        \"text\": \"Warnings\",\n                                        \"value\": \"warn\"\n                                    },\n                                    {\n                                        \"text\": \"Errors\",\n                                        \"value\": \"error\"\n                                    }\n                                ],\n                                \"required\": false\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "prettierrc",
    "content": "{\n  \"trailingComma\": \"all\"\n}\n"
  },
  {
    "path": "smartapps/tonesto7/homebridge-v2.src/homebridge-v2.groovy",
    "content": "/**\n *  Homebridge SmartThing Interface\n *  Loosely Modelled off of Paul Lovelace's JSON API\n *  Copyright 2018, 2019, 2020 Anthony Santilli\n */\n\nString appVersion()                     { return \"2.3.3\" }\nString appModified()                    { return \"04-28-2020\" }\nString branch()                         { return \"master\" }\nString platform()                       { return \"SmartThings\" }\nString pluginName()                     { return \"${platform()}-v2\" }\nString appIconUrl()                     { return \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings-v2/${branch()}/images/hb_tonesto7@2x.png\" }\nString getAppImg(imgName, ext=\".png\")   { return \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings-v2/${branch()}/images/${imgName}${ext}\" }\nMap minVersions()                       { return [plugin: 233] }\n\ndefinition(\n    name: \"Homebridge v2\",\n    namespace: \"tonesto7\",\n    author: \"Anthony Santilli\",\n    description: \"Provides the API interface between Homebridge (HomeKit) and ${platform()}\",\n    category: \"My Apps\",\n    iconUrl:   \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings-v2/master/images/hb_tonesto7@1x.png\",\n    iconX2Url: \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings-v2/master/images/hb_tonesto7@2x.png\",\n    iconX3Url: \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings-v2/master/images/hb_tonesto7@3x.png\",\n    oauth: true)\n\n{\n    appSetting \"devMode\"\n    appSetting \"log_address\"\n}\n\npreferences {\n    page(name: \"startPage\")\n    page(name: \"mainPage\")\n    page(name: \"defineDevicesPage\")\n    page(name: \"deviceSelectPage\")\n    page(name: \"changeLogPage\")\n    page(name: \"capFilterPage\")\n    page(name: \"virtDevicePage\")\n    page(name: \"developmentPage\")\n    page(name: \"donationPage\")\n    page(name: \"historyPage\")\n    page(name: \"deviceDebugPage\")\n    page(name: \"settingsPage\")\n    page(name: \"confirmPage\")\n}\n\nprivate Map ignoreLists() {\n    Boolean noPwr = true\n    Boolean noEnr = true\n    Map o = [\n        commands: [\"indicatorWhenOn\", \"indicatorWhenOff\", \"ping\", \"refresh\", \"indicatorNever\", \"configure\", \"poll\", \"reset\"],\n        attributes: [\n            'DeviceWatch-Enroll', 'DeviceWatch-Status', \"checkInterval\", \"LchildVer\", \"FchildVer\", \"LchildCurr\", \"FchildCurr\", \"lightStatus\", \"lastFanMode\", \"lightLevel\",\n            \"coolingSetpointRange\", \"heatingSetpointRange\", \"thermostatSetpointRange\"\n        ],\n        evt_attributes: [\n            'DeviceWatch-DeviceStatus', \"DeviceWatch-Enroll\", 'checkInterval', 'devTypeVer', 'dayPowerAvg', 'apiStatus', 'yearCost', 'yearUsage','monthUsage', 'monthEst', 'weekCost', 'todayUsage',\n            'maxCodeLength', 'maxCodes', 'readingUpdated', 'maxEnergyReading', 'monthCost', 'maxPowerReading', 'minPowerReading', 'monthCost', 'weekUsage', 'minEnergyReading',\n            'codeReport', 'scanCodes', 'verticalAccuracy', 'horizontalAccuracyMetric', 'altitudeMetric', 'latitude', 'distanceMetric', 'closestPlaceDistanceMetric',\n            'closestPlaceDistance', 'leavingPlace', 'currentPlace', 'codeChanged', 'codeLength', 'lockCodes', 'healthStatus', 'horizontalAccuracy', 'bearing', 'speedMetric',\n            'speed', 'verticalAccuracyMetric', 'altitude', 'indicatorStatus', 'todayCost', 'longitude', 'distance', 'previousPlace','closestPlace', 'places', 'minCodeLength',\n            'arrivingAtPlace', 'lastUpdatedDt', 'scheduleType', 'zoneStartDate', 'zoneElapsed', 'zoneDuration', 'watering', 'eventTime', 'eventSummary', 'endOffset', 'startOffset',\n            'closeTime', 'endMsgTime', 'endMsg', 'openTime', 'startMsgTime', 'startMsg', 'calName', \"deleteInfo\", \"eventTitle\", \"floor\", \"sleeping\", \"powerSource\", \"batteryStatus\",\n            \"LchildVer\", \"FchildVer\", \"LchildCurr\", \"FchildCurr\", \"lightStatus\", \"lastFanMode\", \"lightLevel\", \"coolingSetpointRange\", \"heatingSetpointRange\", \"thermostatSetpointRange\",\n            \"colorName\", \"locationForURL\", \"location\", \"offsetNotify\"\n        ],\n        capabilities: [\"Health Check\", \"Ultraviolet Index\", \"Indicator\", \"Window Shade Preset\"]\n    ]\n    if(noPwr) { o?.attributes?.push(\"power\"); o?.evt_attributes?.push(\"power\"); o?.capabilities?.push(\"Power Meter\") }\n    if(noEnr) { o?.attributes?.push(\"energy\"); o?.evt_attributes?.push(\"energy\"); o?.capabilities?.push(\"Energy Meter\") }\n    return o\n}\n\ndef startPage() {\n    if(!getAccessToken()) { return dynamicPage(name: \"mainPage\", install: false, uninstall: true) { section() { paragraph title: \"OAuth Error\", \"OAuth is not Enabled for ${app?.getName()}!.\\n\\nPlease click remove and Enable Oauth under the SmartApp App Settings in the IDE\", required: true, state: null } } }\n    else {\n        if(!state?.installData) { state?.installData = [initVer: appVersion(), dt: getDtNow().toString(), updatedDt: getDtNow().toString(), shownDonation: false] }\n        checkVersionData(true)\n        if(showChgLogOk()) { return changeLogPage() }\n        if(showDonationOk()) { return donationPage() }\n        return mainPage()\n    }\n}\n\ndef mainPage() {\n    Boolean isInst = (state?.isInstalled == true)\n    return dynamicPage(name: \"mainPage\", nextPage: (isInst ? \"confirmPage\" : \"\"), install: !isInst, uninstall: true) {\n        appInfoSect()\n        section(\"Define Specific Categories:\") {\n            paragraph \"Each category below will adjust the device attributes to make sure they are recognized as the desired device type under HomeKit.\\nNOTICE: Don't select the same devices used here in the Select Your Devices Input below.\", state: \"complete\"\n            Boolean conf = (lightList || buttonList || fanList || fan3SpdList || fan4SpdList || speakerList || shadesList || garageList || tstatList || tstatHeatList)\n            Integer fansize = (fanList?.size() ?: 0) + (fan3SpdList?.size() ?: 0) + (fan4SpdList?.size() ?: 0)\n            String desc = \"Tap to configure\"\n            if(conf) {\n                desc = \"\"\n                desc += lightList ? \"(${lightList?.size()}) Light Devices\\n\" : \"\"\n                desc += buttonList ? \"(${buttonList?.size()}) Button Devices\\n\" : \"\"\n                desc += (fanList || fan3SpdList || fan4SpdList) ? \"(${fansize}) Fan Devices\\n\" : \"\"\n                desc += speakerList ? \"(${speakerList?.size()}) Speaker Devices\\n\" : \"\"\n                desc += shadesList ? \"(${shadesList?.size()}) Shade Devices\\n\" : \"\"\n                desc += garageList ? \"(${garageList?.size()}) Garage Door Devices\\n\" : \"\"\n                desc += tstatList ? \"(${tstatList?.size()}) Tstat Devices\\n\" : \"\"\n                desc += tstatHeatList ? \"(${tstatHeatList?.size()}) Tstat Heat Devices\\n\" : \"\"\n                desc += \"\\nTap to modify...\"\n            }\n            href \"defineDevicesPage\", title: \"Define Device Types\", required: false, image: getAppImg(\"devices2\"), state: (conf ? \"complete\" : null), description: desc\n        }\n\n        section(\"All Other Devices:\") {\n            Boolean conf = (sensorList || switchList || deviceList)\n            String desc = \"Tap to configure\"\n            if(conf) {\n                desc = \"\"\n                desc += sensorList ? \"(${sensorList?.size()}) Sensor Devices\\n\" : \"\"\n                desc += switchList ? \"(${switchList?.size()}) Switch Devices\\n\" : \"\"\n                desc += deviceList ? \"(${deviceList?.size()}) Other Devices\\n\" : \"\"\n                desc += \"\\nTap to modify...\"\n            }\n            href \"deviceSelectPage\", title: \"Select your Devices\", required: false, image: getAppImg(\"devices\"), state: (conf ? \"complete\" : null), description: desc\n        }\n\n        inputDupeValidation()\n\n        section(\"Capability Filtering:\") {\n            Boolean conf = (\n                removeAcceleration || removeBattery || removeButton || removeContact || removeEnergy || removeHumidity || removeIlluminance || removeLevel || removeLock || removeMotion ||\n                removePower || removePresence || removeSwitch || removeTamper || removeTemp || removeValve\n            )\n            href \"capFilterPage\", title: \"Filter out capabilities from your devices\", required: false, image: getAppImg(\"filter\"), state: (conf ? \"complete\" : null), description: (conf ? \"Tap to modify...\" : \"Tap to configure\")\n        }\n\n        section(\"Virtual Devices:\") {\n            Boolean conf = (modeList || routineList)\n            String desc = \"Create virtual (mode, routine) devices\\n\\nTap to Configure...\"\n            if(conf) {\n                desc = \"\"\n                desc += modeList ? \"(${modeList?.size()}) Mode Devices\\n\" : \"\"\n                desc += routineList ? \"(${routineList?.size()}) Routine Devices\\n\" : \"\"\n                desc += \"\\nTap to modify...\"\n            }\n            href \"virtDevicePage\", title: \"Configure Virtual Devices\", required: false, image: getAppImg(\"devices\"), state: (conf ? \"complete\" : null), description: desc\n        }\n\n        section(\"Smart Home Monitor (SHM):\") {\n            paragraph title:\"NOTICE:\", \"This will not work with the New SmartThings Home Monitor (Under the new mobile app).  If you are using the new STHM please disable the setting below.\"\n            input \"addSecurityDevice\", \"bool\", title: \"Allow SHM Control in HomeKit?\", required: false, defaultValue: true, submitOnChange: true, image: getAppImg(\"alarm_home\")\n        }\n        section(\"Plugin Options & Review:\") {\n            // paragraph \"Turn off if you are having issues sending commands\"\n            // input \"sendCmdViaHubaction\", \"bool\", title: \"Send HomeKit Commands Locally?\", required: false, defaultValue: true, submitOnChange: true, image: getAppImg(\"command\")\n            input \"temp_unit\", \"enum\", title: \"Temperature Unit?\", required: true, defaultValue: location?.temperatureScale, options: [\"F\":\"Fahrenheit\", \"C\":\"Celcius\"], submitOnChange: true, image: getAppImg(\"command\")\n            Integer devCnt = getDeviceCnt()\n            href url: getAppEndpointUrl(\"config\"), style: \"embedded\", required: false, title: \"Render the platform data for Homebridge config.json\", description: \"Tap, select, copy, then click \\\"Done\\\"\", state: \"complete\", image: getAppImg(\"info\")\n            if(devCnt > 148) {\n                paragraph \"Notice:\\nHomebridge Allows for 149 Devices per Bridge!!!\", image: getAppImg(\"error\"), state: null, required: true\n            }\n            paragraph \"Devices Selected: (${devCnt})\", image: getAppImg(\"info\"), state: \"complete\"\n        }\n        section(\"History and Device Data:\") {\n            href \"historyPage\", title: \"Command and Event History\", image: getAppImg(\"backup\")\n            href \"deviceDebugPage\", title: \"Device Data Viewer\", image: getAppImg(\"debug\")\n        }\n        section(\"App Preferences:\") {\n            def sDesc = getSetDesc()\n            href \"settingsPage\", title: \"App Settings\", description: sDesc, state: (sDesc?.endsWith(\"modify...\") ? \"complete\" : null), required: false, image: getAppImg(\"settings\")\n            label title: \"App Label (optional)\", description: \"Rename this App\", defaultValue: app?.name, required: false, image: getAppImg(\"name_tag\")\n        }\n        if(devMode()) {\n            section(\"Dev Mode Options\") {\n                input \"sendViaNgrok\", \"bool\", title: \"Communicate with Plugin via Ngrok Http?\", defaultValue: false, submitOnChange: true, image: getAppImg(\"command\")\n                if(sendViaNgrok) { input \"ngrokHttpUrl\", \"text\", title: \"Enter the ngrok code from the url\", required: true, submitOnChange: true }\n            }\n            section(\"Other Settings:\") {\n                input \"restartService\", \"bool\", title: \"Restart Homebridge plugin when you press Save?\", required: false, defaultValue: false, submitOnChange: true, image: getAppImg(\"reset\")\n            }\n        }\n        clearTestDeviceItems()\n    }\n}\n\ndef deviceValidationErrors() {\n    /*\n        NOTE: Determine what we require to determine the thermostat a thermostat so we can support devices like Flair which are custom heat-only thermostats.\n    */\n    Map reqs = [\n        tstat: [ c:[\"Thermostat Operating State\"], a: [r: [\"thermostatOperatingState\"], o: [\"heatingSetpoint\", \"coolingSetpoint\"]] ],\n        tstat_heat: [\n            c: [\"Thermostat Operating State\"],\n            a: [\n                r: [\"thermostatOperatingState\", \"heatingSetpoint\"],\n                o: []\n            ]\n        ]\n    ]\n\n    // if(tstatHeatList || tstatList) {}\n    return reqs\n}\n\ndef defineDevicesPage() {\n    return dynamicPage(name: \"defineDevicesPage\", title: \"\", install: false, uninstall: false) {\n        section(\"Define Specific Categories:\") {\n            paragraph \"NOTE: Please do not select a device here and then again in another input below.\"\n            paragraph \"Each category below will adjust the device attributes to make sure they are recognized as the desired device type under HomeKit\", state: \"complete\"\n            input \"lightList\", \"capability.switch\", title: \"Lights: (${lightList ? lightList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"light_on\")\n            input \"garageList\", \"capability.garageDoorControl\", title: \"Garage Doors: (${garageList ? garageList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"garage_door\")\n            input \"buttonList\", \"capability.button\", title: \"Buttons: (${buttonList ? buttonList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"button\")\n            input \"speakerList\", \"capability.switch\", title: \"Speakers: (${speakerList ? speakerList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"media_player\")\n            input \"shadesList\", \"capability.windowShade\", title: \"Window Shades: (${shadesList ? shadesList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"window_shade\")\n        }\n        section(\"Fans\") {\n            input \"fanList\", \"capability.switch\", title: \"Fans: (${fanList ? fanList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"fan_on\")\n            input \"fan3SpdList\", \"capability.switch\", title: \"Fans (3 Speeds): (${fan3SpdList ? fan3SpdList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"fan_on\")\n            input \"fan4SpdList\", \"capability.switch\", title: \"Fans (4 Speeds): (${fan4SpdList ? fan4SpdList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"fan_on\")\n        }\n        section(\"Thermostats\") {\n            input \"tstatList\", \"capability.thermostat\", title: \"Thermostats: (${tstatList ? tstatList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"thermostat\")\n            input \"tstatHeatList\", \"capability.thermostat\", title: \"Heat Only Thermostats: (${tstatHeatList ? tstatHeatList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"thermostat\")\n        }\n    }\n}\n\ndef deviceSelectPage() {\n    return dynamicPage(name: \"deviceSelectPage\", title: \"\", install: false, uninstall: false) {\n        section(\"All Other Devices:\") {\n            input \"sensorList\", \"capability.sensor\", title: \"Sensors: (${sensorList ? sensorList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"sensors\")\n            input \"switchList\", \"capability.switch\", title: \"Switches: (${switchList ? switchList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"switch\")\n            input \"deviceList\", \"capability.refresh\", title: \"Others: (${deviceList ? deviceList?.size() : 0} Selected)\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"devices2\")\n        }\n    }\n}\n\ndef settingsPage() {\n    return dynamicPage(name: \"settingsPage\", title: \"\", install: false, uninstall: false) {\n        section(\"Logging:\") {\n            input \"showEventLogs\", \"bool\", title: \"Show Events in Live Logs?\", required: false, defaultValue: true, submitOnChange: true, image: getAppImg(\"debug\")\n            input \"showDebugLogs\", \"bool\", title: \"Debug Logging?\", required: false, defaultValue: false, submitOnChange: true, image: getAppImg(\"debug\")\n        }\n        section(\"Reset Token:\") {\n            paragraph title: \"What This?\", \"This will allow you to clear you existing and auth_token and force a new one to be created\"\n            input \"resetAppToken\", \"bool\", title: \"Revoke and Recreate Access Token?\", defaultValue: false, submitOnChange: true, image: getAppImg(\"reset\")\n            if(settings?.resetAppToken) { settingUpdate(\"resetAppToken\", \"false\", \"bool\"); resetAppToken() }\n        }\n    }\n}\n\nprivate resetAppToken() {\n    logWarn(\"resetAppToken | Current Access Token Removed...\")\n    state.remove(\"accessToken\")\n    if(getAccessToken()) {\n        logInfo(\"resetAppToken | New Access Token Created...\")\n    }\n}\n\nprivate resetCapFilters() {\n    List items = settings?.each?.findAll { it?.key?.startsWith(\"remove\") }?.collect { it?.key as String }\n    if(items?.size()) {\n        items?.each { item->\n            settingRemove(item)\n        }\n    }\n}\n\nprivate inputDupeValidation() {\n    Map clnUp = [d: [:], o: [:]]\n    Map items = [\n        d: [\"fanList\": \"Fans\", \"fan3SpdList\": \"Fans (3-Speed)\", \"fan4SpdList\": \"Fans (4-Speed)\", \"buttonList\": \"Buttons\", \"lightList\": \"Lights\", \"shadesList\": \"Window Shadse\", \"speakerList\": \"Speakers\",\n            \"garageList\": \"Garage Doors\", \"tstatList\": \"Thermostat\", \"tstatHeatList\": \"Thermostat (Heat Only)\"\n        ],\n        o: [\"deviceList\": \"Other\", \"sensorList\": \"Sensor\", \"switchList\": \"Switch\"]\n    ]\n    items?.d?.each { k, v->\n        List priItems = (settings?.\"${k}\"?.size()) ? settings?.\"${k}\"?.collect { it?.getLabel() } : null\n        if(priItems) {\n            items?.d?.each { k2, v2->\n                List secItems = (settings?.\"${k2}\"?.size()) ? settings?.\"${k2}\"?.collect { it?.getLabel() } : null\n                if(k != k2 && secItems) {\n                    secItems?.retainAll(priItems)\n                    if(secItems?.size()) {\n                        clnUp?.d[k2] = clnUp?.d[k2] ?: []\n                        clnUp?.d[k2] = (clnUp?.d[k2] + secItems)?.unique()\n                    }\n                }\n            }\n\n            items?.o?.each { k2, v2->\n                List secItems = (settings?.\"${k2}\"?.size()) ? settings?.\"${k2}\"?.collect { it?.getLabel() } : null\n                if(secItems) {\n                    secItems?.retainAll(priItems)\n                    if(secItems?.size()) {\n                        clnUp?.o[k2] = clnUp?.o[k2] ?: []\n                        clnUp?.o[k2] = (clnUp?.o[k2] + secItems)?.unique()\n                    }\n                }\n            }\n        }\n    }\n    String out = \"\"\n    Boolean show = false\n    Boolean first = true\n    if(clnUp?.d?.size()) {\n        show=true\n        clnUp?.d?.each { k,v->\n            out += \"${first ? \"\" : \"\\n\"}${items?.d[k]}:\\n \"\n            out += v?.join(\"\\n \") + \"\\n\"\n            first = false\n        }\n    }\n    if(clnUp?.o?.size()) {\n        show=true\n        clnUp?.o?.each { k,v->\n            out += \"${first ? \"\" : \"\\n\"}${items?.o[k]}:\\n \"\n            out += v?.join(\"\\n \") + \"\\n\"\n            first = false\n        }\n    }\n    if(show && out) {\n        section(\"Duplicate Device Validation:\") {\n            paragraph title: \"Duplicate Devices Found in these Inputs:\", out + \"\\nPlease remove these duplicate items!\", required: true, state: null\n        }\n    }\n}\n\nString getSetDesc() {\n    def s = []\n    if(settings?.showEventLogs == true) s?.push(\"\\u2022 Device Event Logs\")\n    if(settings?.showDebugLogs == true) s?.push(\"\\u2022 Debug Logging\")\n    return s?.size() ? \"${s?.join(\"\\n\")}\\n\\nTap to modify...\" : \"Tap to configure...\"\n}\n\ndef historyPage() {\n    return dynamicPage(name: \"historyPage\", title: \"\", install: false, uninstall: false) {\n        List cHist = getCmdHistory()?.sort {it?.dt}?.reverse()\n        List eHist = getEvtHistory()?.sort {it?.dt}?.reverse()\n        section(\"Last (${cHist?.size()}) Commands:\") {\n            if(cHist?.size()) {\n                cHist?.each { c-> paragraph title: c?.dt, \"Device: ${c?.data?.device}\\nCommand: (${c?.data?.cmd})${c?.data?.value1 ? \"\\nValue1: (${c?.data?.value1})\" : \"\"}${c?.data?.value2 ? \"\\nValue2: (${c?.data?.value2})\" : \"\"}\", state: \"complete\" }\n            } else {paragraph \"No Command History Found...\" }\n        }\n        section(\"Last (${eHist?.size()}) Events:\") {\n            if(eHist?.size()) {\n                eHist?.each { h-> paragraph title: h?.dt, \"Device: ${h?.data?.device}\\nEvent: (${h?.data?.name})${h?.data?.value ? \"\\nValue: (${h?.data?.value})\" : \"\"}\", state: \"complete\" }\n            } else {paragraph \"No Event History Found...\" }\n        }\n    }\n}\n\ndef capFilterPage() {\n    return dynamicPage(name: \"capFilterPage\", title: \"Filter out capabilities\", install: false, uninstall: false) {\n        section(\"Restrict Temp Device Creation\") {\n            input \"noTemp\", \"bool\", title: \"Remove Temp from All Contacts and Water Sensors?\", required: false, defaultValue: false, submitOnChange: true\n            if(settings?.noTemp) {\n                input \"sensorAllowTemp\", \"capability.sensor\", title: \"Allow Temp on these Sensors\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"temperature\")\n            }\n        }\n        section(\"Remove Capabilities from Devices\") {\n            paragraph \"This will allow you to filter out certain capabilities from creating unwanted devices under HomeKit\"\n            input \"removeAcceleration\", \"capability.accelerationSensor\", title: \"Remove Acceleration from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"acceleration\")\n            input \"removeBattery\", \"capability.battery\", title: \"Remove Battery from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"battery\")\n            input \"removeButton\", \"capability.button\", title: \"Remove Buttons from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"button\")\n            input \"removeContact\", \"capability.contactSensor\", title: \"Remove Contact from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"contact\")\n            // input \"removeEnergy\", \"capability.energyMeter\", title: \"Remove Energy Meter from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"power\")\n            input \"removeHumidity\", \"capability.relativeHumidityMeasurement\", title: \"Remove Humidity from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"humidity\")\n            input \"removeIlluminance\", \"capability.illuminanceMeasurement\", title: \"Remove Illuminance from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"illuminance\")\n            input \"removeLevel\", \"capability.switchLevel\", title: \"Remove Level from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"speed_knob\")\n            input \"removeLock\", \"capability.lock\", title: \"Remove Lock from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"lock\")\n            input \"removeMotion\", \"capability.motionSensor\", title: \"Remove Motion from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"motion\")\n            // input \"removePower\", \"capability.powerMeter\", title: \"Remove Power Meter from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"power\")\n            input \"removePresence\", \"capability.presenceSensor\", title: \"Remove Presence from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"presence\")\n            input \"removeSwitch\", \"capability.switch\", title: \"Remove Switch from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"switch\")\n            input \"removeTamper\", \"capability.tamperAlert\", title: \"Remove Tamper from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"tamper\")\n            input \"removeTemp\", \"capability.temperatureMeasurement\", title: \"Remove Temperature from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"temperature\")\n            input \"removeValve\", \"capability.valve\", title: \"Remove Valve from these Devices\", multiple: true, submitOnChange: true, required: false, image: getAppImg(\"valve\")\n        }\n        section(\"Filter Reset:\", hideable: true, hidden: true) {\n            input \"resetCapFilters\", \"bool\", title: \"Clear All Selected Removal Filters?\", required: false, defaultValue: false, submitOnChange: true, image: getAppImg(\"reset\")\n            if(settings?.resetCapFilters) { settingUpdate(\"resetCapFilters\", \"false\", \"bool\"); resetCapFilters() }\n        }\n    }\n}\n\ndef virtDevicePage() {\n    return dynamicPage(name: \"virtDevicePage\", title: \"\", install: false, uninstall: false) {\n        section(\"Create Devices for Modes in HomeKit?\") {\n            paragraph title: \"What are these for?\", \"A virtual switch will be created for each mode in HomeKit.\\nThe switch will be ON when that mode is active.\", state: \"complete\", image: getAppImg(\"info\")\n            def modes = location?.modes?.sort{it?.name}?.collect { [(it?.id):it?.name] }\n            input \"modeList\", \"enum\", title: \"Create Devices for these Modes\", required: false, multiple: true, options: modes, submitOnChange: true, image: getAppImg(\"mode\")\n        }\n        section(\"Create Devices for Routines in HomeKit?\") {\n            paragraph title: \"What are these?\", \"A virtual device will be created for each routine in HomeKit.\\nThese are very useful for use in Home Kit scenes\", state: \"complete\", image: getAppImg(\"info\")\n            def routines = location.helloHome?.getPhrases()?.sort { it?.label }?.collect { [(it?.id):it?.label] }\n            input \"routineList\", \"enum\", title: \"Create Devices for these Routines\", required: false, multiple: true, options: routines, submitOnChange: true, image: getAppImg(\"routine\")\n        }\n    }\n}\n\ndef donationPage() {\n    return dynamicPage(name: \"donationPage\", title: \"\", nextPage: \"mainPage\", install: false, uninstall: false) {\n        section(\"\") {\n            def str = \"\"\n            str += \"Hello User, \\n\\nPlease forgive the interuption but it's been 30 days since you installed/updated this SmartApp and I wanted to present you with this one time reminder that donations are accepted (We do not require them).\"\n            str += \"\\n\\nIf you have been enjoying the software and devices please remember that we have spent thousand's of hours of our spare time working on features and stability for those applications and devices.\"\n            str += \"\\n\\nIf you have already donated, thank you very much for your support!\"\n\n            str += \"\\n\\nIf you are just not interested or have already donated please ignore this message and toggle the setting below\"\n            str += \"\\n\\nThanks again for using Homebridge SmartThings\"\n            paragraph str, required: true, state: null\n            input \"sentDonation\", \"bool\", title: \"Already Donated?\", defaultValue: false, submitOnChange: true\n            href url: textDonateLink(), style: \"external\", required: false, title: \"Donations\", description: \"Tap to open in browser\", state: \"complete\", image: getAppImg(\"donate\")\n        }\n        updInstData(\"shownDonation\", true)\n    }\n}\n\ndef confirmPage() {\n    return dynamicPage(name: \"confirmPage\", title: \"Confirmation Page\", install: true, uninstall:true) {\n        section() {\n            paragraph \"Restarting the service is no longer required to apply any device changes under homekit.\\n\\nThe service will refresh your devices about 15-20 seconds after Pressing Done/Save.\", state: \"complete\", image: getAppImg(\"info\")\n        }\n    }\n}\n\ndef deviceDebugPage() {\n    return dynamicPage(name: \"deviceDebugPage\", title: \"\", install: false, uninstall: false) {\n        section(\"All Other Devices:\") {\n            paragraph \"Have a device that's not working under homekit like you want?\\nSelect a device from one of the inputs below and it will show you all data about the device.\", state: \"complete\", image: getAppImg(\"info\")\n            if(!debug_switch && !debug_other && !debug_garage && !debug_tstat)\n                input \"debug_sensor\", \"capability.sensor\", title: \"Sensors: \", multiple: false, submitOnChange: true, required: false, image: getAppImg(\"sensors\")\n            if(!debug_sensor && !debug_other && !debug_garage && !debug_tstat)\n                input \"debug_switch\", \"capability.actuator\", title: \"Switches: \", multiple: false, submitOnChange: true, required: false, image: getAppImg(\"switch\")\n            if(!debug_switch && !debug_sensor && !debug_garage && !debug_tstat)\n                input \"debug_other\", \"capability.refresh\", title: \"Others Devices: \", multiple: false, submitOnChange: true, required: false, image: getAppImg(\"devices2\")\n            if(!debug_sensor && !debug_other && !debug_switch)\n                input \"debug_garage\", \"capability.garageDoorControl\", title: \"Garage Doors: \", multiple: false, submitOnChange: true, required: false, image: getAppImg(\"garage_door\")\n            if(!debug_sensor && !debug_other && !debug_switch && !debug_garage)\n                input \"debug_tstat\", \"capability.thermostat\", title: \"Thermostats: \", multiple: false, submitOnChange: true, required: false, image: getAppImg(\"thermostat\")\n            if(debug_other || debug_sensor || debug_switch || debug_garage || debug_tstat) {\n                href url: getAppEndpointUrl(\"deviceDebug\"), style: \"embedded\", required: false, title: \"Tap here to view Device Data...\", description: \"\", state: \"complete\", image: getAppImg(\"info\")\n            }\n        }\n    }\n}\n\npublic clearTestDeviceItems() {\n    settingRemove(\"debug_sensor\")\n    settingRemove(\"debug_switch\")\n    settingRemove(\"debug_other\")\n    settingRemove(\"debug_garage\")\n    settingRemove(\"debug_tstat\")\n}\n\ndef viewDeviceDebug() {\n    def sDev = null;\n    if(debug_other) sDev = debug_other\n    if(debug_sensor) sDev = debug_sensor\n    if(debug_switch) sDev = debug_switch\n    if(debug_garage) sDev = debug_garage\n    if(debug_tstat)  sDev = debug_tstat\n    def json = new groovy.json.JsonOutput().toJson(getDeviceDebugMap(sDev))\n    def jsonStr = new groovy.json.JsonOutput().prettyPrint(json)\n    render contentType: \"application/json\", data: jsonStr\n}\n\ndef getDeviceDebugMap(dev) {\n    def r = \"No Data Returned\"\n    if(dev) {\n        try {\n            r = [:]\n            r?.name = dev?.displayName?.toString()?.replaceAll(\"[#\\$()!%&@^']\", \"\");\n            r?.basename = dev?.getName();\n            r?.deviceid = dev?.getId();\n            r?.status = dev?.getStatus();\n            r?.manufacturer = dev?.getManufacturerName() ?: \"Unknown\";\n            r?.model = dev?.getModelName() ?: dev?.getTypeName();\n            r?.deviceNetworkId = dev?.getDeviceNetworkId();\n            r?.lastActivity = dev?.getLastActivity() ?: null;\n            r?.capabilities = dev?.capabilities?.collect { it?.name as String }?.unique()?.sort() ?: [];\n            r?.commands = dev?.supportedCommands?.collect { it?.name as String }?.unique()?.sort() ?: [];\n            r?.customflags = getDeviceFlags(dev) ?: [];\n            r?.attributes = [:];\n            r?.eventHistory = dev?.eventsSince(new Date() - 1, [max: 20])?.collect { \"${it?.date} | [${it?.name}] | (${it?.value}${it?.unit ? \" ${it?.unit}\" : \"\"})\" };\n            dev?.supportedAttributes?.collect { it?.name as String }?.unique()?.sort()?.each { r?.attributes[it] = dev?.currentValue(it as String); };\n        } catch(ex) {\n            logError(\"Error while generating device data: \", ex);\n        }\n    }\n    return r\n}\n\ndef getDeviceCnt(phyOnly=false) {\n    List devices = []\n    List items = deviceSettingKeys()?.collect { it?.key as String }\n    items?.each { item -> if(settings[item]?.size() > 0) devices = devices + settings[item] }\n    if(!phyOnly) {\n        [\"modeList\", \"routineList\"]?.each { item->\n            if(settings[item]?.size() > 0) devices = devices + settings[item]\n        }\n    }\n    return devices?.unique()?.size() ?: 0\n}\n\ndef installed() {\n    logDebug(\"${app.name} | installed() has been called...\")\n    state?.isInstalled = true\n    state?.installData = [initVer: appVersion(), dt: getDtNow().toString(), updatedDt: \"Not Set\", showDonation: false, shownChgLog: true]\n    initialize()\n}\n\ndef updated() {\n    logDebug(\"${app.name} | updated() has been called...\")\n    state?.isInstalled = true\n    if(!state?.installData) { state?.installData = [initVer: appVersion(), dt: getDtNow().toString(), updatedDt: getDtNow().toString(), shownDonation: false] }\n    unsubscribe()\n    stateCleanup()\n    initialize()\n}\n\ndef initialize() {\n    if(getAccessToken()) {\n        subscribeToEvts()\n        runEvery5Minutes(\"healthCheck\")\n    } else { logError(\"initialize error: Unable to get or generate smartapp access token\") }\n}\n\ndef getAccessToken() {\n    try {\n        if(!atomicState?.accessToken) {\n            atomicState?.accessToken = createAccessToken();\n            logWarn(\"SmartApp Access Token Missing... Generating New Token!!!\")\n            return true;\n        }\n        return true\n    } catch (ex) {\n        def msg = \"Error: OAuth is not Enabled for ${appName()}!. Please click remove and Enable Oauth under the SmartApp App Settings in the IDE\"\n        logError(\"getAccessToken Exception: ${msg}\")\n        return false\n    }\n}\n\nprivate subscribeToEvts() {\n    runIn(4, \"registerDevices\")\n    logInfo(\"Starting Device Subscription Process\")\n    if(settings?.addSecurityDevice) {\n        subscribe(location, \"alarmSystemStatus\", changeHandler)\n    }\n    if(settings?.modeList) {\n        logDebug(\"Registering (${settings?.modeList?.size() ?: 0}) Virtual Mode Devices\")\n        subscribe(location, \"mode\", changeHandler)\n        if(state?.lastMode == null) { state?.lastMode = location?.mode?.toString() }\n    }\n    state?.subscriptionRenewed = 0\n    subscribe(app, onAppTouch)\n    if(settings?.sendCmdViaHubaction != false) { subscribe(location, null, lanEventHandler, [filterEvents:false]) }\n    if(settings?.routineList) {\n        logDebug(\"Registering (${settings?.routineList?.size() ?: 0}) Virtual Routine Devices\")\n        subscribe(location, \"routineExecuted\", changeHandler)\n    }\n}\n\nprivate healthCheck() {\n    checkVersionData()\n    if(checkIfCodeUpdated()) {\n        logWarn(\"Code Version Change Detected... Health Check will occur on next cycle.\")\n        return\n    }\n}\n\nprivate checkIfCodeUpdated() {\n    logDebug(\"Code versions: ${state?.codeVersions}\")\n    if(state?.codeVersions) {\n        if(state?.codeVersions?.mainApp != appVersion()) {\n            checkVersionData(true)\n            state?.pollBlocked = true\n            updCodeVerMap(\"mainApp\", appVersion())\n            Map iData = atomicState?.installData ?: [:]\n            iData[\"updatedDt\"] = getDtNow().toString()\n            iData[\"shownChgLog\"] = false\n            if(iData?.shownDonation == null) {\n                iData[\"shownDonation\"] = false\n            }\n            atomicState?.installData = iData\n            logInfo(\"Code Version Change Detected... | Re-Initializing SmartApp in 5 seconds\")\n            return true\n        }\n    }\n    return false\n}\n\nprivate stateCleanup() {\n    List removeItems = []\n    if(state?.directIP && state?.directPort) {\n        state?.pluginDetails = [\n            directIP: state?.directIP,\n            directPort: state?.directPort\n        ]\n        removeItems?.push(\"directIP\")\n        removeItems?.push(\"directPort\")\n    }\n    removeItems?.each { if(state?.containsKey(it)) state?.remove(it) }\n}\n\ndef onAppTouch(event) {\n    updated()\n}\n\ndef renderDevices() {\n    Map devMap = [:]\n    List devList = []\n    List items = deviceSettingKeys()?.collect { it?.key as String }\n    items = items+[\"modeList\", \"routineList\"]\n    items?.each { item ->\n        if(settings[item]?.size()) {\n            settings[item]?.each { dev->\n                try {\n                    Map devObj = getDeviceData(item, dev) ?: [:]\n                    if(devObj?.size()) { devMap[dev] = devObj }\n                } catch (e) {\n                    logError(\"Device (${dev?.displayName}) Render Exception: ${ex.message}\")\n                }\n            }\n        }\n    }\n    if(settings?.addSecurityDevice == true) { devList?.push(getSecurityDevice()) }\n    if(devMap?.size()) { devMap?.sort{ it?.value?.name }?.each { k,v-> devList?.push(v) } }\n    return devList\n}\n\ndef getDeviceData(type, sItem) {\n    // log.debug \"getDeviceData($type, $sItem)\"\n    String curType = null\n    String devId = sItem\n    Boolean isVirtual = false\n    String firmware = null\n    String name = null\n    Map optFlags = [:]\n    def attrVal = null\n    def obj = null\n    switch(type) {\n        case \"routineList\":\n            isVirtual = true\n            curType = \"Routine\"\n            optFlags[\"virtual_routine\"] = 1\n            obj = getRoutineById(sItem)\n            if(obj) {\n                name = \"Routine - \" + obj?.label\n                attrVal = \"off\"\n            }\n            break\n        case \"modeList\":\n            isVirtual = true\n            curType = \"Mode\"\n            optFlags[\"virtual_mode\"] = 1\n            obj = getModeById(sItem)\n            if(obj) {\n                name = \"Mode - \" + obj?.name\n                attrVal = modeSwitchState(obj?.name)\n            }\n            break\n        default:\n            curType = \"device\"\n            obj = sItem\n            // Define firmware variable and initialize it out of device handler attribute`\n            try {\n                if (sItem?.hasAttribute(\"firmware\")) { firmware = sItem?.currentValue(\"firmware\")?.toString() }\n            } catch (ex) { firmware = null }\n            break\n    }\n    if(curType && obj) {\n        return [\n            name: !isVirtual ? sItem?.displayName?.toString()?.replaceAll(\"[#\\$()!%&@^']\", \"\") : name?.toString()?.replaceAll(\"[#\\$()!%&@^']\", \"\"),\n            basename: !isVirtual ? sItem?.name : name,\n            deviceid: !isVirtual ? sItem?.id : devId,\n            status: !isVirtual ? sItem?.status : \"Online\",\n            manufacturerName: (!isVirtual ? sItem?.getManufacturerName() : pluginName()) ?: pluginName(),\n            modelName: !isVirtual ? (sItem?.getModelName() ?: sItem?.getTypeName()) : \"${curType} Device\",\n            serialNumber: !isVirtual ? sItem?.getDeviceNetworkId() : \"${curType}${devId}\",\n            firmwareVersion: firmware ?: \"1.0.0\",\n            lastTime: !isVirtual ? (sItem?.getLastActivity() ?: null) : now(),\n            capabilities: !isVirtual ? deviceCapabilityList(sItem) : [\"${curType}\": 1],\n            commands: !isVirtual ? deviceCommandList(sItem) : [on: 1],\n            deviceflags: !isVirtual ? getDeviceFlags(sItem) : optFlags,\n            attributes: !isVirtual ? deviceAttributeList(sItem) : [\"switch\": attrVal]\n        ]\n    }\n    return null\n}\n\nString modeSwitchState(String mode) {\n    return location?.mode?.toString() == mode ? \"on\" : \"off\"\n}\n\ndef getSecurityDevice() {\n    return [\n        name: \"Security Alarm\",\n        basename: \"Security Alarm\",\n        deviceid: \"alarmSystemStatus_${location?.id}\",\n        status: \"ACTIVE\",\n        manufacturerName: pluginName(),\n        modelName: \"Security System\",\n        serialNumber: \"SHM\",\n        firmwareVersion: \"1.0.0\",\n        lastTime: null,\n        capabilities: [\"Alarm System Status\": 1, \"Alarm\": 1],\n        commands: [],\n        attributes: [\"alarmSystemStatus\": getSecurityStatus()]\n    ]\n}\n\ndef getDeviceFlags(device) {\n    Map opts = [:]\n    if(settings?.fan3SpdList?.find { it?.id == device?.id }) {\n        opts[\"fan_3_spd\"] = 1\n    }\n    if(settings?.fan4SpdList?.find { it?.id == device?.id }) {\n        opts[\"fan_4_spd\"] = 1\n    }\n    // if(opts?.size()) log.debug \"opts: ${opts}\"\n    return opts\n}\n\ndef findDevice(dev_id) {\n    List allDevs = []\n    deviceSettingKeys()?.collect { it?.key as String }?.each { key-> allDevs = allDevs + (settings?.\"${key}\" ?: []) }\n    return allDevs?.find { it?.id == dev_id } ?: null\n}\n\ndef authError() {\n    return [error: \"Permission denied\"]\n}\n\ndef getSecurityStatus(retInt=false) {\n    def cur = location.currentState(\"alarmSystemStatus\")?.value\n    def inc = getShmIncidents()\n    if(inc != null && inc?.size()) { cur = 'alarm_active' }\n    if(retInt) {\n        switch (cur) {\n            case 'stay':\n                return 0\n            case 'away':\n                return 1\n            case 'night':\n                return 2\n            case 'off':\n                return 3\n            case 'alarm_active':\n                return 4\n        }\n    } else { return cur ?: \"disarmed\" }\n}\n\nprivate setSecurityMode(mode) {\n    logInfo(\"Setting the Smart Home Monitor Mode to (${mode})...\")\n    sendLocationEvent(name: 'alarmSystemStatus', value: mode.toString())\n}\n\ndef renderConfig() {\n    Map jsonMap = [\n        platform: pluginName(),\n        name: pluginName(),\n        app_url: apiServerUrl(\"/api/smartapps/installations/\"),\n        app_id: app?.getId(),\n        access_token: atomicState?.accessToken,\n        temperature_unit: settings?.temp_unit ?: location?.temperatureScale,\n        validateTokenId: false,\n        logConfig: [\n            debug: false,\n            showChanges: true,\n            hideTimestamp: false,\n            hideNamePrefix: false,\n            file: [\n                enabled: true\n            ]\n        ]\n    ]\n    def configJson = new groovy.json.JsonOutput().toJson(jsonMap)\n    def configString = new groovy.json.JsonOutput().prettyPrint(configJson)\n    render contentType: \"text/plain\", data: configString\n}\n\ndef renderLocation() {\n    return [\n        latitude: location?.latitude,\n        longitude: location?.longitude,\n        mode: location?.mode,\n        name: location?.name,\n        temperature_scale: settings?.temp_unit ?: location?.temperatureScale,\n        zip_code: location?.zipCode,\n        hubIP: location?.hubs[0]?.localIP,\n        local_commands: false, //(settings?.sendCmdViaHubaction != false),\n        app_version: appVersion()\n    ]\n}\n\ndef CommandReply(statusOut, messageOut) {\n    def replyJson = new groovy.json.JsonOutput().toJson([status: statusOut, message: messageOut])\n    render contentType: \"application/json\", data: replyJson\n}\n\nprivate getHttpHeaders(headers) {\n  def obj = [:]\n    new String(headers.decodeBase64()).split(\"\\r\\n\")?.each {param ->\n    def nameAndValue = param.split(\":\")\n    obj[nameAndValue[0]] = (nameAndValue.length == 1) ? \"\" : nameAndValue[1].trim()\n  }\n  return obj\n}\n\ndef lanEventHandler(evt) {\n    // log.trace \"lanEventHandler...\"\n    try {\n        def evtData = parseLanMessage(evt?.description?.toString())\n        Map headers = evtData?.headers\n        def slurper = new groovy.json.JsonSlurper()\n        def body = evtData?.body ? slurper?.parseText(evtData?.body as String) : null\n        // log.trace \"lanEventHandler... | headers: ${headerMap}\"\n        // log.debug \"headers: $headers\"\n        // log.debug \"body: $body\"\n        Map msgData = [:]\n        if (headers?.size()) {\n            String evtSrc = (headers?.evtsource || body?.evtsource) ? (headers?.evtsource ?: body?.evtsource) : null\n            if (evtSrc && evtSrc?.startsWith(\"Homebridge_${pluginName()}_${app?.getId()}\")) {\n                String evtType = (headers?.evttype || body?.evttype) ? (headers?.evttype ?: body?.evttype) : null\n                if (body && evtType) {\n                    switch(evtType) {\n                        case \"hkCommand\":\n                            // log.trace \"hkCommand($msgData)\"\n                            def val1 = body?.values?.value1 ?: null\n                            def val2 = body?.values?.value2 ?: null\n                            processCmd(body?.deviceid, body?.command, val1, val2, true)\n                            break\n                        case \"enableDirect\":\n                            // log.trace \"enableDirect($msgData)\"\n                            state?.pluginDetails = [\n                                directIP: body?.ip,\n                                directPort: body?.port,\n                                version: body?.version ?: null\n                            ]\n                            updCodeVerMap(\"plugin\", body?.version ?: null)\n                            activateDirectUpdates(true)\n                            break\n                        case \"attrUpdStatus\":\n                            // if(body?.evtStatus && body?.evtStatus != \"OK\") { log.warn \"Attribute Update Failed | Device: ${body?.evtDevice} | Attribute: ${body?.evtAttr}\" }\n                            break\n                        default:\n                            break\n                    }\n                }\n            }\n        }\n    } catch (ex) {\n        logError \"lanEventHandler Exception:\", ex\n    }\n}\n\ndef deviceCommand() {\n    // log.info(\"Command Request: $params\")\n    def val1 = request?.JSON?.value1 ?: null\n    def val2 = request?.JSON?.value2 ?: null\n    processCmd(params?.id, params?.command, val1, val2)\n}\n\nprivate processCmd(devId, cmd, value1, value2, local=false) {\n    logInfo(\"Process Command${local ? \"(LOCAL)\" : \"\"} | DeviceId: $devId | Command: ($cmd)${value1 ? \" | Param1: ($value1)\" : \"\"}${value2 ? \" | Param2: ($value2)\" : \"\"}\")\n    def device = findDevice(devId)\n    def command = cmd\n    if(settings?.addSecurityDevice != false && devId == \"alarmSystemStatus_${location?.id}\") {\n        setSecurityMode(command)\n        CommandReply(\"Success\", \"Security Alarm, Command $command\")\n    }  else if (settings?.modeList && command == \"mode\" && devId) {\n        logDebug(\"Virtual Mode Received: ${devId}\")\n        changeMode(devId)\n        CommandReply(\"Success\", \"Mode Device, Command $command\")\n    } else if (settings?.routineList && command == \"routine\" && devId) {\n        logDebug(\"Virtual Routine Received: ${devId}\")\n        runRoutine(devId)\n        CommandReply(\"Success\", \"Routine Device, Command $command\")\n    } else {\n        if (!device) {\n            logError(\"Device Not Found\")\n            CommandReply(\"Failure\", \"Device Not Found\")\n        } else if (!device?.hasCommand(command as String)) {\n            logError(\"Device ${device.displayName} does not have the command $command\")\n            CommandReply(\"Failure\", \"Device ${device.displayName} does not have the command $command\")\n        } else {\n            try {\n                if (value2 != null) {\n                    device?.\"$command\"(value1,value2)\n                    logInfo(\"Command Successful for Device ${device.displayName} | Command ${command}($value1, $value2)\")\n                } else if (value1 != null) {\n                    device?.\"$command\"(value1)\n                    logInfo(\"Command Successful for Device ${device.displayName} | Command ${command}($value1)\")\n                } else {\n                    device?.\"$command\"()\n                    logInfo(\"Command Successful for Device ${device.displayName} | Command ${command}()\")\n                }\n                CommandReply(\"Success\", \"Device ${device.displayName} | Command ${command}()\")\n                logCmd([cmd: command, device: device?.displayName, value1: value1, value2: value2])\n            } catch (e) {\n                logError(\"Error Occurred for Device ${device.displayName} | Command ${command}()\")\n                CommandReply(\"Failure\", \"Error Occurred For Device ${device.displayName} | Command ${command}()\")\n            }\n        }\n    }\n\n}\n\ndef changeMode(modeId) {\n    if(modeId) {\n        def mode = findVirtModeDevice(modeId)\n        if(mode) {\n            logInfo(\"Setting the Location Mode to (${mode})...\")\n            setLocationMode(mode)\n            state?.lastMode = mode\n        } else { logError(\"Unable to find a matching mode for the id: ${modeId}\") }\n    }\n}\n\ndef runRoutine(rtId) {\n    if(rtId) {\n        def rt = findVirtRoutineDevice(rtId)\n        if(rt?.label) {\n            logInfo(\"Executing the (${rt?.label}) Routine...\")\n            location?.helloHome?.execute(rt?.label)\n        } else { logError(\"Unable to find a matching routine for the id: ${rtId}\") }\n    }\n}\n\ndef deviceAttribute() {\n    def device = findDevice(params?.id)\n    def attribute = params?.attribute\n    if (!device) {\n        httpError(404, \"Device not found\")\n    } else {\n        return [currentValue: device?.currentValue(attribute)]\n    }\n}\n\ndef findVirtModeDevice(id) {\n    return getModeById(id) ?: null\n}\n\ndef findVirtRoutineDevice(id) {\n    return getRoutineById(id) ?: null\n}\n\ndef deviceQuery() {\n    log.trace \"deviceQuery(${params?.id}\"\n    def device = findDevice(params?.id)\n    if (!device) {\n        def mode = findVirtModeDevice(params?.id)\n        def routine = findVirtModeDevice(params?.id)\n        def obj = mode ? mode : routine ?: null\n        if(!obj) {\n            device = null\n            httpError(404, \"Device not found\")\n        } else {\n            def name = routine ? obj?.label : obj?.name\n            def type = routine ? \"Routine\" : \"Mode\"\n            def attrVal = routine ? \"off\" : modeSwitchState(obj?.name)\n            try {\n                deviceData?.push([\n                    name: name,\n                    deviceid: params?.id,\n                    capabilities: [\"${type}\": 1],\n                    commands: [on:1],\n                    attributes: [\"switch\": attrVal]\n                ])\n            } catch (e) {\n                logError(\"Error Occurred Parsing ${item} ${type} ${name}, Error: ${ex}\")\n            }\n        }\n    }\n\n    if (result) {\n        def jsonData = [\n            name: device.displayName,\n            deviceid: device.id,\n            capabilities: deviceCapabilityList(device),\n            commands: deviceCommandList(device),\n            attributes: deviceAttributeList(device)\n        ]\n        def resultJson = new groovy.json.JsonOutput().toJson(jsonData)\n        render contentType: \"application/json\", data: resultJson\n    }\n}\n\ndef deviceCapabilityList(device) {\n    String devId = device?.getId()\n    def capItems = device?.capabilities?.findAll{ !(it?.name in ignoreLists()?.capabilities) }?.collectEntries { capability-> [ (capability?.name as String):1 ] }\n    if(isDeviceInInput(\"lightList\", device?.id)) {\n        capItems[\"LightBulb\"] = 1\n    }\n    if(isDeviceInInput(\"buttonList\", device?.id)) {\n        capItems[\"Button\"] = 1\n    }\n    if(isDeviceInInput(\"fanList\", device?.id)) {\n        capItems[\"Fan\"] = 1\n    }\n    if(isDeviceInInput(\"speakerList\", device?.id)) {\n        capItems[\"Speaker\"] = 1\n    }\n    if(isDeviceInInput(\"shadesList\", device?.id)) {\n        capItems[\"Window Shade\"] = 1\n    }\n    if(isDeviceInInput(\"garageList\", device?.id)) {\n        capItems[\"Garage Door Control\"] = 1\n    }\n    if(isDeviceInInput(\"tstatList\", device?.id)) {\n        capItems[\"Thermostat\"] = 1\n        capItems[\"Thermostat Operating State\"] = 1\n    }\n    if(isDeviceInInput(\"tstatHeatList\", device?.id)) {\n        capItems[\"Thermostat\"] = 1\n        capItems[\"Thermostat Operating State\"] = 1\n        capItems?.remove(\"Thermostat Cooling Setpoint\")\n    }\n    if(settings?.noTemp && capItems[\"Temperature Measurement\"] && (capItems[\"Contact Sensor\"] || capItems[\"Water Sensor\"])) {\n        Boolean remTemp = true\n        if(settings?.sensorAllowTemp && isDeviceInInput(\"sensorAllowTemp\", device?.id)) remTemp = false\n        if(remTemp) { capItems?.remove(\"Temperature Measurement\") }\n    }\n\n    //This will filter out selected capabilities from the devices selected in filtering inputs.\n    Map remCaps = [\n       \"Acceleration\": \"Acceleration Sensor\", \"Battery\": \"Battery\", \"Button\": \"Button\", \"Contact\": \"Contact Sensor\", \"Energy\": \"Energy Meter\", \"Humidity\": \"Relative Humidity Measurement\",\n       \"Illuminance\": \"Illuminance Measurement\", \"Level\": \"Switch Level\", \"Lock\": \"Lock\", \"Motion\": \"Motion Sensor\", \"Power\": \"Power Meter\", \"Presence\": \"Presence Sensor\", \"Switch\": \"Switch\",\n       \"Tamper\": \"Tamper Alert\", \"Temp\": \"Temperature Measurement\", \"Valve\": \"Valve\"\n    ]\n    List remKeys = settings?.findAll { it?.key?.toString()?.startsWith(\"remove\") && it?.value != null }?.collect { it?.key as String } ?: []\n    remKeys?.each { k->\n        String capName = k?.replaceAll(\"remove\", \"\")\n        if(remCaps[capName] && capItems[remCaps[capName]] && isDeviceInInput(k, device?.id)) { capItems?.remove(remCaps[capName]);  if(showDebugLogs) { logDebug(\"Filtering ${capName}\"); } }\n    }\n    return capItems\n}\n\ndef deviceCommandList(device) {\n    def cmds = device?.supportedCommands?.findAll { !(it?.name in ignoreLists()?.commands) }?.collectEntries { c-> [ (c?.name): 1 ] }\n    if(isDeviceInInput(\"tstatHeatList\", device?.id)) { cmds?.remove(\"setCoolingSetpoint\"); cmds?.remove(\"auto\"); cmds?.remove(\"cool\"); }\n    return cmds\n}\n\ndef deviceAttributeList(device) {\n    def atts = device?.supportedAttributes?.findAll { !(it?.name in ignoreLists()?.attributes) }?.collectEntries { attribute->\n        try {\n            [(attribute?.name): device?.currentValue(attribute?.name)]\n        } catch(e) {\n            [(attribute?.name): null]\n        }\n    }\n    if(isDeviceInInput(\"tstatHeatList\", device?.id)) { atts?.remove(\"coolingSetpoint\"); atts?.remove(\"coolingSetpointRange\"); }\n    return atts\n}\n\nString getAppEndpointUrl(subPath) { return \"${apiServerUrl(\"/api/smartapps/installations/${app.id}${subPath ? \"/${subPath}\" : \"\"}?access_token=${atomicState?.accessToken}\")}\" }\n\ndef getAllData() {\n    state?.subscriptionRenewed = now()\n    state?.devchanges = []\n    def deviceJson = new groovy.json.JsonOutput().toJson([location: renderLocation(), deviceList: renderDevices()])\n    updTsVal(\"lastDeviceDataQueryDt\")\n    render contentType: \"application/json\", data: deviceJson\n}\n\ndef checkForMissedRegistration() {\n    def mr = atomicState?.pendingDeviceRegistrations ?: []\n}\n\nMap deviceSettingKeys() {\n    return [\n        \"fanList\": \"Fan Devices\", \"fan3SpdList\": \"Fans (3Spd) Devices\", \"fan4SpdList\": \"Fans (4Spd) Devices\", \"buttonList\": \"Button Devices\", \"deviceList\": \"Other Devices\",\n        \"sensorList\": \"Sensor Devices\", \"speakerList\": \"Speaker Devices\", \"switchList\": \"Switch Devices\", \"lightList\": \"Light Devices\", \"shadesList\": \"Window Shade Devices\",\n        \"garageList\": \"Garage Devices\", \"tstatList\":\"T-Stat Devices\", \"tstatHeatList\": \"T-Stat Devices (Heat)\"\n    ]\n}\n\nprivate registerDevicesTest() {\n    def strtDt = now()\n    Boolean done = false\n    Boolean sched = false\n    List keysToRegister = atomicState?.pendingDeviceRegistrations ?: []\n    Integer regRnd = atomicState?.pendingDeviceRegistrationRnd ?: 1\n    if(!keysToRegister?.size()) {\n        deviceSettingKeys()?.each { k,v ->\n            if(settings?.\"${k}\"?.size()>0) keysToRegister?.push(k)\n        }\n    }\n    if(keysToRegister?.size()) {\n        List keyToRemove = []\n        List devItems = []\n        log.trace \"(${keysToRegister?.size()}) Device Groups Pending Event Registration...\"\n        keysToRegister?.each { key->\n            if(devItems?.size() > 30) {\n                sched = true\n            } else {\n                settings?.\"${key}\"?.each { dev->\n                    devItems?.push(dev)\n                    registerChangeHandler(dev)\n                }\n                keyToRemove?.push(key)\n            }\n        }\n        keysToRegister -= keyToRemove\n\n        if(sched) {\n            log.trace \"Device Registration Round (${regRnd}) Completed | Registered (${devItems?.size()}) Devices | Starting Next Round in 4 seconds... | Process Time: (${now()-strtDt}ms)\"\n            atomicState?.pendingDeviceRegistrations = keysToRegister\n            atomicState?.pendingDeviceRegistrationRnd = regRnd+1\n            runIn(3, \"registerDevices\")\n        } else {\n            done = true\n            log.trace \"Device Registration Round (${regRnd}) Completed | Registered (${devItems?.size()}) Devices... | Process Time: (${now()-strtDt}ms)\"\n        }\n    }\n    if(done) {\n        log.trace \"Device Registration Process Completed | Registered (${getDeviceCnt(true)} Devices) | Process Time: (${now()-strtDt}ms) | Rounds: ${atomicState?.pendingDeviceRegistrationRnd}\"\n        log.info \"-----------------------------------------------\"\n        unschedule(\"registerDevices\")\n        state?.remove(\"pendingDeviceRegistrations\");\n        state?.remove(\"pendingDeviceRegistrationRnd\")\n\n        if(settings?.restartService == true) {\n            logWarn(\"Sent Request to Homebridge Service to Stop... Service should restart automatically\")\n            attemptServiceRestart()\n            settingUpdate(\"restartService\", \"false\", \"bool\")\n        }\n        runIn(10, \"updateServicePrefs\")\n        runIn(15, \"sendDeviceRefreshCmd\")\n    }\n}\n\ndef registerDevices() {\n    //This has to be done at startup because it takes too long for a normal command.\n    [\"lightList\": \"Light Devices\", \"fanList\": \"Fan Devices\", \"fan3SpdList\": \"Fans (3SPD) Devices\", \"fan4SpdList\": \"Fans (4SPD) Devices\", \"buttonList\": \"Button Devices\"]?.each { k,v->\n        logDebug(\"Registering (${settings?.\"${k}\"?.size() ?: 0}) ${v}\")\n        registerChangeHandler(settings?.\"${k}\")\n    }\n    runIn(3, \"registerDevices2\")\n}\n\ndef registerDevices2() {\n    //This has to be done at startup because it takes too long for a normal command.\n    [\"sensorList\": \"Sensor Devices\", \"speakerList\": \"Speaker Devices\", \"deviceList\": \"Other Devices\"]?.each { k,v->\n        logDebug(\"Registering (${settings?.\"${k}\"?.size() ?: 0}) ${v}\")\n        registerChangeHandler(settings?.\"${k}\")\n    }\n    runIn(3, \"registerDevices3\")\n}\n\ndef registerDevices3() {\n    //This has to be done at startup because it takes too long for a normal command.\n    [\"switchList\": \"Switch Devices\", \"shadesList\": \"Window Shade Devices\", \"garageList\": \"Garage Door Devices\", \"tstatList\": \"Thermostat Devices\", \"tstatHeatList\": \"Thermostat (HeatOnly) Devices\"]?.each { k,v->\n        logDebug(\"Registering (${settings?.\"${k}\"?.size() ?: 0}) ${v}\")\n        registerChangeHandler(settings?.\"${k}\")\n    }\n    logDebug(\"Registered (${getDeviceCnt(true)} Devices)\")\n    logDebug(\"-----------------------------------------------\")\n\n    if(settings?.restartService == true) {\n        logWarn(\"Sent Request to Homebridge Service to Stop... Service should restart automatically\")\n        attemptServiceRestart()\n        settingUpdate(\"restartService\", \"false\", \"bool\")\n    }\n    runIn(5, \"updateServicePrefs\")\n    runIn(8, \"sendDeviceRefreshCmd\")\n}\n\nBoolean isDeviceInInput(setKey, devId) {\n    if(settings[setKey]) {\n        return (settings[setKey]?.find { it?.getId() == devId })\n    }\n    return false\n}\n\ndef registerChangeHandler(devices, showlog=false) {\n    devices?.each { device ->\n        List theAtts = device?.supportedAttributes?.collect { it?.name as String }?.unique()\n        if(showlog) { log.debug \"atts: ${theAtts}\" }\n        theAtts?.each {att ->\n            if(!(ignoreLists()?.evt_attributes?.contains(att))) {\n                if(settings?.noTemp && att == \"temperature\" && (device?.hasAttribute(\"contact\") || device?.hasAttribute(\"water\"))) {\n                    Boolean skipAtt = true\n                    if(settings?.sensorAllowTemp) {\n                        skipAtt = isDeviceInInput('sensorAllowTemp', device?.id)\n                    }\n                    if(skipAtt) { return }\n                }\n                Map attMap = [\n                    \"acceleration\": \"Acceleration\", \"battery\": \"Battery\", \"button\": \"Button\", \"contact\": \"Contact\", \"energy\": \"Energy\", \"humidity\": \"Humidity\", \"illuminance\": \"Illuminance\",\n                    \"level\": \"Level\", \"lock\": \"Lock\", \"motion\": \"Motion\", \"power\": \"Power\", \"presence\": \"Presence\", \"switch\": \"Switch\", \"tamper\": \"Tamper\",\n                    \"temperature\": \"Temp\", \"valve\": \"Valve\"\n                ]?.each { k,v -> if(att == k && isDeviceInInput('remove${v}', device?.id)) { return } }\n                subscribe(device, att, \"changeHandler\")\n                if(showlog) { log.debug \"Registering ${device?.displayName} for ${att} events\" }\n            }\n        }\n    }\n}\n\ndef changeHandler(evt) {\n    def sendItems = []\n    def sendNum = 1\n    def src = evt?.source\n    def deviceid = evt?.deviceId\n    def deviceName = evt?.displayName\n    def attr = evt?.name\n    def value = evt?.value\n    def dt = evt?.date\n    def sendEvt = true\n\n    switch(evt?.name) {\n        case \"hsmStatus\":\n            deviceid = \"alarmSystemStatus_${location?.id}\"\n            attr = \"alarmSystemStatus\"\n            sendItems?.push([evtSource: src, evtDeviceName: deviceName, evtDeviceId: deviceid, evtAttr: attr, evtValue: value, evtUnit: evt?.unit ?: \"\", evtDate: dt])\n            break\n        case \"hsmAlert\":\n            if(evt?.value == \"intrusion\") {\n                deviceid = \"alarmSystemStatus_${location?.id}\"\n                attr = \"alarmSystemStatus\"\n                value = \"alarm_active\"\n                sendItems?.push([evtSource: src, evtDeviceName: deviceName, evtDeviceId: deviceid, evtAttr: attr, evtValue: value, evtUnit: evt?.unit ?: \"\", evtDate: dt])\n            } else { sendEvt = false }\n            break\n        case \"hsmRules\":\n        case \"hsmSetArm\":\n            sendEvt = false\n            break\n        case \"alarmSystemStatus\":\n            deviceid = \"alarmSystemStatus_${location?.id}\"\n            sendItems?.push([evtSource: src, evtDeviceName: deviceName, evtDeviceId: deviceid, evtAttr: attr, evtValue: value, evtUnit: evt?.unit ?: \"\", evtDate: dt])\n            break\n        case \"mode\":\n            settings?.modeList?.each { id->\n                def md = getModeById(id)\n                if(md && md?.id) { sendItems?.push([evtSource: \"MODE\", evtDeviceName: \"Mode - ${md?.name}\", evtDeviceId: md?.id, evtAttr: \"switch\", evtValue: modeSwitchState(md?.name), evtUnit: \"\", evtDate: dt]) }\n            }\n            break\n        case \"routineExecuted\":\n            settings?.routineList?.each { id->\n                def rt = getRoutineById(id)\n                if(rt && rt?.id) {\n                    sendItems?.push([evtSource: \"ROUTINE\", evtDeviceName: \"Routine - ${rt?.label}\", evtDeviceId: rt?.id, evtAttr: \"switch\", evtValue: \"off\", evtUnit: \"\", evtDate: dt])\n                }\n            }\n            break\n        default:\n            def evtData = null\n            if(attr == \"button\") { evtData = parseJson(evt?.data) }\n            sendItems?.push([evtSource: src, evtDeviceName: deviceName, evtDeviceId: deviceid, evtAttr: attr, evtValue: value, evtUnit: evt?.unit ?: \"\", evtDate: dt, evtData: evtData])\n            break\n    }\n\n    if (sendEvt && state?.pluginDetails?.directIP != \"\" && sendItems?.size()) {\n        //Send Using the Direct Mechanism\n        sendItems?.each { send->\n            if(settings?.showEventLogs) {\n                String unitStr = \"\"\n                switch(send?.evtAttr as String) {\n                    case \"temperature\":\n                        unitStr = \"\\u00b0${send?.evtUnit}\"\n                        break\n                    case \"humidity\":\n                    case \"level\":\n                    case \"battery\":\n                        unitStr = \"%\"\n                        break\n                    case \"power\":\n                        unitStr = \"W\"\n                        break\n                    case \"illuminance\":\n                        unitStr = \" Lux\"\n                        break\n                    default:\n                        unitStr = \"${send?.evtUnit}\"\n                        break\n                }\n                logDebug(\"Sending${\" ${send?.evtSource}\" ?: \"\"} Event (${send?.evtDeviceName} | ${send?.evtAttr.toUpperCase()}: ${send?.evtValue}${unitStr}) ${send?.evtData ? \"Data: ${send?.evtData}\" : \"\"} to Homebridge at (${state?.pluginDetails?.directIP}:${state?.pluginDetails?.directPort})\")\n            }\n            sendHttpPost(\"update\", [\n                change_name: send?.evtDeviceName,\n                change_device: send?.evtDeviceId,\n                change_attribute: send?.evtAttr,\n                change_value: send?.evtValue,\n                change_data: send?.evtData,\n                change_date: send?.evtDate,\n                app_id: app?.getId(),\n                access_token: atomicState?.accessToken\n            ])\n            logEvt([name: send?.evtAttr, value: send?.evtValue, device: send?.evtDeviceName])\n        }\n    }\n}\n\nprivate sendHttpGet(path, contentType) {\n    if(settings?.sendViaNgrok && settings?.ngrokHttpUrl) {\n        httpGet([\n            uri: \"https://${settings?.ngrokHttpUrl}.ngrok.io\",\n            path: \"/${path}\",\n            contentType: contentType\n        ])\n    } else { sendHubCommand(new physicalgraph.device.HubAction(method: \"GET\", path: \"/${path}\", headers: [HOST: getServerAddress()])) }\n}\n\nprivate sendHttpPost(path, body, contentType = \"application/json\") {\n    if(settings?.sendViaNgrok && settings?.ngrokHttpUrl) {\n        Map params = [\n            uri: \"https://${settings?.ngrokHttpUrl}.ngrok.io\",\n            path: \"/${path}\",\n            contentType: contentType,\n            body: body\n        ]\n        httpPost(params)\n    } else {\n        Map params = [\n            method: \"POST\",\n            path: \"/${path}\",\n            headers: [\n                HOST: getServerAddress(),\n                'Content-Type': contentType\n            ],\n            body: body\n        ]\n        def result = new physicalgraph.device.HubAction(params)\n        sendHubCommand(result)\n    }\n}\n\nprivate getServerAddress() { return \"${state?.pluginDetails?.directIP}:${state?.pluginDetails?.directPort}\" }\n\ndef getModeById(String mId) {\n    return location?.modes?.find{it?.id?.toString() == mId}\n}\n\ndef getRoutineById(String rId) {\n    return location?.helloHome?.getPhrases()?.find{it?.id == rId}\n}\n\ndef getModeByName(String name) {\n    return location?.modes?.find{it?.name?.toString() == name}\n}\n\ndef getRoutineByName(String name) {\n    return location?.helloHome?.getPhrases()?.find{it?.label == name}\n}\n\ndef getShmIncidents() {\n    //Thanks Adrian\n    def incidentThreshold = now() - 604800000\n    return location.activeIncidents.collect{[date: it?.date?.time, title: it?.getTitle(), message: it?.getMessage(), args: it?.getMessageArgs(), sourceType: it?.getSourceType()]}.findAll{ it?.date >= incidentThreshold } ?: null\n}\n\nvoid settingUpdate(name, value, type=null) {\n    if(name && type) {\n        app?.updateSetting(\"$name\", [type: \"$type\", value: value])\n    }\n    else if (name && type == null){ app?.updateSetting(name.toString(), value) }\n}\n\nvoid settingRemove(String name) {\n    if(name && settings?.containsKey(name as String)) { app?.deleteSetting(name as String) }\n}\n\nBoolean devMode() {\n    return (appSettings?.devMode?.toString() == \"true\")\n}\n\nprivate activateDirectUpdates(isLocal=false) {\n    logTrace(\"activateDirectUpdates: ${getServerAddress()}${isLocal ? \" | (Local)\" : \"\"}\")\n    sendHttpPost(\"initial\", [\n        app_id: app?.getId(),\n        access_token: atomicState?.accessToken\n    ])\n}\n\nprivate attemptServiceRestart(isLocal=false) {\n    logTrace(\"attemptServiceRestart: ${getServerAddress()}${isLocal ? \" | (Local)\" : \"\"}\")\n    sendHttpPost(\"restart\", [\n        app_id: app?.getId(),\n        access_token: atomicState?.accessToken\n    ])\n}\n\nprivate sendDeviceRefreshCmd(isLocal=false) {\n    logTrace(\"sendDeviceRefreshCmd: ${getServerAddress()}${isLocal ? \" | (Local)\" : \"\"}\")\n    sendHttpPost(\"refreshDevices\", [\n        app_id: app?.getId(),\n        access_token: atomicState?.accessToken\n    ])\n}\n\nprivate updateServicePrefs(isLocal=false) {\n    logTrace(\"updateServicePrefs: ${getServerAddress()}${isLocal ? \" | (Local)\" : \"\"}\")\n    sendHttpPost(\"updateprefs\", [\n        app_id: app?.getId(),\n        access_token: atomicState?.accessToken,\n        local_commands: false, //(settings?.sendCmdViaHubaction != false),\n        local_hub_ip: location?.hubs[0]?.localIP\n    ])\n}\n\ndef pluginStatus() {\n    def body = request?.JSON;\n    state?.pluginUpdates = [hasUpdate: (body?.hasUpdate == true), newVersion: (body?.newVersion ?: null)]\n    if(body?.version) { updCodeVerMap(\"plugin\", body?.version)}\n    def resultJson = new groovy.json.JsonOutput().toJson([status: 'OK'])\n    render contentType: \"application/json\", data: resultJson\n}\n\ndef enableDirectUpdates() {\n    // log.trace \"enableDirectUpdates: ($params)\"\n    state?.pluginDetails = [\n        directIP: params?.ip,\n        directPort: params?.port,\n        version: params?.version ?: null\n    ]\n    updCodeVerMap(\"plugin\", params?.version ?: null)\n    activateDirectUpdates()\n    updTsVal(\"lastDirectUpdsEnabled\")\n    def resultJson = new groovy.json.JsonOutput().toJson([status: 'OK'])\n    render contentType: \"application/json\", data: resultJson\n}\n\nmappings {\n    if (!params?.access_token || (params?.access_token && params?.access_token != atomicState?.accessToken)) {\n        path(\"/devices\")\t\t\t\t\t{ action: [GET: \"authError\"] }\n        path(\"/config\")\t\t\t\t\t\t{ action: [GET: \"authError\"] }\n        path(\"/location\")\t\t\t\t\t{ action: [GET: \"authError\"] }\n        path(\"/pluginStatus\")\t\t\t    { action: [POST: \"authError\"] }\n        path(\"/:id/command/:command\")\t\t{ action: [POST: \"authError\"] }\n        path(\"/:id/query\")\t\t\t\t\t{ action: [GET: \"authError\"] }\n        path(\"/:id/attribute/:attribute\") \t{ action: [GET: \"authError\"] }\n        path(\"/startDirect/:ip/:port/:version\")\t\t{ action: [GET: \"authError\"] }\n    } else {\n        path(\"/devices\")\t\t\t\t\t{ action: [GET: \"getAllData\"] }\n        path(\"/config\")\t\t\t\t\t\t{ action: [GET: \"renderConfig\"]  }\n        path(\"/deviceDebug\")\t\t\t    { action: [GET: \"viewDeviceDebug\"]  }\n        path(\"/location\")\t\t\t\t\t{ action: [GET: \"renderLocation\"] }\n        path(\"/pluginStatus\")\t\t\t    { action: [POST: \"pluginStatus\"] }\n        path(\"/:id/command/:command\")\t\t{ action: [POST: \"deviceCommand\"] }\n        path(\"/:id/query\")\t\t\t\t\t{ action: [GET: \"deviceQuery\"] }\n        path(\"/:id/attribute/:attribute\")\t{ action: [GET: \"deviceAttribute\"] }\n        path(\"/startDirect/:ip/:port/:version\")\t\t{ action: [POST: \"enableDirectUpdates\"] }\n    }\n}\n\ndef appInfoSect() {\n    Map codeVer = state?.codeVersions ?: null\n    Boolean isNote = false\n    section() {\n        String str = \"Version: v${appVersion()}\"\n        str += state?.pluginDetails?.version ? \"\\nPlugin: v${state?.pluginDetails?.version}\" : \"\"\n        str += (state?.pluginDetails?.version && state?.pluginUpdates) ? ((state?.pluginUpdates?.hasUpdate == true) ? \"\\nUpdate Available: (v${state?.pluginUpdates?.newVersion})\" : \"\") : \"\"\n        href \"changeLogPage\", title: \"${app?.name}\", description: str, image: appIconUrl()\n        Map minUpdMap = getMinVerUpdsRequired()\n        List codeUpdItems = codeUpdateItems(true)\n        if(minUpdMap?.updRequired && minUpdMap?.updItems?.size()) {\n            isNote=true\n            String str3 = \"Updates Required:\"\n            minUpdMap?.updItems?.each { item-> str3 += bulletItem(str3, item)  }\n            paragraph str3, required: true, state: null\n            paragraph \"If you just updated the code please press Done/Save to let the app process the changes.\", required: true, state: null\n        } else if(codeUpdItems?.size()) {\n            isNote=true\n            String str2 = \"Code Updates Available:\"\n            codeUpdItems?.each { item-> str2 += bulletItem(str2, item) }\n            paragraph str2, required: true, state: null\n        }\n        if(!isNote) { paragraph \"No Issues to Report\" }\n    }\n}\n\n/**********************************************\n        APP HELPER FUNCTIONS\n***********************************************/\nString bulletItem(String inStr, String strVal) { return \"${inStr == \"\" ? \"\" : \"\\n\"} \\u2022 ${strVal}\" }\nString dashItem(String inStr, String strVal, newLine=false) { return \"${(inStr == \"\" && !newLine) ? \"\" : \"\\n\"} - ${strVal}\" }\nString textDonateLink() { return \"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RVFJTG8H86SK8&source=url\" }\nInteger versionStr2Int(str) { return str ? str?.toString()?.tokenize(\"-\")[0]?.replaceAll(\"\\\\.\", \"\")?.toInteger() : null }\nString versionCleanup(str) { return str ? str?.toString()?.tokenize(\"-\")[0] : null }\nBoolean codeUpdIsAvail(String newVer, String curVer, String type) {\n    Boolean result = false\n    def latestVer\n    if(newVer && curVer) {\n        newVer = versionCleanup(newVer)\n        curVer = versionCleanup(curVer)\n        List versions = [newVer, curVer]\n        if(newVer != curVer) {\n            latestVer = versions?.max { a, b ->\n                List verA = a?.tokenize('.'); List verB = b?.tokenize('.'); Integer commonIndices = Math.min(verA?.size(), verB?.size());\n                for (int i = 0; i < commonIndices; ++i) { if(verA[i]?.toInteger() != verB[i]?.toInteger()) { return verA[i]?.toInteger() <=> verB[i]?.toInteger() }; }\n                verA?.size() <=> verB?.size()\n            }\n            result = (latestVer == newVer)\n        }\n    }\n    return result\n}\nBoolean appUpdAvail() { return (state?.appData?.versions && state?.codeVersions?.mainApp && codeUpdIsAvail(state?.appData?.versions?.mainApp, appVersion(), \"main_app\")) }\nBoolean pluginUpdAvail() { return (state?.appData?.versions && state?.codeVersions?.plugin && codeUpdIsAvail(state?.appData?.versions?.plugin, state?.codeVersions?.plugin, \"plugin\")) }\nprivate Map getMinVerUpdsRequired() {\n    Boolean updRequired = false\n    List updItems = []\n    Map codeItems = [plugin: \"Homebridge Plugin\"]\n    Map codeVers = state?.codeVersions ?: [:]\n    codeVers?.each { k,v->\n        try {\n            if(codeItems?.containsKey(k as String) && v != null && (versionStr2Int(v) < minVersions()[k as String])) { updRequired = true; updItems?.push(codeItems[k]); }\n        } catch (ex) {\n            logError(\"getMinVerUpdsRequired Error: ${ex}\")\n        }\n    }\n    return [updRequired: updRequired, updItems: updItems]\n}\n\nprivate List codeUpdateItems(shrt=false) {\n    Boolean appUpd = appUpdAvail()\n    Boolean plugUpd = pluginUpdAvail()\n    List updItems = []\n    if(appUpd || servUpd) {\n        if(appUpd) updItems.push(\"${!shrt ? \"\\nHomebridge \" : \"\"}App: (v${state?.appData?.versions?.mainApp?.toString()})\")\n        if(plugUpd) updItems.push(\"${!shrt ? \"\\n\" : \"\"}Plugin: (v${state?.appData?.versions?.server?.toString()})\")\n    }\n    return updItems\n}\n\nInteger getLastTsValSecs(val, nullVal=1000000) {\n    def tsMap = atomicState?.tsDtMap\n    return (val && tsMap && tsMap[val]) ? GetTimeDiffSeconds(tsMap[val]).toInteger() : nullVal\n}\n\nprivate updTsVal(key, dt=null) {\n    def data = atomicState?.tsDtMap ?: [:]\n    if(key) { data[key] = dt ?: getDtNow() }\n    atomicState?.tsDtMap = data\n}\n\nprivate remTsVal(key) {\n    def data = atomicState?.tsDtMap ?: [:]\n    if(key) {\n        if(key instanceof List) {\n            key?.each { k-> if(data?.containsKey(k)) { data?.remove(k) } }\n        } else { if(data?.containsKey(key)) { data?.remove(key) } }\n        atomicState?.tsDtMap = data\n    }\n}\n\nprivate getTsVal(val) {\n    def tsMap = atomicState?.tsDtMap\n    if(val && tsMap && tsMap[val]) { return tsMap[val] }\n    return null\n}\n\nprivate updCodeVerMap(key, val) {\n    Map cv = atomicState?.codeVersions ?: [:]\n    if(val && (!cv.containsKey(key) || (cv?.containsKey(key) && cv[key] != val))) { cv[key as String] = val }\n    if (cv?.containsKey(key) && val == null) { cv?.remove(key) }\n    atomicState?.codeVersions = cv\n}\n\nprivate cleanUpdVerMap() {\n    Map cv = atomicState?.codeVersions ?: [:]\n    cv?.each { k, v-> if(v == null) ri?.push(k) }\n    ri?.each { cv?.remove(it) }\n    atomicState?.codeVersions = cv\n}\n\nprivate updInstData(key, val) {\n    Map iData = atomicState?.installData ?: [:]\n    iData[key] = val\n    atomicState?.installData = iData\n}\n\nprivate getInstData(key) {\n    def iMap = atomicState?.installData\n    if(val && iMap && iMap[val]) { return iMap[val] }\n    return null\n}\n\nprivate checkVersionData(now = false) { //This reads a JSON file from GitHub with version numbers\n    def lastUpd = getLastTsValSecs(\"lastAppDataUpdDt\")\n    if (now || !state?.appData || (lastUpd > (3600*6))) {\n        if(now && (lastUpd < 300)) { return }\n        getConfigData()\n    }\n}\n\nprivate getConfigData() {\n    Map params = [\n        uri: \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings-v2/master/appData.json\",\n        contentType: \"application/json\"\n    ]\n    def data = getWebData(params, \"appData\", false)\n    if(data) {\n        state?.appData = data\n        updTsVal(\"lastAppDataUpdDt\")\n        logDebug(\"Successfully Retrieved (v${data?.appDataVer}) of AppData Content from GitHub Repo...\")\n    }\n}\n\nprivate getWebData(params, desc, text=true) {\n    try {\n        httpGet(params) { resp ->\n            if(resp?.data) {\n                if(text) { return resp?.data?.text.toString() }\n                return resp?.data\n            }\n        }\n    } catch (ex) {\n        incrementCntByKey(\"appErrorCnt\")\n        if(ex instanceof groovyx.net.http.HttpResponseException) {\n            logWarn(\"${desc} file not found\")\n        } else { logError(\"getWebData(params: $params, desc: $desc, text: $text) Exception: ${ex}\") }\n        return \"${label} info not found\"\n    }\n}\n\n/******************************************\n|       DATE | TIME HELPERS\n******************************************/\ndef formatDt(dt, tzChg=true) {\n    def tf = new java.text.SimpleDateFormat(\"E MMM dd HH:mm:ss z yyyy\")\n    if(tzChg) { if(location.timeZone) { tf.setTimeZone(location?.timeZone) } }\n    return tf?.format(dt)\n}\n\ndef getDtNow() {\n    def now = new Date()\n    return formatDt(now)\n}\n\ndef GetTimeDiffSeconds(lastDate, sender=null) {\n    try {\n        if(lastDate?.contains(\"dtNow\")) { return 10000 }\n        def now = new Date()\n        def lastDt = Date.parse(\"E MMM dd HH:mm:ss z yyyy\", lastDate)\n        def start = Date.parse(\"E MMM dd HH:mm:ss z yyyy\", formatDt(lastDt)).getTime()\n        def stop = Date.parse(\"E MMM dd HH:mm:ss z yyyy\", formatDt(now)).getTime()\n        def diff = (int) (long) (stop - start) / 1000\n        return diff?.abs()\n    } catch (ex) {\n        logError(\"GetTimeDiffSeconds Exception: (${sender ? \"$sender | \" : \"\"}lastDate: $lastDate): ${ex}\")\n        return 10000\n    }\n}\n\n/******************************************\n|       Changelog Logic\n******************************************/\nBoolean showDonationOk() { return (state?.isInstalled && !atomicState?.installData?.shownDonation && getDaysSinceUpdated() >= 30 && !settings?.sentDonation) }\nInteger getDaysSinceUpdated() {\n    def updDt = atomicState?.installData?.updatedDt ?: null\n    if(updDt == null || updDt == \"Not Set\") {\n        updInstData(\"updatedDt\", getDtNow().toString())\n        return 0\n    }\n    def start = Date.parse(\"E MMM dd HH:mm:ss z yyyy\", updDt)\n    def stop = new Date()\n    if(start && stop) {\treturn (stop - start) }\n    return 0\n}\n\nString changeLogData() { return getWebData([uri: \"https://raw.githubusercontent.com/tonesto7/homebridge-smartthings-v2/master/CHANGELOG-app.md\", contentType: \"text/plain; charset=UTF-8\"], \"changelog\") }\nBoolean showChgLogOk() { return (state?.isInstalled && (state?.curAppVer != appVersion() || state?.installData?.shownChgLog != true)) }\ndef changeLogPage() {\n    def execTime = now()\n    return dynamicPage(name: \"changeLogPage\", title: \"\", nextPage: \"mainPage\", install: false) {\n        section() {\n            paragraph title: \"Release Notes\", \"\", state: \"complete\", image: getAppImg(\"change_log\")\n            paragraph changeLogData()\n        }\n        state?.curAppVer = appVersion()\n        updInstData(\"shownChgLog\", true)\n    }\n}\n\nInteger stateSize() { def j = new groovy.json.JsonOutput().toJson(state); return j?.toString().length(); }\nInteger stateSizePerc() { return (int) ((stateSize() / 100000)*100).toDouble().round(0); }\nprivate addToHistory(String logKey, data, Integer max=10) {\n    Boolean ssOk = (stateSizePerc() > 70)\n    List eData = atomicState[logKey as String] ?: []\n    if(eData?.find { it?.data == data }) { return; }\n    eData?.push([dt: getDtNow(), data: data])\n    if(!ssOk || eData?.size() > max) { eData = eData?.drop( (eData?.size()-max) ) }\n    atomicState[logKey as String] = eData\n}\n\nprivate logDebug(msg) { if(showDebugLogs) { logToServer(msg, \"debug\"); log.debug \"Homebridge (v${appVersion()}) | ${msg}\"; } }\nprivate logInfo(msg) { logToServer(msg, \"info\"); log.info \" Homebridge (v${appVersion()}) | ${msg}\"; }\nprivate logTrace(msg) { logToServer(msg, \"trace\"); log.trace \"Homebridge (v${appVersion()}) | ${msg}\"; }\nprivate logWarn(msg) { logToServer(msg, \"warn\"); log.warn \" Homebridge (v${appVersion()}) | ${msg}\"; }\nprivate logError(msg) { logToServer(msg, \"error\"); log.error \"Homebridge (v${appVersion()}) | ${msg}\"; }\n\npublic String getLogServerAddr() { return appSettings?.log_address ?: null }\npublic logToServer(msg, lvl) {\n    String addr = parent ? parent?.getLogServerAddr() : getLogServerAddr()\n    if(addr) {\n        Map params = [\n            method: \"POST\",\n            path: \"/gelf\",\n            headers: [\n                HOST: addr,\n                'Content-Type': \"application/json\"\n            ],\n            body: [short_message: msg, logLevel: lvl, host: \"SmartThings (HomebridgeV2)\"]\n        ]\n        params?.body?.appVersion = appVersion(); params?.body?.appName = app?.getName(); params?.body?.appLabel = app?.getLabel();\n        // params?.body?.devVersion = devVersion(); params?.body?.deviceHandler = device?.getName(); params?.body?.deviceName = device?.displayName;\n        def result = new physicalgraph.device.HubAction(params)\n        sendHubCommand(result)\n    }\n}\n\nList getCmdHistory() { return atomicState?.cmdHistory ?: [] }\nList getEvtHistory() { return atomicState?.evtHistory ?: [] }\nvoid clearHistory() {\n    atomicState?.cmdHistory = []\n    atomicState?.evtHistory = []\n}\n\nprivate logEvt(evtData) { addToHistory(\"evtHistory\", evtData, 15) }\nprivate logCmd(cmdData) { addToHistory(\"cmdHistory\", cmdData, 15) }\n"
  },
  {
    "path": "src/ST_Accessories.js",
    "content": "const knownCapabilities = require(\"./libs/Constants\").knownCapabilities,\n    pluginVersion = require(\"./libs/Constants\").pluginVersion,\n    _ = require(\"lodash\"),\n    ServiceTypes = require(\"./ST_ServiceTypes\"),\n    Transforms = require(\"./ST_Transforms\"),\n    DeviceTypes = require(\"./ST_DeviceCharacteristics\");\nvar Service, Characteristic, appEvts;\n\nmodule.exports = class ST_Accessories {\n    constructor(platform) {\n        this.mainPlatform = platform;\n        appEvts = platform.appEvts;\n        this.logConfig = platform.logConfig;\n        this.configItems = platform.getConfigItems();\n        this.myUtils = platform.myUtils;\n        this.log = platform.log;\n        this.hap = platform.hap;\n        this.uuid = platform.uuid;\n        Service = platform.Service;\n        Characteristic = platform.Characteristic;\n        this.CommunityTypes = require(\"./libs/CommunityTypes\")(Service, Characteristic);\n        this.client = platform.client;\n        this.comparator = this.comparator.bind(this);\n        this.transforms = new Transforms(this, Characteristic);\n        this.serviceTypes = new ServiceTypes(this, Service);\n        this.device_types = new DeviceTypes(this, Characteristic);\n        this._accessories = {};\n        this._buttonMap = {};\n        this._attributeLookup = {};\n    }\n\n    initializeAccessory(accessory, fromCache = false) {\n        if (!fromCache) {\n            accessory.deviceid = accessory.context.deviceData.deviceid;\n            accessory.name = accessory.context.deviceData.name;\n            accessory.context.deviceData.excludedCapabilities.forEach((cap) => {\n                if (cap !== undefined) {\n                    this.log.debug(`Removing capability: ${cap} from Device: ${accessory.context.deviceData.name}`);\n                    delete accessory.context.deviceData.capabilities[cap];\n                }\n            });\n            accessory.context.name = accessory.context.deviceData.name;\n            accessory.context.deviceid = accessory.context.deviceData.deviceid;\n        } else {\n            this.log.debug(`Initializing Cached Device ${accessory.context.deviceid}`);\n            accessory.deviceid = accessory.context.deviceid;\n            accessory.name = accessory.context.name;\n        }\n        try {\n            accessory.commandTimers = {};\n            accessory.commandTimersTS = {};\n            accessory.context.uuid = accessory.UUID || this.uuid.generate(`smartthings_v2_${accessory.deviceid}`);\n            accessory.getOrAddService = this.getOrAddService.bind(accessory);\n            accessory.getOrAddServiceByName = this.getOrAddServiceByName.bind(accessory);\n            accessory.getOrAddCharacteristic = this.getOrAddCharacteristic.bind(accessory);\n            accessory.hasCapability = this.hasCapability.bind(accessory);\n            accessory.getCapabilities = this.getCapabilities.bind(accessory);\n            accessory.hasAttribute = this.hasAttribute.bind(accessory);\n            accessory.hasCommand = this.hasCommand.bind(accessory);\n            accessory.hasDeviceFlag = this.hasDeviceFlag.bind(accessory);\n            accessory.hasService = this.hasService.bind(accessory);\n            accessory.hasCharacteristic = this.hasCharacteristic.bind(accessory);\n            accessory.updateDeviceAttr = this.updateDeviceAttr.bind(accessory);\n            accessory.updateCharacteristicVal = this.updateCharacteristicVal.bind(accessory);\n            accessory.manageGetCharacteristic = this.device_types.manageGetCharacteristic.bind(accessory);\n            accessory.manageGetSetCharacteristic = this.device_types.manageGetSetCharacteristic.bind(accessory);\n            accessory.sendCommand = this.sendCommand.bind(accessory);\n            return this.configureCharacteristics(accessory);\n        } catch (err) {\n            this.log.error(`initializeAccessory (fromCache: ${fromCache}) Error:`, err);\n            // console.error(err);\n            return accessory;\n        }\n    }\n\n    configureCharacteristics(accessory) {\n        for (let index in accessory.context.deviceData.capabilities) {\n            if (knownCapabilities.indexOf(index) === -1 && this.mainPlatform.unknownCapabilities.indexOf(index) === -1) this.mainPlatform.unknownCapabilities.push(index);\n        }\n        accessory.context.deviceGroups = [];\n        accessory.servicesToKeep = [];\n        accessory.reachable = true;\n        accessory.context.lastUpdate = new Date();\n\n        let accessoryInformation = accessory\n            .getOrAddService(Service.AccessoryInformation)\n            .setCharacteristic(Characteristic.FirmwareRevision, accessory.context.deviceData.firmwareVersion)\n            .setCharacteristic(Characteristic.Manufacturer, accessory.context.deviceData.manufacturerName)\n            .setCharacteristic(Characteristic.Model, accessory.context.deviceData.modelName ? `${this.myUtils.toTitleCase(accessory.context.deviceData.modelName)}` : \"Unknown\")\n            .setCharacteristic(Characteristic.Name, accessory.context.deviceData.name)\n            .setCharacteristic(Characteristic.HardwareRevision, pluginVersion);\n        accessory.servicesToKeep.push(Service.AccessoryInformation.UUID);\n\n        if (!accessoryInformation.listeners(\"identify\")) {\n            accessoryInformation.on(\"identify\", function(paired, callback) {\n                this.log.info(\"%s - identify\", accessory.displayName);\n                callback();\n            });\n        }\n\n        let svcTypes = this.serviceTypes.getServiceTypes(accessory);\n        if (svcTypes) {\n            svcTypes.forEach((svc) => {\n                if (svc.name && svc.type) {\n                    this.log.debug(accessory.name, \" | \", svc.name);\n                    accessory.servicesToKeep.push(svc.type.UUID);\n                    this.device_types[svc.name](accessory, svc.type);\n                }\n            });\n        } else {\n            throw \"Unable to determine the service type of \" + accessory.deviceid;\n        }\n        return this.removeUnusedServices(accessory);\n    }\n\n    processDeviceAttributeUpdate(change) {\n        // let that = this;\n        return new Promise((resolve) => {\n            let characteristics = this.getAttributeStoreItem(change.attribute, change.deviceid);\n            let accessory = this.getAccessoryFromCache(change);\n            // console.log(characteristics);\n            if (!characteristics || !accessory) resolve(false);\n            if (characteristics instanceof Array) {\n                characteristics.forEach((char) => {\n                    accessory.context.deviceData.attributes[change.attribute] = change.value;\n                    accessory.context.lastUpdate = new Date().toLocaleString();\n                    switch (change.attribute) {\n                        case \"thermostatSetpoint\":\n                            char.getValue();\n                            break;\n                        case \"button\":\n                            // console.log(characteristics);\n                            var btnNum = change.data && change.data.buttonNumber ? change.data.buttonNumber : 1;\n                            if (btnNum && accessory.buttonEvent !== undefined) {\n                                accessory.buttonEvent(btnNum, change.value, change.deviceid, this._buttonMap);\n                            }\n                            break;\n                        default:\n                            char.updateValue(this.transforms.transformAttributeState(change.attribute, change.value, char.displayName));\n                            break;\n                    }\n                });\n                resolve(this.addAccessoryToCache(accessory));\n            } else {\n                resolve(false);\n            }\n        });\n    }\n\n    sendCommand(callback, acc, dev, cmd, vals) {\n        const id = `${cmd}`;\n        const tsNow = Date.now();\n        let d = 0;\n        let b = false;\n        let d2;\n        let o = {};\n        switch (cmd) {\n            case \"setLevel\":\n            case \"setVolume\":\n            case \"setFanSpeed\":\n            case \"setSaturation\":\n            case \"setHue\":\n            case \"setColorTemperature\":\n            case \"setHeatingSetpoint\":\n            case \"setCoolingSetpoint\":\n            case \"setThermostatSetpoint\":\n                d = 600;\n                d2 = 1500;\n                o.trailing = true;\n                break;\n            case \"setThermostatMode\":\n                d = 600;\n                d2 = 1500;\n                o.trailing = true;\n                break;\n            default:\n                b = true;\n                break;\n        }\n\n        if (b) {\n            appEvts.emit(\"event:device_command\", dev, cmd, vals);\n        } else {\n            let lastTS = acc.commandTimersTS[id] && tsNow ? tsNow - acc.commandTimersTS[id] : undefined;\n            // console.log(\"lastTS: \" + lastTS, ' | ts:', acc.commandTimersTS[id]);\n            if (acc.commandTimers[id] && acc.commandTimers[id] !== null) {\n                acc.commandTimers[id].cancel();\n                acc.commandTimers[id] = null;\n                // console.log('lastTS: ', lastTS, ' | now:', tsNow, ' | last: ', acc.commandTimersTS[id]);\n                // console.log(`Existing Command Found | Command: ${cmd} | Vals: ${vals} | Executing in (${d}ms) | Last Cmd: (${lastTS ? (lastTS/1000).toFixed(1) : \"unknown\"}sec) | Id: ${id} `);\n                if (lastTS && lastTS < d) {\n                    d = d2 || 0;\n                }\n            }\n            acc.commandTimers[id] = _.debounce(\n                async() => {\n                    acc.commandTimersTS[id] = tsNow;\n                    appEvts.emit(\"event:device_command\", dev, cmd, vals);\n                },\n                d,\n                o,\n            );\n            acc.commandTimers[id]();\n        }\n        if (callback) {\n            callback();\n            callback = undefined;\n        }\n    }\n\n    log_change(attr, char, acc, chgObj) {\n        if (this.logConfig.debug === true) this.log.notice(`[CHARACTERISTIC (${char}) CHANGE] ${attr} (${acc.displayName}) | LastUpdate: (${acc.context.lastUpdate}) | NewValue: (${chgObj.newValue}) | OldValue: (${chgObj.oldValue})`);\n    }\n\n    log_get(attr, char, acc, val) {\n        if (this.logConfig.debug === true) this.log.good(`[CHARACTERISTIC (${char}) GET] ${attr} (${acc.displayName}) | LastUpdate: (${acc.context.lastUpdate}) | Value: (${val})`);\n    }\n\n    log_set(attr, char, acc, val) {\n        if (this.logConfig.debug === true) this.log.warn(`[CHARACTERISTIC (${char}) SET] ${attr} (${acc.displayName}) | LastUpdate: (${acc.context.lastUpdate}) | Value: (${val})`);\n    }\n\n    hasCapability(obj) {\n        let keys = Object.keys(this.context.deviceData.capabilities);\n        if (keys.includes(obj) || keys.includes(obj.toString().replace(/\\s/g, \"\"))) return true;\n        return false;\n    }\n\n    getCapabilities() {\n        return Object.keys(this.context.deviceData.capabilities);\n    }\n\n    hasAttribute(attr) {\n        return Object.keys(this.context.deviceData.attributes).includes(attr) || false;\n    }\n\n    hasCommand(cmd) {\n        return Object.keys(this.context.deviceData.commands).includes(cmd) || false;\n    }\n\n    getCommands() {\n        return Object.keys(this.context.deviceData.commands);\n    }\n\n    hasService(service) {\n        return this.services.map((s) => s.UUID).includes(service.UUID) || false;\n    }\n\n    hasCharacteristic(svc, char) {\n        let s = this.getService(svc) || undefined;\n        return (s && s.getCharacteristic(char) !== undefined) || false;\n    }\n\n    updateCharacteristicVal(svc, char, val) {\n        this.getOrAddService(svc).setCharacteristic(char, val);\n    }\n\n    updateCharacteristicProps(svc, char, props) {\n        this.getOrAddService(svc).getCharacteristic(char).setProps(props);\n    }\n\n    hasDeviceFlag(flag) {\n        return (this.context && this.context.deviceData && this.context.deviceData.deviceflags && Object.keys(this.context.deviceData.deviceflags).includes(flag)) || false;\n    }\n\n    updateDeviceAttr(attr, val) {\n        this.context.deviceData.attributes[attr] = val;\n    }\n\n    getOrAddService(svc) {\n        return this.getService(svc) || this.addService(svc);\n    }\n\n    getOrAddServiceByName(service, dName, sType) {\n        let svc = this.services.find((s) => s.displayName === dName);\n        if (svc) {\n            // console.log('service found');\n            return svc;\n        } else {\n            // console.log('service not found adding new one...');\n            svc = this.addService(new service(dName, sType));\n            return svc;\n        }\n    }\n\n    getOrAddCharacteristic(service, characteristic) {\n        return service.getCharacteristic(characteristic) || service.addCharacteristic(characteristic);\n    }\n\n    getServices() {\n        return this.services;\n    }\n\n    removeUnusedServices(acc) {\n        // console.log('servicesToKeep:', acc.servicesToKeep);\n        let newSvcUuids = acc.servicesToKeep || [];\n        let svcs2rmv = acc.services.filter((s) => !newSvcUuids.includes(s.UUID));\n        if (Object.keys(svcs2rmv).length) {\n            svcs2rmv.forEach((s) => {\n                acc.removeService(s);\n                this.log.info(\"Removing Unused Service:\", s.UUID);\n            });\n        }\n        return acc;\n    }\n\n    storeCharacteristicItem(attr, devid, char) {\n        // console.log('storeCharacteristicItem: ', attr, devid, char);\n        if (!this._attributeLookup[attr]) {\n            this._attributeLookup[attr] = {};\n        }\n        if (!this._attributeLookup[attr][devid]) {\n            this._attributeLookup[attr][devid] = [];\n        }\n        this._attributeLookup[attr][devid].push(char);\n    }\n\n    getAttributeStoreItem(attr, devid) {\n        if (!this._attributeLookup[attr] || !this._attributeLookup[attr][devid]) {\n            return undefined;\n        }\n        return this._attributeLookup[attr][devid] || undefined;\n    }\n\n    removeAttributeStoreItem(attr, devid) {\n        if (!this._attributeLookup[attr] || !this._attributeLookup[attr][devid]) return;\n        delete this._attributeLookup[attr][devid];\n    }\n\n    getDeviceAttributeValueFromCache(device, attr) {\n        const key = this.getAccessoryId(device);\n        let result = this._accessories[key] ? this._accessories[key].context.deviceData.attributes[attr] : undefined;\n        this.log.info(`Attribute (${attr}) Value From Cache: [${result}]`);\n        return result;\n    }\n\n    getAccessoryId(accessory) {\n        const id = accessory.deviceid || accessory.context.deviceid || undefined;\n        return id;\n    }\n\n    getAccessoryFromCache(device) {\n        const key = this.getAccessoryId(device);\n        return this._accessories[key];\n    }\n\n    getAllAccessoriesFromCache() {\n        return this._accessories;\n    }\n\n    clearAccessoryCache() {\n        this.log.alert(\"CLEARING ACCESSORY CACHE AND FORCING DEVICE RELOAD\");\n        this._accessories = {};\n    }\n\n    addAccessoryToCache(accessory) {\n        const key = this.getAccessoryId(accessory);\n        this._accessories[key] = accessory;\n        return true;\n    }\n\n    removeAccessoryFromCache(accessory) {\n        const key = this.getAccessoryId(accessory);\n        const _accessory = this._accessories[key];\n        delete this._accessories[key];\n        return _accessory;\n    }\n\n    forEach(fn) {\n        return _.forEach(this._accessories, fn);\n    }\n\n    intersection(devices) {\n        const accessories = _.values(this._accessories);\n        return _.intersectionWith(devices, accessories, this.comparator);\n    }\n\n    diffAdd(devices) {\n        const accessories = _.values(this._accessories);\n        return _.differenceWith(devices, accessories, this.comparator);\n    }\n\n    diffRemove(devices) {\n        const accessories = _.values(this._accessories);\n        return _.differenceWith(accessories, devices, this.comparator);\n    }\n\n    comparator(accessory1, accessory2) {\n        return this.getAccessoryId(accessory1) === this.getAccessoryId(accessory2);\n    }\n\n    clearAndSetTimeout(timeoutReference, fn, timeoutMs) {\n        if (timeoutReference) clearTimeout(timeoutReference);\n        return setTimeout(fn, timeoutMs);\n    }\n};"
  },
  {
    "path": "src/ST_Client.js",
    "content": "const {\n    platformName,\n    platformDesc,\n    pluginVersion\n} = require(\"./libs/Constants\"),\n    axios = require('axios').default,\n    url = require(\"url\");\n\nmodule.exports = class ST_Client {\n    constructor(platform) {\n        this.platform = platform;\n        this.log = platform.log;\n        this.appEvts = platform.appEvts;\n        this.useLocal = false; //platform.local_commands;\n        this.hubIp = platform.local_hub_ip;\n        this.configItems = platform.getConfigItems();\n        let appURL = url.parse(this.configItems.app_url);\n        this.urlItems = {\n            app_host: appURL.hostname || \"graph.api.smartthings.com\",\n            app_port: appURL.port || 443,\n            app_path: `${(appURL.path || \"/api/smartapps/installations/\")}${this.configItems.app_id}/`\n        };\n        this.localErrCnt = 0;\n        this.localDisabled = false;\n        this.registerEvtListeners();\n    }\n\n    registerEvtListeners() {\n        this.appEvts.on(\"event:device_command\", async(devData, cmd, vals) => {\n            await this.sendDeviceCommand(devData, cmd, vals);\n        });\n        this.appEvts.on(\"event:plugin_upd_status\", async() => {\n            await this.sendUpdateStatus();\n        });\n        this.appEvts.on(\"event:plugin_start_direct\", async() => {\n            await this.sendStartDirect();\n        });\n    }\n\n    sendAsLocalCmd() {\n        return (this.useLocal === true && this.hubIp !== undefined);\n    }\n\n    localHubErr(hasErr) {\n        if (hasErr) {\n            if (this.useLocal && !this.localDisabled) {\n                this.log.error(`Unable to reach your SmartThing Hub Locally... You will not receive device events!!!`);\n                this.useLocal = false;\n                this.localDisabled = true;\n            }\n        } else {\n            if (this.localDisabled) {\n                this.useLocal = true;\n                this.localDisabled = false;\n                this.log.good(`Now able to reach local Hub... Restoring Local Commands!!!`);\n                this.sendStartDirect();\n            }\n        }\n    }\n\n    updateGlobals(hubIp, useLocal = false) {\n        this.log.notice(`Updating Global Values | HubIP: ${hubIp} | UseLocal: ${useLocal}`);\n        this.hubIp = hubIp;\n        this.useLocal = false; //(useLocal === true);\n    }\n\n    handleError(src, err, allowLocal = false) {\n        switch (err.status) {\n            case 401:\n                this.log.error(`${src} Error | SmartThings Token Error: ${err.response} | Message: ${err.message}`);\n                break;\n            case 403:\n                this.log.error(`${src} Error | SmartThings Authentication Error: ${err.response} | Message: ${err.message}`);\n                break;\n            default:\n                if (err.message.startsWith('getaddrinfo EAI_AGAIN')) {\n                    this.log.error(`${src} Error | Possible Internet/Network/DNS Error | Unable to reach the uri | Message ${err.message}`);\n                } else if (allowLocal && err.message.startsWith('Error: connect ETIMEDOUT ')) {\n                    this.localHubErr(true);\n                } else {\n                    // console.error(err);\n                    this.log.error(`${src} Error: ${err.response} | Message: ${err.message}`);\n                }\n                break;\n        }\n    }\n\n    getDevices() {\n        let that = this;\n        return new Promise((resolve) => {\n            axios({\n                    method: 'get',\n                    url: `${that.configItems.app_url}${that.configItems.app_id}/devices`,\n                    params: {\n                        access_token: that.configItems.access_token\n                    },\n                    timeout: 10000\n                })\n                .then((response) => {\n                    resolve(response.data);\n                })\n                .catch((err) => {\n                    this.handleError('getDevices', err);\n                    resolve(undefined);\n                });\n        });\n    }\n\n    getDevice(deviceid) {\n        let that = this;\n        return new Promise((resolve) => {\n            axios({\n                    method: 'get',\n                    url: `${that.configItems.app_url}${that.configItems.app_id}/${deviceid}/query`,\n                    params: {\n                        access_token: that.configItems.access_token\n                    },\n                    timeout: 10000\n                })\n                .then((response) => {\n                    resolve(response.data);\n                })\n                .catch((err) => {\n                    this.handleError('getDevice', err);\n                    resolve(undefined);\n                });\n        });\n    }\n\n    sendDeviceCommand(devData, cmd, vals) {\n        return new Promise((resolve) => {\n            let that = this;\n            let sendLocal = this.sendAsLocalCmd();\n            let config = {\n                method: 'post',\n                url: `${this.configItems.app_url}${this.configItems.app_id}/${devData.deviceid}/command/${cmd}`,\n                params: {\n                    access_token: this.configItems.access_token\n                },\n                headers: {\n                    evtsource: `Homebridge_${platformName}_${this.configItems.app_id}`,\n                    evttype: 'hkCommand'\n                },\n                data: vals,\n                timeout: 5000\n            };\n            if (sendLocal) {\n                config.url = `http://${this.hubIp}:39500/event`;\n                delete config.params;\n                config.data = {\n                    deviceid: devData.deviceid,\n                    command: cmd,\n                    values: vals,\n                    evtsource: `Homebridge_${platformName}_${this.configItems.app_id}`,\n                    evttype: 'hkCommand'\n                };\n            }\n\n            try {\n                that.log.notice(`Sending Device Command: ${cmd}${vals ? ' | Value: ' + JSON.stringify(vals) : ''} | Name: (${devData.name}) | DeviceID: (${devData.deviceid}) | SendToLocalHub: (${sendLocal})`);\n                axios(config)\n                    .then((response) => {\n                        // console.log('command response:', response.data);\n                        this.log.debug(`sendDeviceCommand | Response: ${JSON.stringify(response.data)}`);\n                        that.localHubErr(false);\n                        resolve(true);\n                    })\n                    .catch((err) => {\n                        that.handleError('sendDeviceCommand', err, true);\n                        resolve(false);\n                    });\n            } catch (err) {\n                resolve(false);\n            }\n        });\n    }\n\n    sendUpdateStatus() {\n        return new Promise((resolve) => {\n            this.platform.myUtils.checkVersion()\n                .then((res) => {\n                    this.log.notice(`Sending Plugin Status to SmartThings | UpdateAvailable: ${res.hasUpdate}${res.newVersion ?  ' | newVersion: ' + res.newVersion : ''}`);\n                    axios({\n                            method: 'post',\n                            url: `${this.configItems.app_url}${this.configItems.app_id}/pluginStatus`,\n                            params: {\n                                access_token: this.configItems.access_token\n                            },\n                            data: {\n                                hasUpdate: res.hasUpdate,\n                                newVersion: res.newVersion,\n                                version: pluginVersion\n                            },\n                            timeout: 10000\n                        })\n                        .then((response) => {\n                            // console.log(response.data);\n                            if (response.data) {\n                                this.log.debug(`sendUpdateStatus Resp: ${JSON.stringify(response.data)}`);\n                                resolve(response.data);\n                            } else {\n                                resolve(null);\n                            }\n                        })\n                        .catch((err) => {\n                            this.handleError('sendUpdateStatus', err, true);\n                            resolve(undefined);\n                        });\n                });\n        });\n    }\n\n    sendStartDirect() {\n        let that = this;\n        return new Promise((resolve) => {\n            let sendLocal = this.sendAsLocalCmd();\n            let config = {\n                method: 'post',\n                url: `${this.configItems.app_url}${this.configItems.app_id}/startDirect/${this.configItems.direct_ip}/${this.configItems.direct_port}/${pluginVersion}`,\n                params: {\n                    access_token: this.configItems.access_token\n                },\n                headers: {\n                    evtsource: `Homebridge_${platformName}_${this.configItems.app_id}`,\n                    evttype: 'enableDirect'\n                },\n                data: {\n                    ip: that.configItems.direct_ip,\n                    port: that.configItems.direct_port,\n                    version: pluginVersion,\n                    evtsource: `Homebridge_${platformName}_${this.configItems.app_id}`,\n                    evttype: 'enableDirect'\n                },\n                timeout: 10000\n            };\n            if (sendLocal) {\n                config.url = `http://${this.hubIp}:39500/event`;\n                delete config.params;\n            }\n            that.log.info(`Sending StartDirect Request to ${platformDesc} | SendToLocalHub: (${sendLocal})`);\n            try {\n                axios(config)\n                    .then((response) => {\n                        // that.log.info('sendStartDirect Resp:', body);\n                        if (response.data) {\n                            this.log.debug(`sendStartDirect Resp: ${JSON.stringify(response.data)}`);\n                            resolve(response.data);\n                            that.localHubErr(false);\n                        } else {\n                            resolve(null);\n                        }\n                    })\n                    .catch((err) => {\n                        that.handleError(\"sendStartDirect\", err, true);\n                        resolve(undefined);\n                    });\n            } catch (err) {\n                resolve(err);\n            }\n        });\n    }\n};"
  },
  {
    "path": "src/ST_DeviceCharacteristics.js",
    "content": "var Characteristic, CommunityTypes, accClass;\n\nmodule.exports = class DeviceCharacteristics {\n    constructor(accessories, char) {\n        this.platform = accessories.mainPlatform;\n        // this.appEvts = accessories.mainPlatform.appEvts;\n        Characteristic = char;\n        CommunityTypes = accessories.CommunityTypes;\n        accClass = accessories;\n        this.log = accessories.log;\n        this.logConfig = accessories.logConfig;\n        this.accessories = accessories;\n        this.client = accessories.client;\n        this.myUtils = accessories.myUtils;\n        this.transforms = accessories.transforms;\n        this.homebridge = accessories.homebridge;\n    }\n\n    manageGetCharacteristic(svc, acc, char, attr, opts = {}) {\n        let c = this.getOrAddService(svc).getCharacteristic(char);\n        if (!c._events.get) {\n            c.on(\"get\", (callback) => {\n                if (attr === 'status' && char === Characteristic.StatusActive) {\n                    callback(null, accClass.transforms.transformStatus(this.context.deviceData.status));\n                } else {\n                    callback(null, accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n                    accClass.log_get(attr, char, acc, accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n                }\n            });\n            if (opts.props && Object.keys(opts.props).length) c.setProps(opts.props);\n            if (opts.evtOnly && opts.evtOnly === true) c.eventOnlyCharacteristic = opts.evtOnly;\n            c.getValue();\n            accClass.storeCharacteristicItem(attr, this.context.deviceData.deviceid, c);\n        } else {\n            if (attr === 'status' && char === Characteristic.StatusActive) {\n                c.updateValue(accClass.transforms.transformStatus(this.context.deviceData.status));\n            } else {\n                c.updateValue(accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n                accClass.log_get(attr, char, acc, accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n            }\n        }\n        if (!c._events.change) {\n            c.on(\"change\", (chg) => {\n                accClass.log_change(attr, char, acc, chg);\n            });\n        }\n    }\n\n    manageGetSetCharacteristic(svc, acc, char, attr, opts = {}) {\n        let c = this.getOrAddService(svc).getCharacteristic(char);\n        if (!c._events.get || !c._events.set) {\n            if (!c._events.get) {\n                c.on(\"get\", (callback) => {\n                    callback(null, accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n                    accClass.log_get(attr, char, acc, accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n                });\n            }\n            if (!c._events.set) {\n                c.on(\"set\", async(value, callback) => {\n                    let cmdName = accClass.transforms.transformCommandName(opts.set_altAttr || attr, value);\n                    let cmdVal = accClass.transforms.transformCommandValue(opts.set_altAttr || attr, value);\n                    if (opts.cmdHasVal === true) {\n                        acc.sendCommand(callback, acc, this.context.deviceData, cmdName, {\n                            value1: cmdVal\n                        });\n                    } else {\n                        acc.sendCommand(callback, acc, this.context.deviceData, cmdVal);\n                    }\n                    if (opts.updAttrVal) this.context.deviceData.attributes[attr] = accClass.transforms.transformAttributeState(opts.set_altAttr || attr, this.context.deviceData.attributes[opts.set_altValAttr || attr], c.displayName);\n                });\n                if (opts.props && Object.keys(opts.props).length) c.setProps(opts.props);\n                if (opts.evtOnly && opts.evtOnly === true) c.eventOnlyCharacteristic = opts.evtOnly;\n                c.getValue();\n            }\n            c.getValue();\n            accClass.storeCharacteristicItem(attr, this.context.deviceData.deviceid, c);\n        } else {\n            c.updateValue(accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n            accClass.log_get(attr, char, acc, accClass.transforms.transformAttributeState(opts.get_altAttr || attr, this.context.deviceData.attributes[opts.get_altValAttr || attr], c.displayName));\n        }\n        if (!c._events.change) {\n            c.on(\"change\", (chg) => {\n                accClass.log_change(attr, char, acc, chg);\n            });\n        }\n    }\n\n    acceleration_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.MotionDetected, 'acceleration');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"acceleration_sensor\");\n        return _accessory;\n    }\n\n    air_purifier(_accessory, _service) {\n        let actState = (_accessory.context.deviceData.attributes.switch === \"on\") ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;\n        let c = this.getOrAddService(_service).getCharacteristic(Characteristic.Active);\n        if (!c.events.get || !c.events.set) {\n            if (!c.events.get) {\n                c.on('get', (callback) => {\n                    callback(null, actState);\n                });\n            }\n            if (!c.events.set) {\n                c.on('set', (value, callback) => {\n                    _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, value ? 'on' : 'off');\n                });\n            }\n            c.getValue();\n            accClass.storeCharacteristicItem(\"switch\", _accessory.context.deviceData.deviceid, c);\n        } else {\n            c.updateValue(actState);\n        }\n\n        c = this.getaddService(_service).getCharacteristic(Characteristic.CurrentAirPurifierState);\n        let apState = (actState === Characteristic.Active.INACTIVE) ? Characteristic.CurrentAirPurifierState.INACTIVE : Characteristic.CurrentAirPurifierState.PURIFYING_AIR;\n        if (!c.events.get) {\n            c.on('get', (callback) => {\n                callback(null, apState);\n            });\n        }\n        c.updateValue(apState);\n\n        c = this.getaddService(CommunityTypes.NewAirPurifierService).getCharacteristic(CommunityTypes.FanOscilationMode);\n        if (!c.events.get || !c.events.set) {\n            if (!c.events.get) {\n                c.on('get', (callback) => {\n                    callback(null, this.transforms.transformAttributeState('fanMode', _accessory.context.deviceData.attributes.fanMode));\n                });\n            }\n            if (!c.events.set) {\n                c.on('set', (value, callback) => {\n                    _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, 'setFanMode', {\n                        value1: this.transforms.transformCommandValue('fanMode', value)\n                    });\n                });\n            }\n        }\n        this.accessories.storeCharacteristicItem(\"fanMode\", _accessory.context.deviceData.deviceid, c);\n        _accessory.context.deviceGroups.push(\"air_purifier\");\n        return _accessory;\n    }\n\n    air_quality(_accessory, _service) {\n        let c = _accessory.getOrAddService(_service).getCharacteristic(Characteristic.AirQuality);\n        if (!c._events.get) {\n            c.on(\"get\", (callback) => {\n                callback(null, Characteristic.AirQuality);\n            });\n        }\n        this.accessories.storeCharacteristicItem(\"airQuality\", _accessory.context.deviceData.deviceid, c);\n        _accessory.context.deviceGroups.push(\"airQuality\");\n        return _accessory;\n    }\n\n    alarm_system(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.SecuritySystemCurrentState, 'alarmSystemStatus');\n        _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.SecuritySystemTargetState, 'alarmSystemStatus');\n        _accessory.context.deviceGroups.push(\"alarm_system\");\n        return _accessory;\n    }\n\n    battery(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.BatteryLevel, 'battery');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusLowBattery, 'battery');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.ChargingState, 'batteryStatus');\n        _accessory.context.deviceGroups.push(\"battery\");\n        return _accessory;\n    }\n\n    button(_accessory, _service) {\n        let that = this;\n        let validValues = this.transforms.transformAttributeState('supportedButtonValues', _accessory.context.deviceData.attributes.supportedButtonValues) || [0, 2];\n        const btnCnt = _accessory.context.deviceData.attributes.numberOfButtons || 1;\n        // console.log('btnCnt: ', btnCnt);\n        if (btnCnt >= 1) {\n            for (let bNum = 1; bNum <= btnCnt; bNum++) {\n                const svc = _accessory.getOrAddServiceByName(_service, `${_accessory.context.deviceData.deviceid}_${bNum}`, bNum);\n                let c = svc.getCharacteristic(Characteristic.ProgrammableSwitchEvent);\n                c.setProps({\n                    validValues: validValues\n                });\n                c.eventOnlyCharacteristic = false;\n                if (!c._events.get) {\n                    that.accessories._buttonMap[`${_accessory.context.deviceData.deviceid}_${bNum}`] = svc;\n                    c.on(\"get\", (callback) => {\n                        this.value = -1;\n                        callback(null, that.transforms.transformAttributeState('button', _accessory.context.deviceData.attributes.button));\n                    });\n                    _accessory.buttonEvent = this.buttonEvent.bind(_accessory);\n                    this.accessories.storeCharacteristicItem(\"button\", _accessory.context.deviceData.deviceid, c);\n                }\n                svc.getCharacteristic(Characteristic.ServiceLabelIndex).setValue(bNum);\n            }\n            _accessory.context.deviceGroups.push(\"button\");\n        }\n        return _accessory;\n    }\n\n    buttonEvent(btnNum, btnVal, devId, btnMap) {\n        console.log('Button Press Event... | Button Number: (' + btnNum + ') | Button Value: ' + btnVal);\n        let bSvc = btnMap[`${devId}_${btnNum}`];\n        // console.log(bSvc);\n        if (bSvc) {\n            bSvc.getCharacteristic(Characteristic.ProgrammableSwitchEvent).getValue();\n        }\n    }\n\n    carbon_dioxide(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CarbonDioxideDetected, 'carbonDioxideMeasurement');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CarbonDioxideLevel, 'carbonDioxideMeasurement');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"carbon_dioxide\");\n        return _accessory;\n    }\n\n    carbon_monoxide(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CarbonMonoxideDetected, 'carbonMonoxide');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"carbon_monoxide\");\n        return _accessory;\n    }\n\n\n    contact_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.ContactSensorState, 'contact');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"contact_sensor\");\n        return _accessory;\n    }\n\n    energy_meter(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, CommunityTypes.KilowattHours, 'energy');\n        _accessory.context.deviceGroups.push(\"energy_meter\");\n        return _accessory;\n    }\n\n    fan(_accessory, _service) {\n        if (_accessory.hasAttribute('switch')) {\n            _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.Active, 'switch');\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CurrentFanState, 'switch', {\n                get_altAttr: \"fanState\"\n            });\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.CurrentFanState);\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.Active);\n        }\n        let spdSteps = 1;\n        if (_accessory.hasDeviceFlag('fan_3_spd')) spdSteps = 32;\n        if (_accessory.hasDeviceFlag('fan_4_spd')) spdSteps = 25;\n        let spdAttr = (_accessory.hasAttribute('level')) ? \"level\" : (_accessory.hasAttribute('fanSpeed') && _accessory.hasCommand('setFanSpeed')) ? 'fanSpeed' : undefined;\n        if (_accessory.hasAttribute('level') || _accessory.hasAttribute('fanSpeed')) {\n            _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.RotationSpeed, spdAttr, {\n                cmdHasVal: true,\n                props: {\n                    minStep: spdSteps\n                }\n            });\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.RotationSpeed);\n        }\n        _accessory.context.deviceGroups.push(\"fan\");\n        return _accessory;\n    }\n\n    garage_door(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CurrentDoorState, 'door');\n        _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.TargetDoorState, 'door');\n        _accessory.getOrAddService(_service).getCharacteristic(Characteristic.ObstructionDetected).updateValue(false);\n        return _accessory;\n    }\n\n    humidity_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CurrentRelativeHumidity, 'humidity');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"humidity_sensor\");\n        return _accessory;\n    }\n\n    illuminance_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CurrentAmbientLightLevel, 'illuminance', {\n            props: {\n                minValue: 0,\n                maxValue: 100000\n            }\n        });\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"illuminance_sensor\");\n        return _accessory;\n    }\n\n    light(_accessory, _service) {\n        _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.On, 'switch');\n        if (_accessory.hasAttribute('level')) {\n            _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.Brightness, 'level', {\n                cmdHasVal: true\n            });\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.Brightness);\n        }\n        if (_accessory.hasAttribute('hue')) {\n            _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.Hue, 'hue', {\n                cmdHasVal: true,\n                props: {\n                    minValue: 1,\n                    maxValue: 30000\n                }\n            });\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.Hue);\n        }\n        if (_accessory.hasAttribute('saturation')) {\n            _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.Saturation, 'saturation', {\n                cmdHasVal: true\n            });\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.Saturation);\n        }\n        if (_accessory.hasAttribute('colorTemperature')) {\n            _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.ColorTemperature, 'colorTemperature', {\n                cmdHasVal: true\n            });\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.ColorTemperature);\n        }\n        _accessory.context.deviceGroups.push(\"light_bulb\");\n        return _accessory;\n    }\n\n    lock(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.LockCurrentState, 'lock');\n        _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.LockTargetState, 'lock');\n        _accessory.context.deviceGroups.push(\"lock\");\n        return _accessory;\n    }\n\n    motion_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.MotionDetected, 'motion');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"motion_sensor\");\n        return _accessory;\n    }\n\n    power_meter(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, CommunityTypes.Watts, 'power');\n        _accessory.context.deviceGroups.push(\"power_meter\");\n        return _accessory;\n    }\n\n    presence_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.OccupancyDetected, 'presence');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"presence_sensor\");\n        return _accessory;\n    }\n\n    smoke_detector(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.SmokeDetected, 'smoke');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"smoke_detector\");\n        return _accessory;\n    }\n\n    speaker(_accessory, _service) {\n        let isSonos = (_accessory.context.deviceData.manufacturerName === \"Sonos\");\n        let lvlAttr = (isSonos || _accessory.hasAttribute('volume')) ? 'volume' : _accessory.hasAttribute('level') ? 'level' : undefined;\n        let c = _accessory.getOrAddService(_service).getCharacteristic(Characteristic.Volume);\n        let lastVolumeWriteValue = null;\n        if (!c._events.get || !c._events.set) {\n            if (!c._events.get) {\n                c.on(\"get\", (callback) => {\n                    callback(null, this.transforms.transformAttributeState(lvlAttr, _accessory.context.deviceData.attributes[lvlAttr]) || 0);\n                });\n            }\n            if (!c._events.set) {\n                c.on(\"set\", (value, callback) => {\n                    if (isSonos) {\n                        if (value > 0 && value !== lastVolumeWriteValue) {\n                            lastVolumeWriteValue = value;\n                            this.log.debug(`Existing volume: ${_accessory.context.deviceData.attributes.volume}, set to ${lastVolumeWriteValue}`);\n                            _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, \"setVolume\", {\n                                value1: lastVolumeWriteValue\n                            });\n                        }\n                    }\n                    if (value > 0) {\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, this.accessories.transformCommandName(lvlAttr, value), {\n                            value1: this.transforms.transformAttributeState(lvlAttr, value)\n                        });\n                    }\n                });\n            }\n            this.accessories.storeCharacteristicItem(\"volume\", _accessory.context.deviceData.deviceid, c);\n        }\n        _accessory.getOrAddService(_service).getCharacteristic(Characteristic.Volume).updateValue(this.transforms.transformAttributeState(lvlAttr, _accessory.context.deviceData.attributes[lvlAttr]) || 0);\n        if (_accessory.hasCapability('Audio Mute')) {\n            _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.Mute, 'mute');\n        }\n\n        _accessory.context.deviceGroups.push(\"speaker_device\");\n        return _accessory;\n    }\n\n    switch_device(_accessory, _service) {\n        _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.On, 'switch');\n        _accessory.context.deviceGroups.push(\"switch\");\n        return _accessory;\n    }\n\n    temperature_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CurrentTemperature, 'temperature', {\n            props: {\n                minValue: -100,\n                maxValue: 200\n            }\n        });\n        if (_accessory.hasCapability('Tamper Alert')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        } else {\n            _accessory.getOrAddService(_service).removeCharacteristic(Characteristic.StatusTampered);\n        }\n        _accessory.context.deviceGroups.push(\"temperature_sensor\");\n        return _accessory;\n    }\n\n    thermostat(_accessory, _service) {\n        //TODO:  Still seeing an issue when setting mode from OFF to HEAT.  It's setting the temp to 40 but if I change to cool then back to heat it sets the correct value.\n        const tstatService = _accessory.getOrAddService(_service);\n        let curTempChar = tstatService.getCharacteristic(Characteristic.CurrentTemperature);\n        let curHeatCoolStateChar = tstatService.getCharacteristic(Characteristic.CurrentHeatingCoolingState);\n        let targetHeatCoolStateChar = tstatService.getCharacteristic(Characteristic.TargetHeatingCoolingState);\n        let targetTempChar = tstatService.getCharacteristic(Characteristic.TargetTemperature);\n\n\n        // CURRENT HEATING/COOLING STATE\n        if (!curHeatCoolStateChar._events.get) {\n            curHeatCoolStateChar.on(\"get\", (callback) => {\n                const state = this.transforms.transformAttributeState('thermostatOperatingState', _accessory.context.deviceData.attributes.thermostatOperatingState);\n                callback(null, state);\n            });\n            this.accessories.storeCharacteristicItem(\"thermostatOperatingState\", _accessory.context.deviceData.deviceid, curHeatCoolStateChar);\n        } else {\n            curHeatCoolStateChar.updateValue(this.transforms.transformAttributeState(\"thermostatOperatingState\", _accessory.context.deviceData.attributes.thermostatOperatingState));\n        }\n\n        // TARGET HEATING/COOLING STATE\n        if (!targetHeatCoolStateChar._events.get || !targetHeatCoolStateChar._events.set) {\n            targetHeatCoolStateChar.setProps({\n                validValues: this.transforms.thermostatSupportedModes(_accessory.context.deviceData)\n            });\n            if (!targetHeatCoolStateChar._events.get) {\n                targetHeatCoolStateChar.on(\"get\", (callback) => {\n                    // console.log('thermostatMode(get): ', this.transforms.transformAttributeState('thermostatMode', _accessory.context.deviceData.attributes.thermostatMode));\n                    callback(null, this.transforms.transformAttributeState('thermostatMode', _accessory.context.deviceData.attributes.thermostatMode));\n                });\n            }\n            if (!targetHeatCoolStateChar._events.set) {\n                targetHeatCoolStateChar.on(\"set\", async(value, callback) => {\n                    let state = this.transforms.transformCommandValue('thermostatMode', value);\n                    _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, this.transforms.transformCommandName('thermostatMode', value), {\n                        value1: state\n                    });\n                    _accessory.context.deviceData.attributes.thermostatMode = state;\n                    // targetTempChar.updateValue(this.transforms.thermostatTargetTemp(_accessory.context.deviceData));\n                });\n            }\n            this.accessories.storeCharacteristicItem(\"thermostatMode\", _accessory.context.deviceData.deviceid, targetHeatCoolStateChar);\n        } else {\n            targetHeatCoolStateChar.updateValue(this.transforms.transformAttributeState(\"thermostatMode\", _accessory.context.deviceData.attributes.thermostatMode));\n        }\n\n        // CURRENT RELATIVE HUMIDITY\n        if (_accessory.hasCapability('Relative Humidity Measurement')) {\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CurrentRelativeHumidity, 'humidity');\n        }\n\n        // CURRENT TEMPERATURE\n        if (!curTempChar._events.get) {\n            curTempChar.on(\"get\", (callback) => {\n                // targetTempChar.updateValue(this.transforms.thermostatTargetTemp(_accessory.context.deviceData));\n                callback(null, this.transforms.thermostatTempConversion(_accessory.context.deviceData.attributes.temperature));\n            });\n            this.accessories.storeCharacteristicItem(\"temperature\", _accessory.context.deviceData.deviceid, curTempChar);\n            this.accessories.storeCharacteristicItem(\"thermostatSetpoint\", _accessory.context.deviceData.deviceid, targetTempChar);\n        } else {\n            curTempChar.updateValue(this.transforms.transformAttributeState(\"temperature\", _accessory.context.deviceData.attributes.temperature));\n        }\n\n        // TARGET TEMPERATURE\n        if (!targetTempChar._events.get || !targetTempChar._events.set) {\n            if (!targetTempChar._events.get) {\n                targetTempChar.on(\"get\", (callback) => {\n                    const targetTemp = this.transforms.thermostatTargetTemp(_accessory.context.deviceData);\n                    // console.log('targetTemp:', targetTemp);\n                    callback(null, targetTemp ? this.transforms.thermostatTempConversion(targetTemp) : null);\n                });\n            }\n            if (!targetTempChar._events.set) {\n                targetTempChar.on(\"set\", (value, callback) => {\n                    // Convert the Celsius value to the appropriate unit for Smartthings\n                    let temp = this.transforms.thermostatTempConversion(value, true);\n                    const targetObj = this.transforms.thermostatTargetTemp_set(_accessory.context.deviceData);\n                    if (targetObj && targetObj.cmdName && targetObj.attrName && temp) {\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, targetObj.cmdName, {\n                            value1: temp\n                        });\n                        _accessory.context.deviceData.attributes[targetObj.attrName] = temp;\n                    }\n                });\n            }\n            this.accessories.storeCharacteristicItem(\"coolingSetpoint\", _accessory.context.deviceData.deviceid, targetTempChar);\n            this.accessories.storeCharacteristicItem(\"heatingSetpoint\", _accessory.context.deviceData.deviceid, targetTempChar);\n            this.accessories.storeCharacteristicItem(\"thermostatSetpoint\", _accessory.context.deviceData.deviceid, targetTempChar);\n        } else {\n            const targetTemp = this.transforms.thermostatTargetTemp(_accessory.context.deviceData);\n            targetTempChar.updateValue(targetTemp ? this.transforms.thermostatTempConversion(targetTemp) : null);\n        }\n\n        // TEMPERATURE DISPLAY UNITS\n        let tempUnitChar = tstatService.getCharacteristic(Characteristic.TemperatureDisplayUnits);\n        tempUnitChar.updateValue((this.platform.getTempUnit() === 'F') ? Characteristic.TemperatureDisplayUnits.FAHRENHEIT : Characteristic.TemperatureDisplayUnits.CELSIUS);\n\n        // HEATING THRESHOLD TEMPERATURE\n        if (targetHeatCoolStateChar.props.validValues.includes(3)) {\n            // console.log('test', targetHeatCoolStateChar.props);\n            let heatThreshTempChar = tstatService.getCharacteristic(Characteristic.HeatingThresholdTemperature);\n            let coolThreshTempChar = tstatService.getCharacteristic(Characteristic.CoolingThresholdTemperature);\n            if (!heatThreshTempChar._events.get || !heatThreshTempChar._events.set) {\n                if (!heatThreshTempChar._events.get) {\n                    heatThreshTempChar.on(\"get\", (callback) => {\n                        console.log('heatingSetpoint: ', _accessory.context.deviceData.attributes.heatingSetpoint);\n                        callback(null, this.transforms.thermostatTempConversion(_accessory.context.deviceData.attributes.heatingSetpoint));\n                    });\n                }\n                if (!heatThreshTempChar._events.set) {\n                    heatThreshTempChar.on(\"set\", (value, callback) => {\n                        // Convert the Celsius value to the appropriate unit for Smartthings\n                        let temp = this.transforms.thermostatTempConversion(value, true);\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, \"setHeatingSetpoint\", {\n                            value1: temp\n                        });\n                        _accessory.context.deviceData.attributes.heatingSetpoint = temp;\n                    });\n                }\n                this.accessories.storeCharacteristicItem(\"heatingSetpoint\", _accessory.context.deviceData.deviceid, heatThreshTempChar);\n                this.accessories.storeCharacteristicItem(\"thermostatSetpoint\", _accessory.context.deviceData.deviceid, heatThreshTempChar);\n            } else {\n                heatThreshTempChar.updateValue(this.transforms.thermostatTempConversion(_accessory.context.deviceData.attributes.heatingSetpoint));\n            }\n\n            // COOLING THRESHOLD TEMPERATURE\n            if (!coolThreshTempChar._events.get || !coolThreshTempChar._events.set) {\n                if (!coolThreshTempChar._events.get) {\n                    coolThreshTempChar.on(\"get\", (callback) => {\n                        console.log('coolingSetpoint: ', _accessory.context.deviceData.attributes.coolingSetpoint);\n                        callback(null, this.transforms.thermostatTempConversion(_accessory.context.deviceData.attributes.coolingSetpoint));\n                    });\n                }\n                if (!coolThreshTempChar._events.set) {\n                    coolThreshTempChar.on(\"set\", (value, callback) => {\n                        // Convert the Celsius value to the appropriate unit for Smartthings\n                        let temp = this.transforms.thermostatTempConversion(value, true);\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, \"setCoolingSetpoint\", {\n                            value1: temp\n                        });\n                        _accessory.context.deviceData.attributes.coolingSetpoint = temp;\n                    });\n                }\n                this.accessories.storeCharacteristicItem(\"coolingSetpoint\", _accessory.context.deviceData.deviceid, coolThreshTempChar);\n                this.accessories.storeCharacteristicItem(\"thermostatSetpoint\", _accessory.context.deviceData.deviceid, coolThreshTempChar);\n            } else {\n                coolThreshTempChar.updateValue(this.transforms.thermostatTempConversion(_accessory.context.deviceData.attributes.coolingSetpoint));\n            }\n        } else {\n            tstatService.removeCharacteristic(Characteristic.HeatingThresholdTemperature);\n            tstatService.removeCharacteristic(Characteristic.CoolingThresholdTemperature);\n        }\n        _accessory.context.deviceGroups.push(\"thermostat\");\n        return _accessory;\n    }\n\n    valve(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.InUse, 'valve');\n        _accessory.manageGetSetCharacteristic(_service, _accessory, Characteristic.Active, 'valve');\n        if (!_accessory.hasCharacteristic(_service, Characteristic.ValveType))\n            _accessory.getOrAddService(_service).setCharacteristic(Characteristic.ValveType, 0);\n\n        _accessory.context.deviceGroups.push(\"valve\");\n        return _accessory;\n    }\n\n    virtual_mode(_accessory, _service) {\n        let c = _accessory.getOrAddService(_service).getCharacteristic(Characteristic.On);\n        if (!c._events.get || !c._events.set) {\n            if (!c._events.get)\n                c.on(\"get\", (callback) => {\n                    callback(null, this.transforms.transformAttributeState('switch', _accessory.context.deviceData.attributes.switch));\n                });\n            if (!c._events.set)\n                c.on(\"set\", (value, callback) => {\n                    if (value && (_accessory.context.deviceData.attributes.switch === \"off\")) {\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, \"mode\");\n                    }\n                });\n            this.accessories.storeCharacteristicItem(\"switch\", _accessory.context.deviceData.deviceid, c);\n        } else {\n            c.updateValue(this.transforms.transformAttributeState('switch', _accessory.context.deviceData.attributes.switch));\n        }\n        _accessory.context.deviceGroups.push(\"virtual_mode\");\n        return _accessory;\n    }\n\n    virtual_routine(_accessory, _service) {\n        let c = _accessory.getOrAddService(_service).getCharacteristic(Characteristic.On);\n        if (!c._events.get || !c._events.set) {\n            if (!c._events.get)\n                c.on(\"get\", (callback) => {\n                    callback(null, false);\n                });\n            if (!c._events.set)\n                c.on(\"set\", (value, callback) => {\n                    if (value) {\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, \"routine\");\n                        setTimeout(() => {\n                            console.log(\"routineOff...\");\n                            _accessory.context.deviceData.attributes.switch = \"off\";\n                            c.updateValue(false);\n                        }, 1000);\n                    }\n                });\n            this.accessories.storeCharacteristicItem(\"switch\", _accessory.context.deviceData.deviceid, c);\n        } else {\n            c.updateValue(this.transforms.transformAttributeState('switch', _accessory.context.deviceData.attributes.switch));\n        }\n        _accessory.context.deviceGroups.push(\"virtual_routine\");\n        return _accessory;\n    }\n\n    water_sensor(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.LeakDetected, 'water');\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusActive, 'status');\n        if (_accessory.hasCapability('Tamper Alert'))\n            _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.StatusTampered, 'tamper');\n        _accessory.context.deviceGroups.push(\"window_shade\");\n        return _accessory;\n    }\n\n    window_shade(_accessory, _service) {\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.CurrentPosition, 'level', {\n            props: {\n                steps: 10\n            }\n        });\n        let c = _accessory.getOrAddService(_service).getCharacteristic(Characteristic.TargetPosition);\n        if (!c._events.get || !c._events.set) {\n            if (!c._events.get) {\n                c.on(\"get\", (callback) => {\n                    callback(null, parseInt(_accessory.context.deviceData.attributes.level));\n                });\n            }\n            if (!c._events.set) {\n                c.on(\"set\", (value, callback) => {\n                    if (_accessory.hasCommand('close') && value <= 2) {\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, \"close\");\n                    } else {\n                        let v = value;\n                        if (value <= 2) v = 0;\n                        if (value >= 98) v = 100;\n                        _accessory.sendCommand(callback, _accessory, _accessory.context.deviceData, \"setLevel\", {\n                            value1: v\n                        });\n                    }\n                });\n            }\n            this.accessories.storeCharacteristicItem(\"level\", _accessory.context.deviceData.deviceid, c);\n        } else {\n            c.updateValue(this.transforms.transformAttributeState('level', _accessory.context.deviceData.attributes.level));\n        }\n        _accessory.manageGetCharacteristic(_service, _accessory, Characteristic.PositionState, 'windowShade');\n        _accessory.getOrAddService(_service).getCharacteristic(Characteristic.ObstructionDetected).updateValue(false);\n        _accessory.getOrAddService(_service).getCharacteristic(Characteristic.HoldPosition).updateValue(false);\n        _accessory.context.deviceGroups.push(\"window_shade\");\n        return _accessory;\n    }\n};\n"
  },
  {
    "path": "src/ST_Platform.js",
    "content": "const {\n    pluginName,\n    platformName,\n    platformDesc,\n    pluginVersion\n} = require(\"./libs/Constants\"),\n    events = require('events'),\n    myUtils = require(\"./libs/MyUtils\"),\n    SmartThingsClient = require(\"./ST_Client\"),\n    SmartThingsAccessories = require(\"./ST_Accessories\"),\n    express = require(\"express\"),\n    bodyParser = require(\"body-parser\"),\n    chalk = require('chalk'),\n    Logging = require(\"./libs/Logger\"),\n    webApp = express(),\n    // os = require('os'),\n    portFinderSync = require('portfinder-sync');\n\nvar PlatformAccessory;\n\nmodule.exports = class ST_Platform {\n    constructor(log, config, api) {\n        this.config = config;\n        this.homebridge = api;\n        this.Service = api.hap.Service;\n        this.Characteristic = api.hap.Characteristic;\n        PlatformAccessory = api.platformAccessory;\n        this.uuid = api.hap.uuid;\n        if (config === undefined || config === null || config.app_url === undefined || config.app_url === null || config.app_id === undefined || config.app_id === null) {\n            log(`${platformName} Plugin is not Configured | Skipping...`);\n            return;\n        }\n        this.ok2Run = true;\n        this.direct_port = this.findDirectPort();\n        this.logConfig = this.getLogConfig();\n        this.appEvts = new events.EventEmitter();\n        this.logging = new Logging(this, this.config[\"name\"], this.logConfig);\n        this.log = this.logging.getLogger();\n        this.log.info(`Homebridge Version: ${api.version}`);\n        this.log.info(`${platformName} Plugin Version: ${pluginVersion}`);\n        this.polling_seconds = config.polling_seconds || 3600;\n        this.excludedAttributes = this.config.excluded_attributes || [];\n        this.excludedCapabilities = this.config.excluded_capabilities || [];\n        this.update_method = this.config.update_method || \"direct\";\n        this.temperature_unit = this.config.temperature_unit || \"F\";\n        this.local_commands = this.config.local_commands || false;\n        this.local_hub_ip = undefined;\n        this.myUtils = new myUtils(this);\n        this.configItems = this.getConfigItems();\n        this.unknownCapabilities = [];\n        this.client = new SmartThingsClient(this);\n        this.SmartThingsAccessories = new SmartThingsAccessories(this);\n        this.homebridge.on(\"didFinishLaunching\", this.didFinishLaunching.bind(this));\n        this.appEvts.emit('event:plugin_upd_status');\n    }\n\n    getLogConfig() {\n        let config = this.config;\n        return (config.logConfig) ? {\n            debug: (config.logConfig.debug === true),\n            showChanges: (config.logConfig.showChanges === true),\n            hideTimestamp: (config.logConfig.hideTimestamp === true),\n            hideNamePrefix: (config.logConfig.hideNamePrefix === true),\n            file: {\n                enabled: (config.logConfig.file.enabled === true),\n                level: (config.logConfig.file.level || 'good')\n            }\n        } : {\n            debug: false,\n            showChanges: true,\n            hideTimestamp: false,\n            hideNamePrefix: false\n        };\n    }\n\n    findDirectPort() {\n        let port = this.config.direct_port || 8000;\n        if (port)\n            port = portFinderSync.getPort(port);\n        return this.direct_port = port;\n    }\n\n    getConfigItems() {\n        return {\n            app_url: this.config.app_url,\n            app_id: this.config.app_id,\n            access_token: this.config.access_token,\n            update_seconds: this.config.update_seconds || 30,\n            direct_port: this.direct_port,\n            direct_ip: this.config.direct_ip || this.myUtils.getIPAddress(),\n            debug: (this.config.debug === true),\n            local_commands: (this.config.local_commands === true),\n            validateTokenId: (this.config.validateTokenId === true)\n        };\n    }\n\n    updateTempUnit(unit) {\n        this.log.notice(`Temperature Unit is Now: (${unit})`);\n        this.temperature_unit = unit;\n    }\n\n    getTempUnit() {\n        return this.temperature_unit;\n    }\n\n    didFinishLaunching() {\n        this.log.info(`Fetching ${platformName} Devices. NOTICE: This may take a moment if you have a large number of device data is being loaded!`);\n        setInterval(this.refreshDevices.bind(this), this.polling_seconds * 1000);\n        let that = this;\n        this.refreshDevices('First Launch')\n            .then(() => {\n                that.WebServerInit(that)\n                    .catch(err => that.log.error(\"WebServerInit Error: \", err))\n                    .then(resp => {\n                        if (resp && resp.status === \"OK\") this.appEvts.emit('event:plugin_start_direct');;\n                    });\n            })\n            .catch(err => {\n                that.log.error(`didFinishLaunching | refreshDevices Exception:`, err);\n            });\n    }\n\n    refreshDevices(src = undefined) {\n        let that = this;\n        let starttime = new Date();\n        return new Promise((resolve, reject) => {\n            try {\n                that.log.good(`Refreshing All Device Data${src ? ' | Source: (' + src + ')' : \"\"}`);\n                this.client.getDevices()\n                    .catch(err => {\n                        that.log.error('getDevices Exception:', err);\n                        reject(err.message);\n                    })\n                    .then(resp => {\n                        if (resp && resp.location) {\n                            that.updateTempUnit(resp.location.temperature_scale);\n                            if (resp.location.hubIP) {\n                                that.local_hub_ip = resp.location.hubIP;\n                                that.local_commands = resp.location.local_commands === true;\n                                that.client.updateGlobals(that.local_hub_ip, that.local_commands);\n                            }\n                        }\n                        if (resp && resp.deviceList && resp.deviceList instanceof Array) {\n                            // that.log.debug(\"Received All Device Data\");\n                            const toCreate = this.SmartThingsAccessories.diffAdd(resp.deviceList);\n                            const toUpdate = this.SmartThingsAccessories.intersection(resp.deviceList);\n                            const toRemove = this.SmartThingsAccessories.diffRemove(resp.deviceList);\n                            that.log.warn(`Devices to Remove: (${Object.keys(toRemove).length})`, toRemove.map(i => i.name));\n                            that.log.info(`Devices to Update: (${Object.keys(toUpdate).length})`);\n                            that.log.good(`Devices to Create: (${Object.keys(toCreate).length})`, toCreate.map(i => i.name));\n\n                            toRemove.forEach(accessory => this.removeAccessory(accessory));\n                            toUpdate.forEach(device => this.updateDevice(device));\n                            toCreate.forEach(device => this.addDevice(device));\n                        }\n                        that.log.alert(`Total Initialization Time: (${Math.round((new Date() - starttime) / 1000)} seconds)`);\n                        that.log.notice(`Unknown Capabilities: ${JSON.stringify(that.unknownCapabilities)}`);\n                        that.log.info(`${platformDesc} DeviceCache Size: (${Object.keys(this.SmartThingsAccessories.getAllAccessoriesFromCache()).length})`);\n                        if (src !== 'First Launch') this.appEvts.emit('event:plugin_upd_status');\n                        resolve(true);\n                    });\n\n            } catch (ex) {\n                this.log.error(\"refreshDevices Error: \", ex);\n                resolve(false);\n            }\n        });\n    }\n\n    getNewAccessory(device, UUID) {\n        let accessory = new PlatformAccessory(device.name, UUID);\n        accessory.context.deviceData = device;\n        this.SmartThingsAccessories.initializeAccessory(accessory);\n        return accessory;\n    }\n\n    addDevice(device) {\n        let accessory;\n        const new_uuid = this.uuid.generate(`smartthings_v2_${device.deviceid}`);\n        device.excludedCapabilities = this.excludedCapabilities[device.deviceid] || [];\n        this.log.debug(`Initializing New Device (${device.name} | ${device.deviceid})`);\n        accessory = this.getNewAccessory(device, new_uuid);\n        this.homebridge.registerPlatformAccessories(pluginName, platformName, [accessory]);\n        this.SmartThingsAccessories.addAccessoryToCache(accessory);\n        this.log.info(`Added Device: (${accessory.name} | ${accessory.deviceid})`);\n    }\n\n    updateDevice(device) {\n        let cachedAccessory = this.SmartThingsAccessories.getAccessoryFromCache(device);\n        device.excludedCapabilities = this.excludedCapabilities[device.deviceid] || [];\n        cachedAccessory.context.deviceData = device;\n        this.log.debug(`Loading Existing Device (${device.name}) | (${device.deviceid})`);\n        cachedAccessory = this.SmartThingsAccessories.initializeAccessory(cachedAccessory);\n        this.SmartThingsAccessories.addAccessoryToCache(cachedAccessory);\n    }\n\n    removeAccessory(accessory) {\n        if (this.SmartThingsAccessories.removeAccessoryFromCache(accessory)) {\n            this.homebridge.unregisterPlatformAccessories(pluginName, platformName, [accessory]);\n            this.log.info(`Removed: ${accessory.context.name} (${accessory.context.deviceid})`);\n        }\n    }\n\n    configureAccessory(accessory) {\n        if (!this.ok2Run) return;\n        this.log.debug(`Configure Cached Accessory: ${accessory.displayName}, UUID: ${accessory.UUID}`);\n        let cachedAccessory = this.SmartThingsAccessories.initializeAccessory(accessory, true);\n        this.SmartThingsAccessories.addAccessoryToCache(cachedAccessory);\n    }\n\n    processIncrementalUpdate(data, that) {\n        that.log.debug(\"new data: \" + data);\n        if (data && data.attributes && data.attributes instanceof Array) {\n            for (let i = 0; i < data.attributes.length; i++) {\n                that.processDeviceAttributeUpdate(data.attributes[i], that);\n            }\n        }\n    }\n\n    isValidRequestor(access_token, app_id, src) {\n        if (this.configItems.validateTokenId !== true) {\n            return true;\n        }\n        if (app_id && access_token && (access_token === this.getConfigItems().access_token) && (app_id === this.getConfigItems().app_id)) return true;\n        this.log.error(`(${src}) | We received a request from a client that didn't provide a valid access_token and app_id`);\n        return false;\n    }\n\n    WebServerInit() {\n        let that = this;\n        // Get the IP address that we will send to the SmartApp. This can be overridden in the config file.\n        return new Promise(resolve => {\n            try {\n                let ip = that.configItems.direct_ip || that.myUtils.getIPAddress();\n                that.log.info(\"WebServer Initiated...\");\n\n                // Start the HTTP Server\n                webApp.listen(that.configItems.direct_port, () => {\n                    that.log.info(`Direct Connect Active | Listening at ${ip}:${that.configItems.direct_port}`);\n                });\n\n                webApp.use(bodyParser.urlencoded({\n                    extended: false\n                }));\n                webApp.use(bodyParser.json());\n                webApp.use((req, res, next) => {\n                    res.header(\"Access-Control-Allow-Origin\", \"*\");\n                    res.header(\"Access-Control-Allow-Headers\", \"Origin, X-Requested-With, Content-Type, Accept\");\n                    next();\n                });\n\n                webApp.get(\"/\", (req, res) => {\n                    res.send(\"WebApp is running...\");\n                });\n\n                webApp.post(\"/initial\", (req, res) => {\n                    let body = JSON.parse(JSON.stringify(req.body));\n                    if (body && that.isValidRequestor(body.access_token, body.app_id, 'initial')) {\n                        that.log.info(`${platformName} Hub Communication Established`);\n                        res.send({\n                            status: \"OK\"\n                        });\n                    } else {\n                        res.send({\n                            status: \"Failed: Missing access_token or app_id\"\n                        });\n                    }\n                });\n\n                webApp.get(\"/debugOpts\", (req, res) => {\n                    that.log.info(`${platformName} Debug Option Request(${req.query.option})...`);\n                    if (req.query && req.query.option) {\n                        let accs = this.SmartThingsAccessories.getAllAccessoriesFromCache();\n                        // let accsKeys = Object.keys(accs);\n                        // console.log(accsKeys);\n                        switch (req.query.option) {\n                            case 'allAccData':\n                                res.send(JSON.stringify(accs));\n                                break;\n                                // case 'accServices':\n                                //     var o = accsKeys.forEach(s => s.services.forEach(s1 => s1.UUID));\n                                //     res.send(JSON.stringify(o));\n                                //     break;\n                                // case 'accCharacteristics':\n                                //     var o = accsKeys.forEach(s => s.services.forEach(s1 => s1.characteristics.forEach(c => c.displayName)));\n                                //     res.send(JSON.stringify(o));\n                                //     break;\n                                // case 'accContext':\n                                //     res.send(JSON.stringify(this.SmartThingsAccessories.getAllAccessoriesFromCache()));\n                                //     break;\n                            default:\n                                res.send(`Error: Invalid Option Parameter Received | Option: ${req.query.option}`);\n                                break;\n                        }\n\n                    } else {\n                        res.send('Error: Missing Valid Debug Query Parameter');\n                    }\n                });\n\n                webApp.post(\"/restartService\", (req, res) => {\n                    let body = JSON.parse(JSON.stringify(req.body));\n                    if (body && that.isValidRequestor(body.access_token, body.app_id, 'restartService')) {\n                        let delay = 10 * 1000;\n                        that.log.info(`Received request from ${platformName} to restart homebridge service in (${(delay / 1000)} seconds) | NOTICE: If you using PM2 or Systemd the Homebridge Service should start back up`);\n                        setTimeout(() => {\n                            process.exit(1);\n                        }, parseInt(delay));\n                        res.send({\n                            status: \"OK\"\n                        });\n                    } else {\n                        res.send({\n                            status: \"Failed: Missing access_token or app_id\"\n                        });\n                    }\n                });\n\n                webApp.post(\"/refreshDevices\", (req, res) => {\n                    let body = JSON.parse(JSON.stringify(req.body));\n                    if (body && that.isValidRequestor(body.access_token, body.app_id, 'refreshDevices')) {\n                        that.log.good(`Received request from ${platformName} to refresh devices`);\n                        that.refreshDevices(\"ST Requested\");\n                        res.send({\n                            status: \"OK\"\n                        });\n                    } else {\n                        that.log.error(`Unable to start device refresh because we didn't receive a valid access_token and app_id`);\n                        res.send({\n                            status: \"Failed: Missing access_token or app_id\"\n                        });\n                    }\n                });\n\n                webApp.post(\"/updateprefs\", (req, res) => {\n                    let body = JSON.parse(JSON.stringify(req.body));\n                    if (body && that.isValidRequestor(body.access_token, body.app_id, 'updateprefs')) {\n                        that.log.info(platformName + \" Hub Sent Preference Updates\");\n                        let sendUpd = false;\n                        if (body.local_commands && that.local_commands !== body.local_commands) {\n                            sendUpd = true;\n                            that.log.info(`${platformName} Updated Local Commands Preference | Before: ${that.local_commands} | Now: ${body.local_commands}`);\n                            that.local_commands = body.local_commands;\n                        }\n                        if (body.local_hub_ip && that.local_hub_ip !== body.local_hub_ip) {\n                            sendUpd = true;\n                            that.log.info(`${platformName} Updated Hub IP Preference | Before: ${that.local_hub_ip} | Now: ${body.local_hub_ip}`);\n                            that.local_hub_ip = body.local_hub_ip;\n                        }\n                        if (sendUpd) {\n                            that.client.updateGlobals(that.local_hub_ip, that.local_commands);\n                        }\n                        res.send({\n                            status: \"OK\"\n                        });\n                    } else {\n                        res.send({\n                            status: \"Failed: Missing access_token or app_id\"\n                        });\n                    }\n                });\n\n                webApp.post(\"/update\", (req, res) => {\n                    if (req.body.length < 3) return;\n                    let body = JSON.parse(JSON.stringify(req.body));\n                    if (body && that.isValidRequestor(body.access_token, body.app_id, 'update')) {\n                        if (Object.keys(body).length > 3) {\n                            let newChange = {\n                                deviceid: body.change_device,\n                                attribute: body.change_attribute,\n                                value: body.change_value,\n                                data: body.change_data,\n                                date: body.change_date\n                            };\n                            that.SmartThingsAccessories.processDeviceAttributeUpdate(newChange)\n                                .then((resp) => {\n                                    if (that.logConfig.showChanges) {\n                                        that.log.info(chalk `[{keyword('orange') Device Event}]: ({blueBright ${body.change_name}}) [{yellow.bold ${(body.change_attribute ? body.change_attribute.toUpperCase() : \"unknown\")}}] is {keyword('pink') ${body.change_value}}`);\n                                    }\n                                    res.send({\n                                        evtSource: `Homebridge_${platformName}_${this.configItems.app_id}`,\n                                        evtType: 'attrUpdStatus',\n                                        evtDevice: body.change_name,\n                                        evtAttr: body.change_attribute,\n                                        evtStatus: resp ? \"OK\" : \"Failed\"\n                                    });\n                                });\n                        } else {\n                            res.send({\n                                evtSource: `Homebridge_${platformName}_${this.configItems.app_id}`,\n                                evtType: 'attrUpdStatus',\n                                evtDevice: body.change_name,\n                                evtAttr: body.change_attribute,\n                                evtStatus: \"Failed\"\n                            });\n                        }\n\n                    } else {\n                        res.send({\n                            status: \"Failed: Missing access_token or app_id\"\n                        });\n                    }\n                });\n                resolve({\n                    status: \"OK\"\n                });\n            } catch (ex) {\n                that.log.error('WebServerInit Exception: ', ex.message);\n                resolve({\n                    status: ex.message\n                });\n            }\n        });\n    }\n};"
  },
  {
    "path": "src/ST_ServiceTypes.js",
    "content": "// const debounce = require('debounce-promise');\nvar Service;\n\nmodule.exports = class ServiceTypes {\n    constructor(accessories, srvc) {\n        this.platform = accessories;\n        this.log = accessories.log;\n        this.logConfig = accessories.logConfig;\n        this.accessories = accessories;\n        this.client = accessories.client;\n        this.myUtils = accessories.myUtils;\n        this.CommunityTypes = accessories.CommunityTypes;\n        Service = srvc;\n        this.homebridge = accessories.homebridge;\n        this.serviceMap = {\n            acceleration_sensor: Service.MotionSensor,\n            air_purifier: this.CommunityTypes.NewAirPurifierService,\n            air_quality: Service.AirQualitySensor,\n            alarm_system: Service.SecuritySystem,\n            battery: Service.BatteryService,\n            button: Service.StatelessProgrammableSwitch,\n            carbon_dioxide: Service.CarbonDioxideSensor,\n            carbon_monoxide: Service.CarbonMonoxideSensor,\n            contact_sensor: Service.ContactSensor,\n            // energy_meter: Service.Switch,\n            fan: Service.Fanv2,\n            garage_door: Service.GarageDoorOpener,\n            humidity_sensor: Service.HumiditySensor,\n            illuminance_sensor: Service.LightSensor,\n            light: Service.Lightbulb,\n            lock: Service.LockMechanism,\n            motion_sensor: Service.MotionSensor,\n            // power_meter: Service.Switch,\n            presence_sensor: Service.OccupancySensor,\n            smoke_detector: Service.SmokeSensor,\n            speaker: Service.Speaker,\n            switch_device: Service.Switch,\n            temperature_sensor: Service.TemperatureSensor,\n            thermostat: Service.Thermostat,\n            valve: Service.Valve,\n            virtual_mode: Service.Switch,\n            virtual_routine: Service.Switch,\n            water_sensor: Service.LeakSensor,\n            window_shade: Service.WindowCovering\n        };\n    }\n\n    getServiceTypes(accessory) {\n        let servicesFound = [];\n        let servicesBlocked = [];\n        for (let i = 0; i < serviceTests.length; i++) {\n            const svcTest = serviceTests[i];\n            if (svcTest.ImplementsService(accessory)) {\n                // console.log(svcTest.Name);\n                const blockSvc = (svcTest.onlyOnNoGrps === true && servicesFound.length > 0);\n                if (blockSvc) {\n                    servicesBlocked.push(svcTest.Name);\n                    this.log.debug(`(${accessory.name}) | Service BLOCKED | name: ${svcTest.Name} | Cnt: ${servicesFound.length} | svcs: ${JSON.stringify(servicesFound)}`);\n                }\n                if (!blockSvc && this.serviceMap[svcTest.Name]) {\n                    servicesFound.push({\n                        name: svcTest.Name,\n                        type: this.serviceMap[svcTest.Name]\n                    });\n                }\n            }\n        }\n        if (servicesBlocked.length) {\n            this.log.debug(`(${accessory.name}) | Services BLOCKED | ${servicesBlocked}`);\n        }\n        return servicesFound;\n    }\n\n    lookupServiceType(name) {\n        if (this.serviceMap[name]) {\n            return this.serviceMap[name];\n        }\n        return null;\n    }\n};\n\nclass ServiceTest {\n    constructor(name, testfn, onlyOnNoGrps = false) {\n        this.ImplementsService = testfn;\n        this.Name = name;\n        this.onlyOnNoGrps = (onlyOnNoGrps !== false);\n    }\n}\n\n//TODO: Build out the access into capabilitiy map { hasSwitch: true, hasWater: false, hasPower: false }\n//TODO: Use those to help filter out the ServiceTest items below instead of a long line of accessory.hasCapability and hasAttribute.\n//TODO: or break each comparision item into a promise.all type logic.\n\n// NOTE: These Tests are executed in order which is important\nconst serviceTests = [\n    new ServiceTest(\"window_shade\", accessory => (\n        (\n            accessory.hasCapability('Window Shade') ||\n            accessory.hasCapability('WindowShade')\n        ) &&\n        !(\n            accessory.hasCapability('Speaker') ||\n            accessory.hasCapability('Fan') ||\n            accessory.hasCapability('Fan Light') ||\n            accessory.hasCapability('Fan Speed') ||\n            accessory.hasCapability('Fan Control') ||\n            accessory.hasCommand('setFanSpeed') ||\n            accessory.hasCommand('lowSpeed') ||\n            accessory.hasAttribute('fanSpeed') ||\n            accessory.hasCapability('custom.airPurifierOperationMode')\n        )\n    ), true),\n    new ServiceTest(\"light\", accessory => (accessory.hasCapability('Switch Level') && (accessory.hasCapability('LightBulb') || accessory.hasCapability('Fan Light') || accessory.hasCapability('Bulb') || accessory.context.deviceData.name.includes('light') || accessory.hasAttribute('saturation') || accessory.hasAttribute('hue') || accessory.hasAttribute('colorTemperature') || accessory.hasCapability(\"Color Control\"))), true),\n    new ServiceTest(\"air_purifier\", accessory => accessory.hasCapability('custom.airPurifierOperationMode')),\n    new ServiceTest(\"garage_door\", accessory => accessory.hasCapability(\"Garage Door Control\")),\n    new ServiceTest(\"lock\", accessory => accessory.hasCapability(\"Lock\")),\n    new ServiceTest(\"valve\", accessory => accessory.hasCapability(\"Valve\")),\n    new ServiceTest(\"speaker\", accessory => accessory.hasCapability('Speaker')),\n    new ServiceTest(\"fan\", accessory => ((accessory.hasCapability('Fan') || accessory.hasCapability('Fan Light') || accessory.hasCapability('Fan Speed') || accessory.hasCapability('Fan Control') || accessory.hasCommand('setFanSpeed') || accessory.hasCommand('lowSpeed') || accessory.hasAttribute('fanSpeed'))), true),\n    new ServiceTest(\"virtual_mode\", accessory => accessory.hasCapability(\"Mode\")),\n    new ServiceTest(\"virtual_routine\", accessory => accessory.hasCapability(\"Routine\")),\n    new ServiceTest(\"button\", accessory => accessory.hasCapability(\"Button\")),\n    new ServiceTest(\"light\", accessory => (accessory.hasCapability('Switch') && (accessory.hasCapability('LightBulb') || accessory.hasCapability('Fan Light') || accessory.hasCapability('Bulb') || accessory.context.deviceData.name.toLowerCase().includes('light'))), true),\n    new ServiceTest(\"switch_device\", accessory => (accessory.hasCapability('Switch') && !(accessory.hasCapability('LightBulb') || accessory.hasCapability('Fan Light') || accessory.hasCapability('Bulb') || accessory.context.deviceData.name.toLowerCase().includes('light') || accessory.hasCapability('Button'))), true),\n    new ServiceTest(\"smoke_detector\", accessory => accessory.hasCapability(\"Smoke Detector\") && accessory.hasAttribute('smoke')),\n    new ServiceTest(\"carbon_monoxide\", accessory => accessory.hasCapability(\"Carbon Monoxide Detector\") && accessory.hasAttribute('carbonMonoxide')),\n    new ServiceTest(\"carbon_dioxide\", accessory => accessory.hasCapability(\"Carbon Dioxide Measurement\") && accessory.hasAttribute('carbonDioxideMeasurement')),\n    new ServiceTest(\"motion_sensor\", accessory => (accessory.hasCapability(\"Motion Sensor\"))),\n    new ServiceTest(\"acceleration_sensor\", accessory => (accessory.hasCapability(\"Acceleration Sensor\"))),\n    new ServiceTest(\"water_sensor\", accessory => (accessory.hasCapability(\"Water Sensor\"))),\n    new ServiceTest(\"presence_sensor\", accessory => (accessory.hasCapability(\"Presence Sensor\"))),\n    new ServiceTest(\"humidity_sensor\", accessory => (accessory.hasCapability(\"Relative Humidity Measurement\") && !(accessory.hasCapability('Thermostat') || accessory.hasCapability('Thermostat Operating State') || accessory.hasAttribute('thermostatOperatingState')))),\n    new ServiceTest(\"temperature_sensor\", accessory => (accessory.hasCapability(\"Temperature Measurement\") && !(accessory.hasCapability('Thermostat') || accessory.hasCapability('Thermostat Operating State') || accessory.hasAttribute('thermostatOperatingState')))),\n    new ServiceTest(\"illuminance_sensor\", accessory => (accessory.hasCapability(\"Illuminance Measurement\"))),\n    new ServiceTest(\"contact_sensor\", accessory => (accessory.hasCapability('Contact Sensor') && !accessory.hasCapability('Garage Door Control'))),\n    new ServiceTest(\"air_quality\", accessory => (accessory.hasCapability('airQuality'))),\n    new ServiceTest(\"battery\", accessory => (accessory.hasCapability('Battery'))),\n    // new ServiceTest(\"energy_meter\", accessory => (accessory.hasCapability('Energy Meter') && !accessory.hasCapability('Switch')), true),\n    // new ServiceTest(\"power_meter\", accessory => (accessory.hasCapability('Power Meter') && !accessory.hasCapability('Switch')), true),\n    new ServiceTest(\"thermostat\", accessory => (accessory.hasCapability('Thermostat') || accessory.hasCapability('Thermostat Operating State') || accessory.hasAttribute('thermostatOperatingState'))),\n    new ServiceTest(\"alarm_system\", accessory => (accessory.hasAttribute(\"alarmSystemStatus\")))\n];"
  },
  {
    "path": "src/ST_Transforms.js",
    "content": "var Characteristic;\nvar CommunityTypes;\n\nmodule.exports = class Transforms {\n    constructor(platform, char) {\n        this.accessories = platform;\n        this.platform = platform.mainPlatform;\n        this.client = platform.client;\n        Characteristic = char;\n        CommunityTypes = platform.CommunityTypes;\n        this.log = platform.log;\n    }\n\n    transformStatus(val) {\n        val = val.toLowerCase() || undefined;\n        switch (val) {\n            case \"online\":\n            case \"active\":\n                return true;\n            default:\n                return false;\n        }\n    }\n\n    transformAttributeState(attr, val, charName) {\n        switch (attr) {\n            case \"switch\":\n                return (val === 'on');\n            case \"door\":\n                switch (val) {\n                    case \"open\":\n                        return Characteristic.TargetDoorState.OPEN;\n                    case \"opening\":\n                        return charName && charName === \"Target Door State\" ? Characteristic.TargetDoorState.OPEN : Characteristic.TargetDoorState.OPENING;\n                    case \"closed\":\n                        return Characteristic.TargetDoorState.CLOSED;\n                    case \"closing\":\n                        return charName && charName === \"Target Door State\" ? Characteristic.TargetDoorState.CLOSED : Characteristic.TargetDoorState.CLOSING;\n                    default:\n                        return charName && charName === \"Target Door State\" ? Characteristic.TargetDoorState.OPEN : Characteristic.TargetDoorState.STOPPED;\n                }\n\n            case \"fanMode\":\n                switch (val) {\n                    case \"low\":\n                        return CommunityTypes.FanOscilationMode.LOW;\n                    case \"medium\":\n                        return CommunityTypes.FanOscilationMode.MEDIUM;\n                    case \"high\":\n                        return CommunityTypes.FanOscilationMode.HIGH;\n                    default:\n                        return CommunityTypes.FanOscilationMode.SLEEP;\n                }\n\n            case \"lock\":\n                switch (val) {\n                    case \"locked\":\n                        return Characteristic.LockCurrentState.SECURED;\n                    case \"unlocked\":\n                        return Characteristic.LockCurrentState.UNSECURED;\n                    default:\n                        return Characteristic.LockCurrentState.UNKNOWN;\n                }\n\n            case \"button\":\n                switch (val) {\n                    case \"pushed\":\n                        return Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS;\n                    case \"held\":\n                        return Characteristic.ProgrammableSwitchEvent.LONG_PRESS;\n                    case \"double\":\n                        return Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS;\n                    default:\n                        return null;\n                }\n            case \"supportedButtonValues\":\n                var validValues = [];\n                if (typeof val === \"string\") {\n                    for (const v of JSON.parse(val)) {\n                        switch (v) {\n                            case \"pushed\":\n                                validValues.push(Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);\n                                continue;\n                            case \"double\":\n                                validValues.push(Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS);\n                                continue;\n                            case \"held\":\n                                validValues.push(Characteristic.ProgrammableSwitchEvent.LONG_PRESS);\n                                continue;\n                            default:\n                                validValues.push(Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);\n                                validValues.push(Characteristic.ProgrammableSwitchEvent.LONG_PRESS);\n                                continue;\n                        }\n                    }\n                } else {\n                    validValues.push(Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);\n                    validValues.push(Characteristic.ProgrammableSwitchEvent.LONG_PRESS);\n                }\n                return validValues;\n            case \"fanState\":\n                return (val === \"off\") ? Characteristic.CurrentFanState.IDLE : Characteristic.CurrentFanState.BLOWING_AIR;\n            case \"valve\":\n                return (val === \"open\") ? Characteristic.InUse.IN_USE : Characteristic.InUse.NOT_IN_USE;\n            case \"mute\":\n                return (val === 'muted');\n            case \"smoke\":\n                return (val === \"clear\") ? Characteristic.SmokeDetected.SMOKE_NOT_DETECTED : Characteristic.SmokeDetected.SMOKE_DETECTED;\n            case \"carbonMonoxide\":\n                return (val === \"clear\") ? Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL : Characteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL;\n            case \"carbonDioxideMeasurement\":\n                switch (charName) {\n                    case \"Carbon Dioxide Detected\":\n                        return (val < 2000) ? Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL : Characteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL;\n                    default:\n                        return parseInt(val);\n                }\n            case \"tamper\":\n                return (val === \"detected\") ? Characteristic.StatusTampered.TAMPERED : Characteristic.StatusTampered.NOT_TAMPERED;\n            case \"acceleration\":\n            case \"motion\":\n                return (val === \"active\");\n            case \"water\":\n                return (val === \"dry\") ? Characteristic.LeakDetected.LEAK_NOT_DETECTED : Characteristic.LeakDetected.LEAK_DETECTED;\n            case \"contact\":\n                return (val === \"closed\") ? Characteristic.ContactSensorState.CONTACT_DETECTED : Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;\n            case \"presence\":\n                return (val === \"present\");\n            case \"battery\":\n                if (charName === \"Status Low Battery\") {\n                    return (val < 20) ? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;\n                } else {\n                    return Math.round(val);\n                }\n            case \"batteryStatus\":\n                return (val === \"USB Cable\") ? Characteristic.ChargingState.CHARGING : Characteristic.ChargingState.NOT_CHARGING;\n            case \"hue\":\n                return Math.round(val * 3.6);\n            case \"colorTemperature\":\n                return this.colorTempFromK(val);\n            case \"temperature\":\n                return this.tempConversion(val);\n            case \"heatingSetpoint\":\n            case \"coolingSetpoint\":\n            case \"thermostatSetpoint\":\n                return this.thermostatTempConversion(val);\n            case \"fanSpeed\":\n                return this.fanSpeedIntToLevel(val);\n            case \"level\":\n            case \"saturation\":\n            case \"volume\":\n                return parseInt(val) || 0;\n            case \"illuminance\":\n                return Math.round(Math.ceil(parseFloat(val)), 0);\n\n            case \"energy\":\n            case \"humidity\":\n            case \"power\":\n                return Math.round(val);\n            case \"thermostatOperatingState\":\n                switch (val) {\n                    case \"pending cool\":\n                    case \"cooling\":\n                        return Characteristic.CurrentHeatingCoolingState.COOL;\n                    case \"pending heat\":\n                    case \"heating\":\n                        return Characteristic.CurrentHeatingCoolingState.HEAT;\n                    default:\n                        // The above list should be inclusive, but we need to return something if they change stuff.\n                        // TODO: Double check if Smartthings can send \"auto\" as operatingstate. I don't think it can.\n                        return Characteristic.CurrentHeatingCoolingState.OFF;\n                }\n            case \"thermostatMode\":\n                switch (val) {\n                    case \"cool\":\n                        return Characteristic.TargetHeatingCoolingState.COOL;\n                    case \"emergency heat\":\n                    case \"heat\":\n                        return Characteristic.TargetHeatingCoolingState.HEAT;\n                    case \"auto\":\n                        return Characteristic.TargetHeatingCoolingState.AUTO;\n                    default:\n                        return Characteristic.TargetHeatingCoolingState.OFF;\n                }\n            case \"thermostatFanMode\":\n                if (val === Characteristic.TargetFanState.MANUAL) {\n                    return 'on';\n                } else {\n                    return 'auto';\n                }\n\n            case \"windowShade\":\n                if (val === 'opening') {\n                    return Characteristic.PositionState.INCREASING;\n                } else if (val === 'closing') {\n                    return Characteristic.PositionState.DECREASING;\n                } else {\n                    return Characteristic.PositionState.STOPPED;\n                }\n            case \"alarmSystemStatus\":\n                return this.convertAlarmState(val);\n            default:\n                return val;\n        }\n    }\n\n    transformCommandName(attr, val) {\n        switch (attr) {\n            case \"valve\":\n                return (val === 1 || val === true) ? \"open\" : \"close\";\n            case \"switch\":\n                return (val === 1 || val === true) ? \"on\" : \"off\";\n            case \"door\":\n                if (val === Characteristic.TargetDoorState.OPEN || val === 0) {\n                    return \"open\";\n                } else {\n                    return \"close\";\n                }\n\n            case \"lock\":\n                return (val === 1 || val === true) ? \"lock\" : \"unlock\";\n            case \"mute\":\n                return (val === \"muted\") ? \"mute\" : \"unmute\";\n            case \"fanSpeed\":\n            case \"level\":\n            case \"volume\":\n            case \"thermostatMode\":\n            case \"saturation\":\n            case \"hue\":\n            case \"colorTemperature\":\n                return `set${attr.charAt(0).toUpperCase() + attr.slice(1)}`;\n            case \"thermostatFanMode\":\n                switch (val) {\n                    case Characteristic.TargetFanState.MANUAL:\n                        return \"fanOn\";\n                    default:\n                        return \"fanAuto\";\n                }\n            default:\n                return val;\n        }\n    }\n\n    transformCommandValue(attr, val) {\n        switch (attr) {\n            case \"valve\":\n                return (val === 1 || val === true) ? \"open\" : \"close\";\n            case \"switch\":\n                return (val === 1 || val === true) ? \"on\" : \"off\";\n            case \"lock\":\n                return (val === 1 || val === true) ? \"lock\" : \"unlock\";\n            case \"door\":\n                if (val === Characteristic.TargetDoorState.OPEN || val === 0) {\n                    return \"open\";\n                } else if (val === Characteristic.TargetDoorState.CLOSED || val === 1) {\n                    return \"close\";\n                }\n                return 'closing';\n            case \"hue\":\n                return Math.round(val / 3.6);\n            case \"colorTemperature\":\n                return this.colorTempToK(val);\n            case \"mute\":\n                return (val === \"muted\") ? \"mute\" : \"unmute\";\n            case \"alarmSystemStatus\":\n                return this.convertAlarmCmd(val);\n            case \"fanSpeed\":\n                if (val === 0) {\n                    return 0;\n                } else if (val < 34) {\n                    return 1;\n                } else if (val < 67) {\n                    return 2;\n                } else {\n                    return 3;\n                }\n            case \"thermostatMode\":\n                switch (val) {\n                    case Characteristic.TargetHeatingCoolingState.COOL:\n                        return \"cool\";\n                    case Characteristic.TargetHeatingCoolingState.HEAT:\n                        return \"heat\";\n                    case Characteristic.TargetHeatingCoolingState.AUTO:\n                        return \"auto\";\n                    case Characteristic.TargetHeatingCoolingState.OFF:\n                        return \"off\";\n                    default:\n                        return undefined;\n                }\n\n            case \"fanMode\":\n                if (val >= 0 && val <= CommunityTypes.FanOscilationMode.SLEEP) {\n                    return \"sleep\";\n                } else if (val > CommunityTypes.FanOscilationMode.SLEEP && val <= CommunityTypes.FanOscilationMode.LOW) {\n                    return \"low\";\n                } else if (val > CommunityTypes.FanOscilationMode.LOW && val <= CommunityTypes.FanOscilationMode.MEDIUM) {\n                    return \"medium\";\n                } else if (val > CommunityTypes.FanOscilationMode.MEDIUM && val <= CommunityTypes.FanOscilationMode.HIGH) {\n                    return \"high\";\n                } else {\n                    return \"sleep\";\n                }\n            default:\n                return val;\n        }\n    }\n\n    colorTempFromK(temp) {\n        return (1000000 / temp).toFixed();\n    }\n\n    colorTempToK(temp) {\n        return (1000000 / temp).toFixed();\n    }\n\n    thermostatTempConversion(temp, isSet = false) {\n        if (isSet) {\n            return (this.platform.getTempUnit() === 'C') ? Math.round(temp) : Math.round(temp * 1.8 + 32);\n        } else {\n            return (this.platform.getTempUnit() === 'C') ? Math.round(temp * 10) / 10 : Math.round((temp - 32) / 1.8 * 10) / 10;\n        }\n    }\n\n    thermostatTargetTemp(devData) {\n        // console.log('ThermostatMode:', devData.attributes.thermostatMode, ' | thermostatOperatingState: ', devData.attributes.thermostatOperatingState);\n        switch (devData.attributes.thermostatMode) {\n            case 'cool':\n            case 'cooling':\n                return devData.attributes.coolingSetpoint;\n            case 'emergency heat':\n            case 'heat':\n            case 'heating':\n                return devData.attributes.heatingSetpoint;\n            default:\n                {\n                    const cool = devData.attributes.coolingSetpoint;\n                    const heat = devData.attributes.heatingSetpoint;\n                    const cur = devData.attributes.temperature;\n                    const cDiff = Math.abs(cool - cur);\n                    const hDiff = Math.abs(heat - cur);\n                    const useCool = cDiff < hDiff;\n                    // console.log('(cool-cur):', cDiff);\n                    // console.log('(heat-cur):', hDiff);\n                    // console.log(`targerTemp(GET) | cool: ${cool} | heat: ${heat} | cur: ${cur} | useCool: ${useCool}`);\n                    return useCool ? cool : heat;\n                }\n        }\n    }\n\n    thermostatSupportedModes(devData) {\n        let hasHeatSetpoint = (devData.attributes.heatingSetpoint !== undefined || devData.attributes.heatingSetpoint !== null);\n        let hasCoolSetpoint = (devData.attributes.coolingSetpoint !== undefined || devData.attributes.coolingSetpoint !== null);\n        let sModes = devData.attributes.supportedThermostatModes || [];\n        let validModes = [Characteristic.TargetHeatingCoolingState.OFF];\n        if ((sModes.length && sModes.includes(\"heat\")) || sModes.includes(\"emergency heat\") || hasHeatSetpoint)\n            validModes.push(Characteristic.TargetHeatingCoolingState.HEAT);\n\n        if ((sModes.length && sModes.includes(\"cool\")) || hasCoolSetpoint)\n            validModes.push(Characteristic.TargetHeatingCoolingState.COOL);\n\n        if ((sModes.length && sModes.includes(\"auto\")) || (hasCoolSetpoint && hasHeatSetpoint))\n            validModes.push(Characteristic.TargetHeatingCoolingState.AUTO);\n        return validModes;\n    }\n\n    thermostatTargetTemp_set(devData) {\n        let cmdName;\n        let attrName;\n        switch (devData.attributes.thermostatMode) {\n            case \"cool\":\n                cmdName = \"setCoolingSetpoint\";\n                attrName = \"coolingSetpoint\";\n                break;\n            case \"emergency heat\":\n            case \"heat\":\n                cmdName = \"setHeatingSetpoint\";\n                attrName = \"heatingSetpoint\";\n                break;\n            default:\n                {\n                    // This should only refer to auto\n                    // Choose closest target as single target\n                    const cool = devData.attributes.coolingSetpoint;\n                    const heat = devData.attributes.heatingSetpoint;\n                    const cur = devData.attributes.temperature;\n                    const cDiff = Math.abs(cool - cur);\n                    const hDiff = Math.abs(heat - cur);\n                    const useCool = cDiff < hDiff;\n                    // console.log('(cool-cur):', cDiff);\n                    // console.log('(heat-cur):', hDiff);\n                    // console.log(`targerTemp(SET) | cool: ${cool} | heat: ${heat} | cur: ${cur} | useCool: ${useCool}`);\n                    cmdName = useCool ? \"setCoolingSetpoint\" : \"setHeatingSetpoint\";\n                    attrName = useCool ? \"coolingSetpoint\" : \"heatingSetpoint\";\n                }\n        }\n        return {\n            cmdName: cmdName,\n            attrName: attrName\n        };\n    }\n\n    tempConversion(temp, onlyC = false) {\n        if (this.platform.getTempUnit() === 'C' || onlyC) {\n            return (parseFloat(temp * 10) / 10);\n        } else {\n            return (parseFloat((temp - 32) / 1.8 * 10) / 10).toFixed(2);\n        }\n    }\n\n    cToF(temp) {\n        return (parseFloat(temp * 10) / 10);\n    }\n\n    fToC(temp) {\n        return (parseFloat((temp - 32) / 1.8 * 10) / 10);\n    }\n\n    fanSpeedConversion(speedVal, has4Spd = false) {\n        if (speedVal <= 0) {\n            return \"off\";\n        }\n        if (has4Spd) {\n            if (speedVal > 0 && speedVal <= 25) {\n                return \"low\";\n            } else if (speedVal > 25 && speedVal <= 50) {\n                return \"med\";\n            } else if (speedVal > 50 && speedVal <= 75) {\n                return \"medhigh\";\n            } else if (speedVal > 75 && speedVal <= 100) {\n                return \"high\";\n            }\n        } else {\n            if (speedVal > 0 && speedVal <= 33) {\n                return \"low\";\n            } else if (speedVal > 33 && speedVal <= 66) {\n                return \"medium\";\n            } else if (speedVal > 66 && speedVal <= 99) {\n                return \"high\";\n            }\n        }\n    }\n\n    fanSpeedConversionInt(speedVal) {\n        if (!speedVal || speedVal <= 0) {\n            return \"off\";\n        } else if (speedVal === 1) {\n            return \"low\";\n        } else if (speedVal === 2) {\n            return \"medium\";\n        } else if (speedVal === 3) {\n            return \"high\";\n        }\n    }\n\n    fanSpeedIntToLevel(speedVal) {\n        switch (speedVal) {\n            case 0:\n                return 0;\n            case 1:\n                return 32;\n            case 2:\n                return 66;\n            case 3:\n                return 100;\n            default:\n                return 0;\n        }\n    }\n\n    fanSpeedLevelToInt(val) {\n        if (val > 0 && val < 34) {\n            return 1;\n        } else if (val >= 34 && val < 66) {\n            return 2;\n        } else if (val >= 66 && val <= 100) {\n            return 3;\n        } else {\n            return 0;\n        }\n    }\n\n    convertAlarmState(value) {\n        switch (value) {\n            case \"stay\":\n            case \"night\":\n                return Characteristic.SecuritySystemCurrentState.STAY_ARM;\n            case \"away\":\n                return Characteristic.SecuritySystemCurrentState.AWAY_ARM;\n            case \"off\":\n                return Characteristic.SecuritySystemCurrentState.DISARMED;\n            case \"alarm_active\":\n                return Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED;\n        }\n    }\n\n    convertAlarmCmd(value) {\n        switch (value) {\n            case 0:\n            case 2:\n                return \"stay\";\n            case 1:\n                return \"away\";\n            case 3:\n                return \"off\";\n            case 4:\n                return \"alarm_active\";\n        }\n    }\n};\n"
  },
  {
    "path": "src/index.js",
    "content": "const {\n    pluginName,\n    platformName\n} = require(\"./libs/Constants\"),\n    StPlatform = require(\"./ST_Platform\");\n\nmodule.exports = (homebridge) => {\n    homebridge.registerPlatform(pluginName, platformName, StPlatform, true);\n};"
  },
  {
    "path": "src/libs/CommunityTypes.js",
    "content": "const inherits = require('util').inherits;\n\nmodule.exports = function(Service, Characteristic) {\n    var CommunityTypes = {};\n    CommunityTypes.KilowattHours = function() {\n        Characteristic.call(this, 'Total Consumption', 'E863F10C-079E-48FF-8F27-9C2605A29F52');\n        this.setProps({\n            format: Characteristic.Formats.UINT32,\n            unit: 'kWh',\n            minValue: 0,\n            maxValue: 65535,\n            minStep: 1,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.KilowattHours, Characteristic);\n\n    CommunityTypes.Watts = function() {\n        Characteristic.call(this, 'Consumption', 'E863F10D-079E-48FF-8F27-9C2605A29F52');\n        this.setProps({\n            format: Characteristic.Formats.UINT16,\n            unit: 'W',\n            minValue: 0,\n            maxValue: 65535,\n            minStep: 1,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.Watts, Characteristic);\n\n\n    // Characteristics\n    CommunityTypes.Timestamp = function() {\n        Characteristic.call(this, \"Timestamp\", 'FF000001-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.Timestamp, Characteristic);\n\n    CommunityTypes.AudioDataURL = function() {\n        Characteristic.call(this, \"Audio URL\", 'FF000002-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n    };\n    inherits(CommunityTypes.AudioDataURL, Characteristic);\n\n    CommunityTypes.VideoDataURL = function() {\n        Characteristic.call(this, \"Video URL\", 'FF000003-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n    };\n    inherits(CommunityTypes.VideoDataURL, Characteristic);\n\n    CommunityTypes.AudioVolume = function() {\n        Characteristic.call(this, 'Audio Volume', '00001001-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.UINT8,\n            unit: Characteristic.Units.PERCENTAGE,\n            maxValue: 100,\n            minValue: 0,\n            minStep: 1,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.AudioVolume, Characteristic);\n\n    CommunityTypes.Muting = function() {\n        Characteristic.call(this, 'Muting', '00001002-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.UINT8,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.Muting, Characteristic);\n\n    CommunityTypes.PlaybackState = function() {\n        Characteristic.call(this, 'Playback State', '00002001-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.UINT8,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.PlaybackState, Characteristic);\n    CommunityTypes.PlaybackState.PLAYING = 0;\n    CommunityTypes.PlaybackState.PAUSED = 1;\n    CommunityTypes.PlaybackState.STOPPED = 2;\n\n    CommunityTypes.SkipForward = function() {\n        Characteristic.call(this, 'Skip Forward', '00002002-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.BOOL,\n            perms: [Characteristic.Perms.WRITE]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.SkipForward, Characteristic);\n\n    CommunityTypes.SkipBackward = function() {\n        Characteristic.call(this, 'Skip Backward', '00002003-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.BOOL,\n            perms: [Characteristic.Perms.WRITE]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.SkipBackward, Characteristic);\n\n    CommunityTypes.ShuffleMode = function() {\n        Characteristic.call(this, 'Shuffle Mode', '00002004-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.UINT8,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.ShuffleMode, Characteristic);\n    //NOTE: If GROUP or SET is not supported, accessories should coerce to ALBUM.\n    // If ALBUM is not supported, coerce to ITEM.\n    // In general, it is recommended for apps to only assume OFF, ITEM, and ALBUM\n    // are supported unless it is known that the accessory supports other settings.\n    CommunityTypes.ShuffleMode.OFF = 0;\n    //NOTE: INDIVIDUAL is deprecated.\n    CommunityTypes.ShuffleMode.ITEM = CommunityTypes.ShuffleMode.INDIVIDUAL = 1;\n    CommunityTypes.ShuffleMode.GROUP = 2; // e.g. iTunes \"Groupings\"\n    CommunityTypes.ShuffleMode.ALBUM = 3; // e.g. album or season\n    CommunityTypes.ShuffleMode.SET = 4; // e.g. T.V. Series or album box set\n\n    CommunityTypes.RepeatMode = function() {\n        Characteristic.call(this, 'Repeat Mode', '00002005-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.UINT8,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.RepeatMode, Characteristic);\n    CommunityTypes.RepeatMode.OFF = 0;\n    CommunityTypes.RepeatMode.ONE = 1;\n    CommunityTypes.RepeatMode.ALL = 2;\n\n    CommunityTypes.PlaybackSpeed = function() {\n        Characteristic.call(this, 'Playback Speed', '00002006-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.FLOAT,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.PlaybackSpeed, Characteristic);\n\n    CommunityTypes.MediaCurrentPosition = function() {\n        Characteristic.call(this, 'Media Current Position', '00002007-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.FLOAT, // In seconds\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.MediaCurrentPosition, Characteristic);\n\n    CommunityTypes.MediaItemName = function() {\n        Characteristic.call(this, 'Media Name', '00003001-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.MediaItemName, Characteristic);\n\n    CommunityTypes.MediaItemAlbumName = function() {\n        Characteristic.call(this, 'Media Album Name', '00003002-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.MediaItemAlbumName, Characteristic);\n\n    CommunityTypes.MediaItemArtist = function() {\n        Characteristic.call(this, 'Media Artist', '00003003-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.MediaItemArtist, Characteristic);\n\n    CommunityTypes.MediaItemDuration = function() {\n        Characteristic.call(this, 'Media Duration', '00003005-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.FLOAT, // In seconds\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.MediaItemDuration, Characteristic);\n\n    CommunityTypes.StillImage = function() {\n        Characteristic.call(this, 'Still Image', '00004001-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.DATA,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.StillImage, Characteristic);\n\n    // Also known as MIME type...\n    CommunityTypes.MediaTypeIdentifier = function() {\n        Characteristic.call(this, 'Media Type Identifier', '00004002-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = null;\n    };\n    inherits(CommunityTypes.MediaTypeIdentifier, Characteristic);\n\n    CommunityTypes.MediaWidth = function() {\n        Characteristic.call(this, 'Media Width', '00004003-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.UINT32,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.MediaWidth, Characteristic);\n\n    CommunityTypes.MediaHeight = function() {\n        Characteristic.call(this, 'Media Width', '00004004-0000-1000-8000-135D67EC4377');\n        this.setProps({\n            format: Characteristic.Formats.UINT32,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n        });\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.MediaHeight, Characteristic);\n\n    // Custom SmartThings Device Characteristic\n    CommunityTypes.DeviceId = function() {\n        Characteristic.call(this, 'Device Id', '2ecc2a94-30d3-4457-bba7-0a93468de8a4');\n        this.setProps({\n            format: Characteristic.Formats.STRING,\n            perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.HIDDEN]\n        });\n\n        this.value = this.getDefaultValue();\n    };\n    inherits(CommunityTypes.DeviceId, Characteristic);\n\n    // Services\n\n    CommunityTypes.AudioDeviceService = function(displayName, subtype) {\n        Service.call(this, displayName, '00000001-0000-1000-8000-135D67EC4377', subtype);\n\n        // Required Characteristics\n        this.addCharacteristic(CommunityTypes.AudioVolume);\n\n        // Optional Characteristics\n        this.addOptionalCharacteristic(CommunityTypes.Muting);\n        this.addOptionalCharacteristic(Characteristic.Name);\n    };\n    inherits(CommunityTypes.AudioDeviceService, Service);\n\n    CommunityTypes.PlaybackDeviceService = function(displayName, subtype) {\n        Service.call(this, displayName, '00000002-0000-1000-8000-135D67EC4377', subtype);\n\n        // Required Characteristics\n        this.addCharacteristic(CommunityTypes.PlaybackState);\n\n        // Optional Characteristics\n        this.addOptionalCharacteristic(CommunityTypes.SkipForward);\n        this.addOptionalCharacteristic(CommunityTypes.SkipBackward);\n        this.addOptionalCharacteristic(CommunityTypes.ShuffleMode);\n        this.addOptionalCharacteristic(CommunityTypes.RepeatMode);\n        this.addOptionalCharacteristic(CommunityTypes.PlaybackSpeed);\n        this.addOptionalCharacteristic(CommunityTypes.MediaCurrentPosition);\n        this.addOptionalCharacteristic(CommunityTypes.MediaItemName);\n        this.addOptionalCharacteristic(CommunityTypes.MediaItemAlbumName);\n        this.addOptionalCharacteristic(CommunityTypes.MediaItemArtist);\n        this.addOptionalCharacteristic(CommunityTypes.MediaItemDuration);\n        this.addOptionalCharacteristic(Characteristic.Name);\n        // Artwork characteristics...would be better reported in a separate service?\n        this.addOptionalCharacteristic(CommunityTypes.StillImage);\n        this.addOptionalCharacteristic(CommunityTypes.MediaTypeIdentifier);\n        this.addOptionalCharacteristic(CommunityTypes.MediaWidth);\n        this.addOptionalCharacteristic(CommunityTypes.MediaHeight);\n    };\n    inherits(CommunityTypes.PlaybackDeviceService, Service);\n\n    // A media information service that has no playback controls, for e.g. DAB radio...\n    CommunityTypes.MediaInformationService = function(displayName, subtype) {\n        Service.call(this, displayName, '00000003-0000-1000-8000-135D67EC4377', subtype);\n\n        // Required Characteristics\n        this.addCharacteristic(CommunityTypes.MediaItemName);\n\n        // Optional Characteristics\n        this.addOptionalCharacteristic(CommunityTypes.MediaItemAlbumName);\n        this.addOptionalCharacteristic(CommunityTypes.MediaItemArtist);\n        this.addOptionalCharacteristic(CommunityTypes.MediaItemDuration);\n        this.addOptionalCharacteristic(CommunityTypes.MediaCurrentPosition);\n        this.addOptionalCharacteristic(Characteristic.Name);\n        // Artwork characteristics...would be better reported in a separate service?\n        this.addOptionalCharacteristic(CommunityTypes.StillImage);\n        this.addOptionalCharacteristic(CommunityTypes.MediaTypeIdentifier);\n        this.addOptionalCharacteristic(CommunityTypes.MediaWidth);\n        this.addOptionalCharacteristic(CommunityTypes.MediaHeight);\n    };\n    inherits(CommunityTypes.MediaInformationService, Service);\n\n    CommunityTypes.StillImageService = function(displayName, subtype) {\n        Service.call(this, displayName, '00000004-0000-1000-8000-135D67EC4377', subtype);\n\n        // Required Characteristics\n        this.addCharacteristic(CommunityTypes.StillImage);\n        this.addCharacteristic(CommunityTypes.MediaTypeIdentifier);\n\n        // Optional Characteristics\n        this.addOptionalCharacteristic(CommunityTypes.MediaWidth);\n        this.addOptionalCharacteristic(CommunityTypes.MediaHeight);\n        this.addOptionalCharacteristic(Characteristic.Name);\n    };\n    inherits(CommunityTypes.StillImageService, Service);\n\n    CommunityTypes.SecurityCameraService = function(displayName, subtype) {\n        Service.call(this, displayName, '00000005-0000-1000-8000-135D67EC4377', subtype);\n\n        // Required Characteristics\n        this.addCharacteristic(CommunityTypes.StillImageService);\n        this.addCharacteristic(CommunityTypes.MediaTypeIdentifier);\n\n        // Optional Characteristics\n        this.addOptionalCharacteristic(CommunityTypes.Timestamp);\n        this.addOptionalCharacteristic(CommunityTypes.MediaWidth);\n        this.addOptionalCharacteristic(CommunityTypes.MediaHeight);\n        this.addOptionalCharacteristic(CommunityTypes.VideoDataURL);\n        this.addOptionalCharacteristic(CommunityTypes.AudioDataURL);\n        this.addOptionalCharacteristic(Characteristic.MotionDetected);\n        this.addOptionalCharacteristic(Characteristic.StatusTampered);\n        this.addOptionalCharacteristic(Characteristic.Name);\n    };\n    \n    CommunityTypes.FanOscilationMode = function() {\n         Characteristic.call(this, 'RotationSpeed', '00000029-0000-1000-8000-0026BB765291');\n         this.setProps({\n             format: Characteristic.Formats.UINT8,\n             maxValue: 100,\n             minValue: 0,\n             validValues: [25,50,75,100],\n             perms: [Characteristic.Perms.READ,Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n         });\n         this.value = this.getDefaultValue();\n     };\n     inherits(CommunityTypes.FanOscilationMode, Characteristic.RotationSpeed);\n\n     // The value property of FanOscilationMode must be one of the following:\n     CommunityTypes.FanOscilationMode.SLEEP  = 25;\n     CommunityTypes.FanOscilationMode.LOW    = 50;\n     CommunityTypes.FanOscilationMode.MEDIUM = 75;\n     CommunityTypes.FanOscilationMode.HIGH   = 100;\n     \n     CommunityTypes.NewAirPurifierService = function(displayName, subtype) {\n         Service.call(this, displayName, '000000BB-0000-1000-8000-0026BB765291', subtype);\n\n         // Required Characteristics\n         this.addCharacteristic(Characteristic.Active);\n         this.addCharacteristic(Characteristic.CurrentAirPurifierState);\n         this.addCharacteristic(Characteristic.TargetAirPurifierState);\n         this.addCharacteristic(CommunityTypes.FanOscilationMode);\n\n         // Optional Characteristics\n         this.addOptionalCharacteristic(Characteristic.Name);\n     };\n     inherits(CommunityTypes.NewAirPurifierService, Service.AirPurifier);\n\n    return CommunityTypes;\n};"
  },
  {
    "path": "src/libs/Constants.js",
    "content": "module.exports = {\n    pluginName: \"homebridge-smartthings\",\n    platformDesc: \"SmartThings\",\n    platformName: \"SmartThings-v2\",\n    pluginVersion: require(\"../../package.json\").version,\n    packageFile: require(\"../../package.json\"),\n    knownCapabilities: [\n        \"Switch\",\n        \"Light\",\n        \"LightBulb\",\n        \"Light Bulb\",\n        \"Bulb\",\n        \"Color Control\",\n        \"Door\",\n        \"Window\",\n        \"Battery\",\n        \"Polling\",\n        \"Lock\",\n        \"Refresh\",\n        \"Lock Codes\",\n        \"Sensor\",\n        \"Actuator\",\n        \"Configuration\",\n        \"Switch Level\",\n        \"Temperature Measurement\",\n        \"Motion Sensor\",\n        \"Color Temperature\",\n        \"Illuminance Measurement\",\n        \"Contact Sensor\",\n        \"Acceleration Sensor\",\n        \"Door Control\",\n        \"Garage Door Control\",\n        \"Relative Humidity Measurement\",\n        \"Presence Sensor\",\n        \"Carbon Dioxide Measurement\",\n        \"Carbon Monoxide Detector\",\n        \"Water Sensor\",\n        \"Window Shade\",\n        \"WindowShade\",\n        \"Valve\",\n        \"Energy Meter\",\n        \"Power Meter\",\n        \"Thermostat\",\n        \"Thermostat Cooling Setpoint\",\n        \"Thermostat Mode\",\n        \"Thermostat Fan Mode\",\n        \"Thermostat Operating State\",\n        \"Thermostat Heating Setpoint\",\n        \"Thermostat Setpoint\",\n        \"Fan Speed\",\n        \"Fan Control\",\n        \"Fan Light\",\n        \"Fan\",\n        \"Speaker\",\n        \"Tamper Alert\",\n        \"Alarm\",\n        \"Alarm System Status\",\n        \"AlarmSystemStatus\",\n        \"Mode\",\n        \"Routine\",\n        \"Button\",\n        // Sonos Capabilities\n        \"Audio Volume\",\n        \"Audio Mute\"\n    ]\n};"
  },
  {
    "path": "src/libs/HomeKitTypes-Bridge.js",
    "content": "'use strict';\n// Removed from new HAS\n\nvar inherits = require('util').inherits;\nvar Characteristic = require('../Characteristic').Characteristic;\nvar Service = require('../Service').Service;\n\n/**\n * \n * Removed in IOS 11\n * \n */\n\n/**\n * Characteristic \"App Matching Identifier\"\n */\n\nCharacteristic.AppMatchingIdentifier = function() {\n  Characteristic.call(this, 'App Matching Identifier', '000000A4-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.TLV8,\n    perms: [Characteristic.Perms.READ]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AppMatchingIdentifier, Characteristic);\n\nCharacteristic.AppMatchingIdentifier.UUID = '000000A4-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Programmable Switch Output State\"\n */\n\nCharacteristic.ProgrammableSwitchOutputState = function() {\n  Characteristic.call(this, 'Programmable Switch Output State', '00000074-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT8,\n    maxValue: 1,\n    minValue: 0,\n    minStep: 1,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ProgrammableSwitchOutputState, Characteristic);\n\nCharacteristic.ProgrammableSwitchOutputState.UUID = '00000074-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Software Revision\"\n */\n\nCharacteristic.SoftwareRevision = function() {\n  Characteristic.call(this, 'Software Revision', '00000054-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.STRING,\n    perms: [Characteristic.Perms.READ]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SoftwareRevision, Characteristic);\n\nCharacteristic.SoftwareRevision.UUID = '00000054-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Camera Control\"\n */\n\nService.CameraControl = function(displayName, subtype) {\n  Service.call(this, displayName, '00000111-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.On);\n\n  // Optional Characteristics\n  this.addOptionalCharacteristic(Characteristic.CurrentHorizontalTiltAngle);\n  this.addOptionalCharacteristic(Characteristic.CurrentVerticalTiltAngle);\n  this.addOptionalCharacteristic(Characteristic.TargetHorizontalTiltAngle);\n  this.addOptionalCharacteristic(Characteristic.TargetVerticalTiltAngle);\n  this.addOptionalCharacteristic(Characteristic.NightVision);\n  this.addOptionalCharacteristic(Characteristic.OpticalZoom);\n  this.addOptionalCharacteristic(Characteristic.DigitalZoom);\n  this.addOptionalCharacteristic(Characteristic.ImageRotation);\n  this.addOptionalCharacteristic(Characteristic.ImageMirroring);\n  this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.CameraControl, Service);\n\nService.CameraControl.UUID = '00000111-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Stateful Programmable Switch\"\n */\n\nService.StatefulProgrammableSwitch = function(displayName, subtype) {\n  Service.call(this, displayName, '00000088-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.ProgrammableSwitchEvent);\n  this.addCharacteristic(Characteristic.ProgrammableSwitchOutputState);\n\n  // Optional Characteristics\n  this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.StatefulProgrammableSwitch, Service);\n\nService.StatefulProgrammableSwitch.UUID = '00000088-0000-1000-8000-0026BB765291';\n\n\n// Aliases\nCharacteristic.SelectedStreamConfiguration = Characteristic.SelectedRTPStreamConfiguration;\nService.Label = Service.ServiceLabel;\nCharacteristic.LabelNamespace = Characteristic.ServiceLabelNamespace;\nCharacteristic.LabelIndex = Characteristic.ServiceLabelIndex;\n\n//Renamed Enums:\nCharacteristic.TargetHumidifierDehumidifierState.AUTO = 0; //Is Now HUMIDIFIER_OR_DEHUMIDIFIER\n\n\n\n\n\n\n\n/**\n * \n * Removed in IOS 10\n * \n */\n\n/**\n * Characteristic \"Accessory Identifier\"\n */\n\nCharacteristic.AccessoryIdentifier = function() {\n  Characteristic.call(this, 'Accessory Identifier', '00000057-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.STRING,\n    perms: [Characteristic.Perms.READ]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AccessoryIdentifier, Characteristic);\n\nCharacteristic.AccessoryIdentifier.UUID = '00000057-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Category\"\n */\n\nCharacteristic.Category = function() {\n  Characteristic.call(this, 'Category', '000000A3-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT16,\n    maxValue: 16,\n    minValue: 1,\n    minStep: 1,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Category, Characteristic);\n\nCharacteristic.Category.UUID = '000000A3-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Configure Bridged Accessory\"\n */\n\nCharacteristic.ConfigureBridgedAccessory = function() {\n  Characteristic.call(this, 'Configure Bridged Accessory', '000000A0-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.TLV8,\n    perms: [Characteristic.Perms.WRITE]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ConfigureBridgedAccessory, Characteristic);\n\nCharacteristic.ConfigureBridgedAccessory.UUID = '000000A0-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Configure Bridged Accessory Status\"\n */\n\nCharacteristic.ConfigureBridgedAccessoryStatus = function() {\n  Characteristic.call(this, 'Configure Bridged Accessory Status', '0000009D-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.TLV8,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ConfigureBridgedAccessoryStatus, Characteristic);\n\nCharacteristic.ConfigureBridgedAccessoryStatus.UUID = '0000009D-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Time\"\n */\n\nCharacteristic.CurrentTime = function() {\n  Characteristic.call(this, 'Current Time', '0000009B-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.STRING,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentTime, Characteristic);\n\nCharacteristic.CurrentTime.UUID = '0000009B-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Day of the Week\"\n */\n\nCharacteristic.DayoftheWeek = function() {\n  Characteristic.call(this, 'Day of the Week', '00000098-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT8,\n    maxValue: 7,\n    minValue: 1,\n    minStep: 1,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.DayoftheWeek, Characteristic);\n\nCharacteristic.DayoftheWeek.UUID = '00000098-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Discover Bridged Accessories\"\n */\n\nCharacteristic.DiscoverBridgedAccessories = function() {\n  Characteristic.call(this, 'Discover Bridged Accessories', '0000009E-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT8,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.DiscoverBridgedAccessories, Characteristic);\n\nCharacteristic.DiscoverBridgedAccessories.UUID = '0000009E-0000-1000-8000-0026BB765291';\n\n// The value property of DiscoverBridgedAccessories must be one of the following:\nCharacteristic.DiscoverBridgedAccessories.START_DISCOVERY = 0;\nCharacteristic.DiscoverBridgedAccessories.STOP_DISCOVERY = 1;\n\n/**\n * Characteristic \"Discovered Bridged Accessories\"\n */\n\nCharacteristic.DiscoveredBridgedAccessories = function() {\n  Characteristic.call(this, 'Discovered Bridged Accessories', '0000009F-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT16,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.DiscoveredBridgedAccessories, Characteristic);\n\nCharacteristic.DiscoveredBridgedAccessories.UUID = '0000009F-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Link Quality\"\n */\n\nCharacteristic.LinkQuality = function() {\n  Characteristic.call(this, 'Link Quality', '0000009C-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT8,\n    maxValue: 4,\n    minValue: 1,\n    minStep: 1,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LinkQuality, Characteristic);\n\nCharacteristic.LinkQuality.UUID = '0000009C-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Reachable\"\n */\n\nCharacteristic.Reachable = function() {\n  Characteristic.call(this, 'Reachable', '00000063-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.BOOL,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Reachable, Characteristic);\n\nCharacteristic.Reachable.UUID = '00000063-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Relay Control Point\"\n */\n\nCharacteristic.RelayControlPoint = function() {\n  Characteristic.call(this, 'Relay Control Point', '0000005E-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.TLV8,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RelayControlPoint, Characteristic);\n\nCharacteristic.RelayControlPoint.UUID = '0000005E-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Relay Enabled\"\n */\n\nCharacteristic.RelayEnabled = function() {\n  Characteristic.call(this, 'Relay Enabled', '0000005B-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.BOOL,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RelayEnabled, Characteristic);\n\nCharacteristic.RelayEnabled.UUID = '0000005B-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Relay State\"\n */\n\nCharacteristic.RelayState = function() {\n  Characteristic.call(this, 'Relay State', '0000005C-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT8,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RelayState, Characteristic);\n\nCharacteristic.RelayState.UUID = '0000005C-0000-1000-8000-0026BB765291';\n\n\n/**\n * Characteristic \"Time Update\"\n */\n\nCharacteristic.TimeUpdate = function() {\n  Characteristic.call(this, 'Time Update', '0000009A-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.BOOL,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TimeUpdate, Characteristic);\n\nCharacteristic.TimeUpdate.UUID = '0000009A-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Tunnel Connection Timeout \"\n */\n\nCharacteristic.TunnelConnectionTimeout = function() {\n  Characteristic.call(this, 'Tunnel Connection Timeout ', '00000061-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.UINT32,\n    perms: [Characteristic.Perms.WRITE, Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TunnelConnectionTimeout, Characteristic);\n\nCharacteristic.TunnelConnectionTimeout.UUID = '00000061-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Tunneled Accessory Advertising\"\n */\n\nCharacteristic.TunneledAccessoryAdvertising = function() {\n  Characteristic.call(this, 'Tunneled Accessory Advertising', '00000060-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.BOOL,\n    perms: [Characteristic.Perms.WRITE, Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TunneledAccessoryAdvertising, Characteristic);\n\nCharacteristic.TunneledAccessoryAdvertising.UUID = '00000060-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Tunneled Accessory Connected\"\n */\n\nCharacteristic.TunneledAccessoryConnected = function() {\n  Characteristic.call(this, 'Tunneled Accessory Connected', '00000059-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.BOOL,\n    perms: [Characteristic.Perms.WRITE, Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TunneledAccessoryConnected, Characteristic);\n\nCharacteristic.TunneledAccessoryConnected.UUID = '00000059-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Tunneled Accessory State Number\"\n */\n\nCharacteristic.TunneledAccessoryStateNumber = function() {\n  Characteristic.call(this, 'Tunneled Accessory State Number', '00000058-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.FLOAT,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TunneledAccessoryStateNumber, Characteristic);\n\nCharacteristic.TunneledAccessoryStateNumber.UUID = '00000058-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Version\"\n */\n\nCharacteristic.Version = function() {\n  Characteristic.call(this, 'Version', '00000037-0000-1000-8000-0026BB765291');\n  this.setProps({\n    format: Characteristic.Formats.STRING,\n    perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n  });\n  this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Version, Characteristic);\n\nCharacteristic.Version.UUID = '00000037-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Bridge Configuration\"\n */\n\nService.BridgeConfiguration = function(displayName, subtype) {\n  Service.call(this, displayName, '000000A1-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.ConfigureBridgedAccessoryStatus);\n  this.addCharacteristic(Characteristic.DiscoverBridgedAccessories);\n  this.addCharacteristic(Characteristic.DiscoveredBridgedAccessories);\n  this.addCharacteristic(Characteristic.ConfigureBridgedAccessory);\n\n  // Optional Characteristics\n  this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.BridgeConfiguration, Service);\n\nService.BridgeConfiguration.UUID = '000000A1-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Bridging State\"\n */\n\nService.BridgingState = function(displayName, subtype) {\n  Service.call(this, displayName, '00000062-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.Reachable);\n  this.addCharacteristic(Characteristic.LinkQuality);\n  this.addCharacteristic(Characteristic.AccessoryIdentifier);\n  this.addCharacteristic(Characteristic.Category);\n\n  // Optional Characteristics\n  this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.BridgingState, Service);\n\nService.BridgingState.UUID = '00000062-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Pairing\"\n */\n\nService.Pairing = function(displayName, subtype) {\n  Service.call(this, displayName, '00000055-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.PairSetup);\n  this.addCharacteristic(Characteristic.PairVerify);\n  this.addCharacteristic(Characteristic.PairingFeatures);\n  this.addCharacteristic(Characteristic.PairingPairings);\n\n  // Optional Characteristics\n};\n\ninherits(Service.Pairing, Service);\n\nService.Pairing.UUID = '00000055-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Protocol Information\"\n */\n\nService.ProtocolInformation = function(displayName, subtype) {\n  Service.call(this, displayName, '000000A2-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.Version);\n\n  // Optional Characteristics\n};\n\ninherits(Service.ProtocolInformation, Service);\n\nService.ProtocolInformation.UUID = '000000A2-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Relay\"\n */\n\nService.Relay = function(displayName, subtype) {\n  Service.call(this, displayName, '0000005A-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.RelayEnabled);\n  this.addCharacteristic(Characteristic.RelayState);\n  this.addCharacteristic(Characteristic.RelayControlPoint);\n\n  // Optional Characteristics\n};\n\ninherits(Service.Relay, Service);\n\nService.Relay.UUID = '0000005A-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Time Information\"\n */\n\nService.TimeInformation = function(displayName, subtype) {\n  Service.call(this, displayName, '00000099-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.CurrentTime);\n  this.addCharacteristic(Characteristic.DayoftheWeek);\n  this.addCharacteristic(Characteristic.TimeUpdate);\n\n  // Optional Characteristics\n  this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.TimeInformation, Service);\n\nService.TimeInformation.UUID = '00000099-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Tunneled BTLE Accessory Service\"\n */\n\nService.TunneledBTLEAccessoryService = function(displayName, subtype) {\n  Service.call(this, displayName, '00000056-0000-1000-8000-0026BB765291', subtype);\n\n  // Required Characteristics\n  this.addCharacteristic(Characteristic.Name);\n  this.addCharacteristic(Characteristic.AccessoryIdentifier);\n  this.addCharacteristic(Characteristic.TunneledAccessoryStateNumber);\n  this.addCharacteristic(Characteristic.TunneledAccessoryConnected);\n  this.addCharacteristic(Characteristic.TunneledAccessoryAdvertising);\n  this.addCharacteristic(Characteristic.TunnelConnectionTimeout);\n\n  // Optional Characteristics\n};\n\ninherits(Service.TunneledBTLEAccessoryService, Service);\n\nService.TunneledBTLEAccessoryService.UUID = '00000056-0000-1000-8000-0026BB765291';\n"
  },
  {
    "path": "src/libs/HomeKitTypes.js",
    "content": "/* eslint-disable no-unused-vars */\n'use strict';\n// THIS FILE IS AUTO-GENERATED - DO NOT MODIFY\n\nvar inherits = require('util').inherits;\nvar Characteristic = require('../Characteristic').Characteristic;\nvar Service = require('../Service').Service;\n\n/**\n * Characteristic \"Accessory Flags\"\n */\n\nCharacteristic.AccessoryFlags = function() {\n    Characteristic.call(this, 'Accessory Flags', '000000A6-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT32,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AccessoryFlags, Characteristic);\n\nCharacteristic.AccessoryFlags.UUID = '000000A6-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Active\"\n */\n\nCharacteristic.Active = function() {\n    Characteristic.call(this, 'Active', '000000B0-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Active, Characteristic);\n\nCharacteristic.Active.UUID = '000000B0-0000-1000-8000-0026BB765291';\n\n// The value property of Active must be one of the following:\nCharacteristic.Active.INACTIVE = 0;\nCharacteristic.Active.ACTIVE = 1;\n\n/**\n * Characteristic \"Administrator Only Access\"\n */\n\nCharacteristic.AdministratorOnlyAccess = function() {\n    Characteristic.call(this, 'Administrator Only Access', '00000001-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AdministratorOnlyAccess, Characteristic);\n\nCharacteristic.AdministratorOnlyAccess.UUID = '00000001-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Air Particulate Density\"\n */\n\nCharacteristic.AirParticulateDensity = function() {\n    Characteristic.call(this, 'Air Particulate Density', '00000064-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 1000,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AirParticulateDensity, Characteristic);\n\nCharacteristic.AirParticulateDensity.UUID = '00000064-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Air Particulate Size\"\n */\n\nCharacteristic.AirParticulateSize = function() {\n    Characteristic.call(this, 'Air Particulate Size', '00000065-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AirParticulateSize, Characteristic);\n\nCharacteristic.AirParticulateSize.UUID = '00000065-0000-1000-8000-0026BB765291';\n\n// The value property of AirParticulateSize must be one of the following:\nCharacteristic.AirParticulateSize._2_5_M = 0;\nCharacteristic.AirParticulateSize._10_M = 1;\n\n/**\n * Characteristic \"Air Quality\"\n */\n\nCharacteristic.AirQuality = function() {\n    Characteristic.call(this, 'Air Quality', '00000095-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 5,\n        minValue: 0,\n        validValues: [0, 1, 2, 3, 4, 5],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AirQuality, Characteristic);\n\nCharacteristic.AirQuality.UUID = '00000095-0000-1000-8000-0026BB765291';\n\n// The value property of AirQuality must be one of the following:\nCharacteristic.AirQuality.UNKNOWN = 0;\nCharacteristic.AirQuality.EXCELLENT = 1;\nCharacteristic.AirQuality.GOOD = 2;\nCharacteristic.AirQuality.FAIR = 3;\nCharacteristic.AirQuality.INFERIOR = 4;\nCharacteristic.AirQuality.POOR = 5;\n\n/**\n * Characteristic \"Audio Feedback\"\n */\n\nCharacteristic.AudioFeedback = function() {\n    Characteristic.call(this, 'Audio Feedback', '00000005-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.AudioFeedback, Characteristic);\n\nCharacteristic.AudioFeedback.UUID = '00000005-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Battery Level\"\n */\n\nCharacteristic.BatteryLevel = function() {\n    Characteristic.call(this, 'Battery Level', '00000068-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.BatteryLevel, Characteristic);\n\nCharacteristic.BatteryLevel.UUID = '00000068-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Brightness\"\n */\n\nCharacteristic.Brightness = function() {\n    Characteristic.call(this, 'Brightness', '00000008-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Brightness, Characteristic);\n\nCharacteristic.Brightness.UUID = '00000008-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Carbon Dioxide Detected\"\n */\n\nCharacteristic.CarbonDioxideDetected = function() {\n    Characteristic.call(this, 'Carbon Dioxide Detected', '00000092-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CarbonDioxideDetected, Characteristic);\n\nCharacteristic.CarbonDioxideDetected.UUID = '00000092-0000-1000-8000-0026BB765291';\n\n// The value property of CarbonDioxideDetected must be one of the following:\nCharacteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL = 0;\nCharacteristic.CarbonDioxideDetected.CO2_LEVELS_ABNORMAL = 1;\n\n/**\n * Characteristic \"Carbon Dioxide Level\"\n */\n\nCharacteristic.CarbonDioxideLevel = function() {\n    Characteristic.call(this, 'Carbon Dioxide Level', '00000093-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 100000,\n        minValue: 0,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CarbonDioxideLevel, Characteristic);\n\nCharacteristic.CarbonDioxideLevel.UUID = '00000093-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Carbon Dioxide Peak Level\"\n */\n\nCharacteristic.CarbonDioxidePeakLevel = function() {\n    Characteristic.call(this, 'Carbon Dioxide Peak Level', '00000094-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 100000,\n        minValue: 0,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CarbonDioxidePeakLevel, Characteristic);\n\nCharacteristic.CarbonDioxidePeakLevel.UUID = '00000094-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Carbon Monoxide Detected\"\n */\n\nCharacteristic.CarbonMonoxideDetected = function() {\n    Characteristic.call(this, 'Carbon Monoxide Detected', '00000069-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CarbonMonoxideDetected, Characteristic);\n\nCharacteristic.CarbonMonoxideDetected.UUID = '00000069-0000-1000-8000-0026BB765291';\n\n// The value property of CarbonMonoxideDetected must be one of the following:\nCharacteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL = 0;\nCharacteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL = 1;\n\n/**\n * Characteristic \"Carbon Monoxide Level\"\n */\n\nCharacteristic.CarbonMonoxideLevel = function() {\n    Characteristic.call(this, 'Carbon Monoxide Level', '00000090-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 100,\n        minValue: 0,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CarbonMonoxideLevel, Characteristic);\n\nCharacteristic.CarbonMonoxideLevel.UUID = '00000090-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Carbon Monoxide Peak Level\"\n */\n\nCharacteristic.CarbonMonoxidePeakLevel = function() {\n    Characteristic.call(this, 'Carbon Monoxide Peak Level', '00000091-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 100,\n        minValue: 0,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CarbonMonoxidePeakLevel, Characteristic);\n\nCharacteristic.CarbonMonoxidePeakLevel.UUID = '00000091-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Charging State\"\n */\n\nCharacteristic.ChargingState = function() {\n    Characteristic.call(this, 'Charging State', '0000008F-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ChargingState, Characteristic);\n\nCharacteristic.ChargingState.UUID = '0000008F-0000-1000-8000-0026BB765291';\n\n// The value property of ChargingState must be one of the following:\nCharacteristic.ChargingState.NOT_CHARGING = 0;\nCharacteristic.ChargingState.CHARGING = 1;\nCharacteristic.ChargingState.NOT_CHARGEABLE = 2;\n\n/**\n * Characteristic \"Color Temperature\"\n */\n\nCharacteristic.ColorTemperature = function() {\n    Characteristic.call(this, 'Color Temperature', '000000CE-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT32,\n        maxValue: 500,\n        minValue: 140,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ColorTemperature, Characteristic);\n\nCharacteristic.ColorTemperature.UUID = '000000CE-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Contact Sensor State\"\n */\n\nCharacteristic.ContactSensorState = function() {\n    Characteristic.call(this, 'Contact Sensor State', '0000006A-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ContactSensorState, Characteristic);\n\nCharacteristic.ContactSensorState.UUID = '0000006A-0000-1000-8000-0026BB765291';\n\n// The value property of ContactSensorState must be one of the following:\nCharacteristic.ContactSensorState.CONTACT_DETECTED = 0;\nCharacteristic.ContactSensorState.CONTACT_NOT_DETECTED = 1;\n\n/**\n * Characteristic \"Cooling Threshold Temperature\"\n */\n\nCharacteristic.CoolingThresholdTemperature = function() {\n    Characteristic.call(this, 'Cooling Threshold Temperature', '0000000D-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.CELSIUS,\n        maxValue: 35,\n        minValue: 10,\n        minStep: 0.1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CoolingThresholdTemperature, Characteristic);\n\nCharacteristic.CoolingThresholdTemperature.UUID = '0000000D-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Air Purifier State\"\n */\n\nCharacteristic.CurrentAirPurifierState = function() {\n    Characteristic.call(this, 'Current Air Purifier State', '000000A9-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentAirPurifierState, Characteristic);\n\nCharacteristic.CurrentAirPurifierState.UUID = '000000A9-0000-1000-8000-0026BB765291';\n\n// The value property of CurrentAirPurifierState must be one of the following:\nCharacteristic.CurrentAirPurifierState.INACTIVE = 0;\nCharacteristic.CurrentAirPurifierState.IDLE = 1;\nCharacteristic.CurrentAirPurifierState.PURIFYING_AIR = 2;\n\n/**\n * Characteristic \"Current Ambient Light Level\"\n */\n\nCharacteristic.CurrentAmbientLightLevel = function() {\n    Characteristic.call(this, 'Current Ambient Light Level', '0000006B-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.LUX,\n        maxValue: 100000,\n        minValue: 0.0001,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentAmbientLightLevel, Characteristic);\n\nCharacteristic.CurrentAmbientLightLevel.UUID = '0000006B-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Door State\"\n */\n\nCharacteristic.CurrentDoorState = function() {\n    Characteristic.call(this, 'Current Door State', '0000000E-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 4,\n        minValue: 0,\n        validValues: [0, 1, 2, 3, 4],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentDoorState, Characteristic);\n\nCharacteristic.CurrentDoorState.UUID = '0000000E-0000-1000-8000-0026BB765291';\n\n// The value property of CurrentDoorState must be one of the following:\nCharacteristic.CurrentDoorState.OPEN = 0;\nCharacteristic.CurrentDoorState.CLOSED = 1;\nCharacteristic.CurrentDoorState.OPENING = 2;\nCharacteristic.CurrentDoorState.CLOSING = 3;\nCharacteristic.CurrentDoorState.STOPPED = 4;\n\n/**\n * Characteristic \"Current Fan State\"\n */\n\nCharacteristic.CurrentFanState = function() {\n    Characteristic.call(this, 'Current Fan State', '000000AF-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentFanState, Characteristic);\n\nCharacteristic.CurrentFanState.UUID = '000000AF-0000-1000-8000-0026BB765291';\n\n// The value property of CurrentFanState must be one of the following:\nCharacteristic.CurrentFanState.INACTIVE = 0;\nCharacteristic.CurrentFanState.IDLE = 1;\nCharacteristic.CurrentFanState.BLOWING_AIR = 2;\n\n/**\n * Characteristic \"Current Heater Cooler State\"\n */\n\nCharacteristic.CurrentHeaterCoolerState = function() {\n    Characteristic.call(this, 'Current Heater Cooler State', '000000B1-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 3,\n        minValue: 0,\n        validValues: [0, 1, 2, 3],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentHeaterCoolerState, Characteristic);\n\nCharacteristic.CurrentHeaterCoolerState.UUID = '000000B1-0000-1000-8000-0026BB765291';\n\n// The value property of CurrentHeaterCoolerState must be one of the following:\nCharacteristic.CurrentHeaterCoolerState.INACTIVE = 0;\nCharacteristic.CurrentHeaterCoolerState.IDLE = 1;\nCharacteristic.CurrentHeaterCoolerState.HEATING = 2;\nCharacteristic.CurrentHeaterCoolerState.COOLING = 3;\n\n/**\n * Characteristic \"Current Heating Cooling State\"\n */\n\nCharacteristic.CurrentHeatingCoolingState = function() {\n    Characteristic.call(this, 'Current Heating Cooling State', '0000000F-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentHeatingCoolingState, Characteristic);\n\nCharacteristic.CurrentHeatingCoolingState.UUID = '0000000F-0000-1000-8000-0026BB765291';\n\n// The value property of CurrentHeatingCoolingState must be one of the following:\nCharacteristic.CurrentHeatingCoolingState.OFF = 0;\nCharacteristic.CurrentHeatingCoolingState.HEAT = 1;\nCharacteristic.CurrentHeatingCoolingState.COOL = 2;\n\n/**\n * Characteristic \"Current Horizontal Tilt Angle\"\n */\n\nCharacteristic.CurrentHorizontalTiltAngle = function() {\n    Characteristic.call(this, 'Current Horizontal Tilt Angle', '0000006C-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 90,\n        minValue: -90,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentHorizontalTiltAngle, Characteristic);\n\nCharacteristic.CurrentHorizontalTiltAngle.UUID = '0000006C-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Humidifier Dehumidifier State\"\n */\n\nCharacteristic.CurrentHumidifierDehumidifierState = function() {\n    Characteristic.call(this, 'Current Humidifier Dehumidifier State', '000000B3-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 3,\n        minValue: 0,\n        validValues: [0, 1, 2, 3],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentHumidifierDehumidifierState, Characteristic);\n\nCharacteristic.CurrentHumidifierDehumidifierState.UUID = '000000B3-0000-1000-8000-0026BB765291';\n\n// The value property of CurrentHumidifierDehumidifierState must be one of the following:\nCharacteristic.CurrentHumidifierDehumidifierState.INACTIVE = 0;\nCharacteristic.CurrentHumidifierDehumidifierState.IDLE = 1;\nCharacteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING = 2;\nCharacteristic.CurrentHumidifierDehumidifierState.DEHUMIDIFYING = 3;\n\n/**\n * Characteristic \"Current Position\"\n */\n\nCharacteristic.CurrentPosition = function() {\n    Characteristic.call(this, 'Current Position', '0000006D-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentPosition, Characteristic);\n\nCharacteristic.CurrentPosition.UUID = '0000006D-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Relative Humidity\"\n */\n\nCharacteristic.CurrentRelativeHumidity = function() {\n    Characteristic.call(this, 'Current Relative Humidity', '00000010-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentRelativeHumidity, Characteristic);\n\nCharacteristic.CurrentRelativeHumidity.UUID = '00000010-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Slat State\"\n */\n\nCharacteristic.CurrentSlatState = function() {\n    Characteristic.call(this, 'Current Slat State', '000000AA-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentSlatState, Characteristic);\n\nCharacteristic.CurrentSlatState.UUID = '000000AA-0000-1000-8000-0026BB765291';\n\n// The value property of CurrentSlatState must be one of the following:\nCharacteristic.CurrentSlatState.FIXED = 0;\nCharacteristic.CurrentSlatState.JAMMED = 1;\nCharacteristic.CurrentSlatState.SWINGING = 2;\n\n/**\n * Characteristic \"Current Temperature\"\n */\n\nCharacteristic.CurrentTemperature = function() {\n    Characteristic.call(this, 'Current Temperature', '00000011-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.CELSIUS,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 0.1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentTemperature, Characteristic);\n\nCharacteristic.CurrentTemperature.UUID = '00000011-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Tilt Angle\"\n */\n\nCharacteristic.CurrentTiltAngle = function() {\n    Characteristic.call(this, 'Current Tilt Angle', '000000C1-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 90,\n        minValue: -90,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentTiltAngle, Characteristic);\n\nCharacteristic.CurrentTiltAngle.UUID = '000000C1-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Current Vertical Tilt Angle\"\n */\n\nCharacteristic.CurrentVerticalTiltAngle = function() {\n    Characteristic.call(this, 'Current Vertical Tilt Angle', '0000006E-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 90,\n        minValue: -90,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.CurrentVerticalTiltAngle, Characteristic);\n\nCharacteristic.CurrentVerticalTiltAngle.UUID = '0000006E-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Digital Zoom\"\n */\n\nCharacteristic.DigitalZoom = function() {\n    Characteristic.call(this, 'Digital Zoom', '0000011D-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.DigitalZoom, Characteristic);\n\nCharacteristic.DigitalZoom.UUID = '0000011D-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Filter Change Indication\"\n */\n\nCharacteristic.FilterChangeIndication = function() {\n    Characteristic.call(this, 'Filter Change Indication', '000000AC-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.FilterChangeIndication, Characteristic);\n\nCharacteristic.FilterChangeIndication.UUID = '000000AC-0000-1000-8000-0026BB765291';\n\n// The value property of FilterChangeIndication must be one of the following:\nCharacteristic.FilterChangeIndication.FILTER_OK = 0;\nCharacteristic.FilterChangeIndication.CHANGE_FILTER = 1;\n\n/**\n * Characteristic \"Filter Life Level\"\n */\n\nCharacteristic.FilterLifeLevel = function() {\n    Characteristic.call(this, 'Filter Life Level', '000000AB-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 100,\n        minValue: 0,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.FilterLifeLevel, Characteristic);\n\nCharacteristic.FilterLifeLevel.UUID = '000000AB-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Firmware Revision\"\n */\n\nCharacteristic.FirmwareRevision = function() {\n    Characteristic.call(this, 'Firmware Revision', '00000052-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.STRING,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.FirmwareRevision, Characteristic);\n\nCharacteristic.FirmwareRevision.UUID = '00000052-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Hardware Revision\"\n */\n\nCharacteristic.HardwareRevision = function() {\n    Characteristic.call(this, 'Hardware Revision', '00000053-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.STRING,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.HardwareRevision, Characteristic);\n\nCharacteristic.HardwareRevision.UUID = '00000053-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Heating Threshold Temperature\"\n */\n\nCharacteristic.HeatingThresholdTemperature = function() {\n    Characteristic.call(this, 'Heating Threshold Temperature', '00000012-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.CELSIUS,\n        maxValue: 25,\n        minValue: 0,\n        minStep: 0.1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.HeatingThresholdTemperature, Characteristic);\n\nCharacteristic.HeatingThresholdTemperature.UUID = '00000012-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Hold Position\"\n */\n\nCharacteristic.HoldPosition = function() {\n    Characteristic.call(this, 'Hold Position', '0000006F-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.HoldPosition, Characteristic);\n\nCharacteristic.HoldPosition.UUID = '0000006F-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Hue\"\n */\n\nCharacteristic.Hue = function() {\n    Characteristic.call(this, 'Hue', '00000013-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 360,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Hue, Characteristic);\n\nCharacteristic.Hue.UUID = '00000013-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Identify\"\n */\n\nCharacteristic.Identify = function() {\n    Characteristic.call(this, 'Identify', '00000014-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Identify, Characteristic);\n\nCharacteristic.Identify.UUID = '00000014-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Image Mirroring\"\n */\n\nCharacteristic.ImageMirroring = function() {\n    Characteristic.call(this, 'Image Mirroring', '0000011F-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ImageMirroring, Characteristic);\n\nCharacteristic.ImageMirroring.UUID = '0000011F-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Image Rotation\"\n */\n\nCharacteristic.ImageRotation = function() {\n    Characteristic.call(this, 'Image Rotation', '0000011E-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 270,\n        minValue: 0,\n        minStep: 90,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ImageRotation, Characteristic);\n\nCharacteristic.ImageRotation.UUID = '0000011E-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"In Use\"\n */\n\nCharacteristic.InUse = function() {\n    Characteristic.call(this, 'In Use', '000000D2-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.InUse, Characteristic);\n\nCharacteristic.InUse.UUID = '000000D2-0000-1000-8000-0026BB765291';\n\n// The value property of InUse must be one of the following:\nCharacteristic.InUse.NOT_IN_USE = 0;\nCharacteristic.InUse.IN_USE = 1;\n\n/**\n * Characteristic \"Is Configured\"\n */\n\nCharacteristic.IsConfigured = function() {\n    Characteristic.call(this, 'Is Configured', '000000D6-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.IsConfigured, Characteristic);\n\nCharacteristic.IsConfigured.UUID = '000000D6-0000-1000-8000-0026BB765291';\n\n// The value property of IsConfigured must be one of the following:\nCharacteristic.IsConfigured.NOT_CONFIGURED = 0;\nCharacteristic.IsConfigured.CONFIGURED = 1;\n\n/**\n * Characteristic \"Leak Detected\"\n */\n\nCharacteristic.LeakDetected = function() {\n    Characteristic.call(this, 'Leak Detected', '00000070-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LeakDetected, Characteristic);\n\nCharacteristic.LeakDetected.UUID = '00000070-0000-1000-8000-0026BB765291';\n\n// The value property of LeakDetected must be one of the following:\nCharacteristic.LeakDetected.LEAK_NOT_DETECTED = 0;\nCharacteristic.LeakDetected.LEAK_DETECTED = 1;\n\n/**\n * Characteristic \"Lock Control Point\"\n */\n\nCharacteristic.LockControlPoint = function() {\n    Characteristic.call(this, 'Lock Control Point', '00000019-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LockControlPoint, Characteristic);\n\nCharacteristic.LockControlPoint.UUID = '00000019-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Lock Current State\"\n */\n\nCharacteristic.LockCurrentState = function() {\n    Characteristic.call(this, 'Lock Current State', '0000001D-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 3,\n        minValue: 0,\n        validValues: [0, 1, 2, 3],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LockCurrentState, Characteristic);\n\nCharacteristic.LockCurrentState.UUID = '0000001D-0000-1000-8000-0026BB765291';\n\n// The value property of LockCurrentState must be one of the following:\nCharacteristic.LockCurrentState.UNSECURED = 0;\nCharacteristic.LockCurrentState.SECURED = 1;\nCharacteristic.LockCurrentState.JAMMED = 2;\nCharacteristic.LockCurrentState.UNKNOWN = 3;\n\n/**\n * Characteristic \"Lock Last Known Action\"\n */\n\nCharacteristic.LockLastKnownAction = function() {\n    Characteristic.call(this, 'Lock Last Known Action', '0000001C-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 8,\n        minValue: 0,\n        validValues: [0, 1, 2, 3, 4, 5, 6, 7, 8],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LockLastKnownAction, Characteristic);\n\nCharacteristic.LockLastKnownAction.UUID = '0000001C-0000-1000-8000-0026BB765291';\n\n// The value property of LockLastKnownAction must be one of the following:\nCharacteristic.LockLastKnownAction.SECURED_PHYSICALLY_INTERIOR = 0;\nCharacteristic.LockLastKnownAction.UNSECURED_PHYSICALLY_INTERIOR = 1;\nCharacteristic.LockLastKnownAction.SECURED_PHYSICALLY_EXTERIOR = 2;\nCharacteristic.LockLastKnownAction.UNSECURED_PHYSICALLY_EXTERIOR = 3;\nCharacteristic.LockLastKnownAction.SECURED_BY_KEYPAD = 4;\nCharacteristic.LockLastKnownAction.UNSECURED_BY_KEYPAD = 5;\nCharacteristic.LockLastKnownAction.SECURED_REMOTELY = 6;\nCharacteristic.LockLastKnownAction.UNSECURED_REMOTELY = 7;\nCharacteristic.LockLastKnownAction.SECURED_BY_AUTO_SECURE_TIMEOUT = 8;\n\n/**\n * Characteristic \"Lock Management Auto Security Timeout\"\n */\n\nCharacteristic.LockManagementAutoSecurityTimeout = function() {\n    Characteristic.call(this, 'Lock Management Auto Security Timeout', '0000001A-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT32,\n        unit: Characteristic.Units.SECONDS,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LockManagementAutoSecurityTimeout, Characteristic);\n\nCharacteristic.LockManagementAutoSecurityTimeout.UUID = '0000001A-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Lock Physical Controls\"\n */\n\nCharacteristic.LockPhysicalControls = function() {\n    Characteristic.call(this, 'Lock Physical Controls', '000000A7-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LockPhysicalControls, Characteristic);\n\nCharacteristic.LockPhysicalControls.UUID = '000000A7-0000-1000-8000-0026BB765291';\n\n// The value property of LockPhysicalControls must be one of the following:\nCharacteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED = 0;\nCharacteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED = 1;\n\n/**\n * Characteristic \"Lock Target State\"\n */\n\nCharacteristic.LockTargetState = function() {\n    Characteristic.call(this, 'Lock Target State', '0000001E-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.LockTargetState, Characteristic);\n\nCharacteristic.LockTargetState.UUID = '0000001E-0000-1000-8000-0026BB765291';\n\n// The value property of LockTargetState must be one of the following:\nCharacteristic.LockTargetState.UNSECURED = 0;\nCharacteristic.LockTargetState.SECURED = 1;\n\n/**\n * Characteristic \"Logs\"\n */\n\nCharacteristic.Logs = function() {\n    Characteristic.call(this, 'Logs', '0000001F-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Logs, Characteristic);\n\nCharacteristic.Logs.UUID = '0000001F-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Manufacturer\"\n */\n\nCharacteristic.Manufacturer = function() {\n    Characteristic.call(this, 'Manufacturer', '00000020-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.STRING,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Manufacturer, Characteristic);\n\nCharacteristic.Manufacturer.UUID = '00000020-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Model\"\n */\n\nCharacteristic.Model = function() {\n    Characteristic.call(this, 'Model', '00000021-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.STRING,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Model, Characteristic);\n\nCharacteristic.Model.UUID = '00000021-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Motion Detected\"\n */\n\nCharacteristic.MotionDetected = function() {\n    Characteristic.call(this, 'Motion Detected', '00000022-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.MotionDetected, Characteristic);\n\nCharacteristic.MotionDetected.UUID = '00000022-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Mute\"\n */\n\nCharacteristic.Mute = function() {\n    Characteristic.call(this, 'Mute', '0000011A-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Mute, Characteristic);\n\nCharacteristic.Mute.UUID = '0000011A-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Name\"\n */\n\nCharacteristic.Name = function() {\n    Characteristic.call(this, 'Name', '00000023-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.STRING,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Name, Characteristic);\n\nCharacteristic.Name.UUID = '00000023-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Night Vision\"\n */\n\nCharacteristic.NightVision = function() {\n    Characteristic.call(this, 'Night Vision', '0000011B-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.NightVision, Characteristic);\n\nCharacteristic.NightVision.UUID = '0000011B-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Nitrogen Dioxide Density\"\n */\n\nCharacteristic.NitrogenDioxideDensity = function() {\n    Characteristic.call(this, 'Nitrogen Dioxide Density', '000000C4-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 1000,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.NitrogenDioxideDensity, Characteristic);\n\nCharacteristic.NitrogenDioxideDensity.UUID = '000000C4-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Obstruction Detected\"\n */\n\nCharacteristic.ObstructionDetected = function() {\n    Characteristic.call(this, 'Obstruction Detected', '00000024-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ObstructionDetected, Characteristic);\n\nCharacteristic.ObstructionDetected.UUID = '00000024-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Occupancy Detected\"\n */\n\nCharacteristic.OccupancyDetected = function() {\n    Characteristic.call(this, 'Occupancy Detected', '00000071-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.OccupancyDetected, Characteristic);\n\nCharacteristic.OccupancyDetected.UUID = '00000071-0000-1000-8000-0026BB765291';\n\n// The value property of OccupancyDetected must be one of the following:\nCharacteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED = 0;\nCharacteristic.OccupancyDetected.OCCUPANCY_DETECTED = 1;\n\n/**\n * Characteristic \"On\"\n */\n\nCharacteristic.On = function() {\n    Characteristic.call(this, 'On', '00000025-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.On, Characteristic);\n\nCharacteristic.On.UUID = '00000025-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Optical Zoom\"\n */\n\nCharacteristic.OpticalZoom = function() {\n    Characteristic.call(this, 'Optical Zoom', '0000011C-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.OpticalZoom, Characteristic);\n\nCharacteristic.OpticalZoom.UUID = '0000011C-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Outlet In Use\"\n */\n\nCharacteristic.OutletInUse = function() {\n    Characteristic.call(this, 'Outlet In Use', '00000026-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.OutletInUse, Characteristic);\n\nCharacteristic.OutletInUse.UUID = '00000026-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Ozone Density\"\n */\n\nCharacteristic.OzoneDensity = function() {\n    Characteristic.call(this, 'Ozone Density', '000000C3-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 1000,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.OzoneDensity, Characteristic);\n\nCharacteristic.OzoneDensity.UUID = '000000C3-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Pair Setup\"\n */\n\nCharacteristic.PairSetup = function() {\n    Characteristic.call(this, 'Pair Setup', '0000004C-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.PairSetup, Characteristic);\n\nCharacteristic.PairSetup.UUID = '0000004C-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Pair Verify\"\n */\n\nCharacteristic.PairVerify = function() {\n    Characteristic.call(this, 'Pair Verify', '0000004E-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.PairVerify, Characteristic);\n\nCharacteristic.PairVerify.UUID = '0000004E-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Pairing Features\"\n */\n\nCharacteristic.PairingFeatures = function() {\n    Characteristic.call(this, 'Pairing Features', '0000004F-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.PairingFeatures, Characteristic);\n\nCharacteristic.PairingFeatures.UUID = '0000004F-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Pairing Pairings\"\n */\n\nCharacteristic.PairingPairings = function() {\n    Characteristic.call(this, 'Pairing Pairings', '00000050-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.PairingPairings, Characteristic);\n\nCharacteristic.PairingPairings.UUID = '00000050-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"PM10 Density\"\n */\n\nCharacteristic.PM10Density = function() {\n    Characteristic.call(this, 'PM10 Density', '000000C7-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 1000,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.PM10Density, Characteristic);\n\nCharacteristic.PM10Density.UUID = '000000C7-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"PM2.5 Density\"\n */\n\nCharacteristic.PM2_5Density = function() {\n    Characteristic.call(this, 'PM2.5 Density', '000000C6-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 1000,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.PM2_5Density, Characteristic);\n\nCharacteristic.PM2_5Density.UUID = '000000C6-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Position State\"\n */\n\nCharacteristic.PositionState = function() {\n    Characteristic.call(this, 'Position State', '00000072-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.PositionState, Characteristic);\n\nCharacteristic.PositionState.UUID = '00000072-0000-1000-8000-0026BB765291';\n\n// The value property of PositionState must be one of the following:\nCharacteristic.PositionState.DECREASING = 0;\nCharacteristic.PositionState.INCREASING = 1;\nCharacteristic.PositionState.STOPPED = 2;\n\n/**\n * Characteristic \"Program Mode\"\n */\n\nCharacteristic.ProgramMode = function() {\n    Characteristic.call(this, 'Program Mode', '000000D1-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ProgramMode, Characteristic);\n\nCharacteristic.ProgramMode.UUID = '000000D1-0000-1000-8000-0026BB765291';\n\n// The value property of ProgramMode must be one of the following:\nCharacteristic.ProgramMode.NO_PROGRAM_SCHEDULED = 0;\nCharacteristic.ProgramMode.PROGRAM_SCHEDULED = 1;\nCharacteristic.ProgramMode.PROGRAM_SCHEDULED_MANUAL_MODE_ = 2;\n\n/**\n * Characteristic \"Programmable Switch Event\"\n */\n\nCharacteristic.ProgrammableSwitchEvent = function() {\n    Characteristic.call(this, 'Programmable Switch Event', '00000073-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.eventOnlyCharacteristic = true; //Manual addition.\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ProgrammableSwitchEvent, Characteristic);\n\nCharacteristic.ProgrammableSwitchEvent.UUID = '00000073-0000-1000-8000-0026BB765291';\n\n// The value property of ProgrammableSwitchEvent must be one of the following:\nCharacteristic.ProgrammableSwitchEvent.SINGLE_PRESS = 0;\nCharacteristic.ProgrammableSwitchEvent.DOUBLE_PRESS = 1;\nCharacteristic.ProgrammableSwitchEvent.LONG_PRESS = 2;\n\n/**\n * Characteristic \"Relative Humidity Dehumidifier Threshold\"\n */\n\nCharacteristic.RelativeHumidityDehumidifierThreshold = function() {\n    Characteristic.call(this, 'Relative Humidity Dehumidifier Threshold', '000000C9-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RelativeHumidityDehumidifierThreshold, Characteristic);\n\nCharacteristic.RelativeHumidityDehumidifierThreshold.UUID = '000000C9-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Relative Humidity Humidifier Threshold\"\n */\n\nCharacteristic.RelativeHumidityHumidifierThreshold = function() {\n    Characteristic.call(this, 'Relative Humidity Humidifier Threshold', '000000CA-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RelativeHumidityHumidifierThreshold, Characteristic);\n\nCharacteristic.RelativeHumidityHumidifierThreshold.UUID = '000000CA-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Remaining Duration\"\n */\n\nCharacteristic.RemainingDuration = function() {\n    Characteristic.call(this, 'Remaining Duration', '000000D4-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT32,\n        maxValue: 3600,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RemainingDuration, Characteristic);\n\nCharacteristic.RemainingDuration.UUID = '000000D4-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Reset Filter Indication\"\n */\n\nCharacteristic.ResetFilterIndication = function() {\n    Characteristic.call(this, 'Reset Filter Indication', '000000AD-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 1,\n        minStep: 1,\n        perms: [Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ResetFilterIndication, Characteristic);\n\nCharacteristic.ResetFilterIndication.UUID = '000000AD-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Rotation Direction\"\n */\n\nCharacteristic.RotationDirection = function() {\n    Characteristic.call(this, 'Rotation Direction', '00000028-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RotationDirection, Characteristic);\n\nCharacteristic.RotationDirection.UUID = '00000028-0000-1000-8000-0026BB765291';\n\n// The value property of RotationDirection must be one of the following:\nCharacteristic.RotationDirection.CLOCKWISE = 0;\nCharacteristic.RotationDirection.COUNTER_CLOCKWISE = 1;\n\n/**\n * Characteristic \"Rotation Speed\"\n */\n\nCharacteristic.RotationSpeed = function() {\n    Characteristic.call(this, 'Rotation Speed', '00000029-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.RotationSpeed, Characteristic);\n\nCharacteristic.RotationSpeed.UUID = '00000029-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Saturation\"\n */\n\nCharacteristic.Saturation = function() {\n    Characteristic.call(this, 'Saturation', '0000002F-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Saturation, Characteristic);\n\nCharacteristic.Saturation.UUID = '0000002F-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Security System Alarm Type\"\n */\n\nCharacteristic.SecuritySystemAlarmType = function() {\n    Characteristic.call(this, 'Security System Alarm Type', '0000008E-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SecuritySystemAlarmType, Characteristic);\n\nCharacteristic.SecuritySystemAlarmType.UUID = '0000008E-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Security System Current State\"\n */\n\nCharacteristic.SecuritySystemCurrentState = function() {\n    Characteristic.call(this, 'Security System Current State', '00000066-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 4,\n        minValue: 0,\n        validValues: [0, 1, 2, 3, 4],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SecuritySystemCurrentState, Characteristic);\n\nCharacteristic.SecuritySystemCurrentState.UUID = '00000066-0000-1000-8000-0026BB765291';\n\n// The value property of SecuritySystemCurrentState must be one of the following:\nCharacteristic.SecuritySystemCurrentState.STAY_ARM = 0;\nCharacteristic.SecuritySystemCurrentState.AWAY_ARM = 1;\nCharacteristic.SecuritySystemCurrentState.NIGHT_ARM = 2;\nCharacteristic.SecuritySystemCurrentState.DISARMED = 3;\nCharacteristic.SecuritySystemCurrentState.ALARM_TRIGGERED = 4;\n\n/**\n * Characteristic \"Security System Target State\"\n */\n\nCharacteristic.SecuritySystemTargetState = function() {\n    Characteristic.call(this, 'Security System Target State', '00000067-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 3,\n        minValue: 0,\n        validValues: [0, 1, 2, 3],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SecuritySystemTargetState, Characteristic);\n\nCharacteristic.SecuritySystemTargetState.UUID = '00000067-0000-1000-8000-0026BB765291';\n\n// The value property of SecuritySystemTargetState must be one of the following:\nCharacteristic.SecuritySystemTargetState.STAY_ARM = 0;\nCharacteristic.SecuritySystemTargetState.AWAY_ARM = 1;\nCharacteristic.SecuritySystemTargetState.NIGHT_ARM = 2;\nCharacteristic.SecuritySystemTargetState.DISARM = 3;\n\n/**\n * Characteristic \"Selected RTP Stream Configuration\"\n */\n\nCharacteristic.SelectedRTPStreamConfiguration = function() {\n    Characteristic.call(this, 'Selected RTP Stream Configuration', '00000117-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SelectedRTPStreamConfiguration, Characteristic);\n\nCharacteristic.SelectedRTPStreamConfiguration.UUID = '00000117-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Serial Number\"\n */\n\nCharacteristic.SerialNumber = function() {\n    Characteristic.call(this, 'Serial Number', '00000030-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.STRING,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SerialNumber, Characteristic);\n\nCharacteristic.SerialNumber.UUID = '00000030-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Service Label Index\"\n */\n\nCharacteristic.ServiceLabelIndex = function() {\n    Characteristic.call(this, 'Service Label Index', '000000CB-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 255,\n        minValue: 1,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ServiceLabelIndex, Characteristic);\n\nCharacteristic.ServiceLabelIndex.UUID = '000000CB-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Service Label Namespace\"\n */\n\nCharacteristic.ServiceLabelNamespace = function() {\n    Characteristic.call(this, 'Service Label Namespace', '000000CD-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ServiceLabelNamespace, Characteristic);\n\nCharacteristic.ServiceLabelNamespace.UUID = '000000CD-0000-1000-8000-0026BB765291';\n\n// The value property of ServiceLabelNamespace must be one of the following:\nCharacteristic.ServiceLabelNamespace.DOTS = 0;\nCharacteristic.ServiceLabelNamespace.ARABIC_NUMERALS = 1;\n\n/**\n * Characteristic \"Set Duration\"\n */\n\nCharacteristic.SetDuration = function() {\n    Characteristic.call(this, 'Set Duration', '000000D3-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT32,\n        maxValue: 3600,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SetDuration, Characteristic);\n\nCharacteristic.SetDuration.UUID = '000000D3-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Setup Endpoints\"\n */\n\nCharacteristic.SetupEndpoints = function() {\n    Characteristic.call(this, 'Setup Endpoints', '00000118-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SetupEndpoints, Characteristic);\n\nCharacteristic.SetupEndpoints.UUID = '00000118-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Slat Type\"\n */\n\nCharacteristic.SlatType = function() {\n    Characteristic.call(this, 'Slat Type', '000000C0-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SlatType, Characteristic);\n\nCharacteristic.SlatType.UUID = '000000C0-0000-1000-8000-0026BB765291';\n\n// The value property of SlatType must be one of the following:\nCharacteristic.SlatType.HORIZONTAL = 0;\nCharacteristic.SlatType.VERTICAL = 1;\n\n/**\n * Characteristic \"Smoke Detected\"\n */\n\nCharacteristic.SmokeDetected = function() {\n    Characteristic.call(this, 'Smoke Detected', '00000076-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SmokeDetected, Characteristic);\n\nCharacteristic.SmokeDetected.UUID = '00000076-0000-1000-8000-0026BB765291';\n\n// The value property of SmokeDetected must be one of the following:\nCharacteristic.SmokeDetected.SMOKE_NOT_DETECTED = 0;\nCharacteristic.SmokeDetected.SMOKE_DETECTED = 1;\n\n/**\n * Characteristic \"Status Active\"\n */\n\nCharacteristic.StatusActive = function() {\n    Characteristic.call(this, 'Status Active', '00000075-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.BOOL,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.StatusActive, Characteristic);\n\nCharacteristic.StatusActive.UUID = '00000075-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Status Fault\"\n */\n\nCharacteristic.StatusFault = function() {\n    Characteristic.call(this, 'Status Fault', '00000077-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.StatusFault, Characteristic);\n\nCharacteristic.StatusFault.UUID = '00000077-0000-1000-8000-0026BB765291';\n\n// The value property of StatusFault must be one of the following:\nCharacteristic.StatusFault.NO_FAULT = 0;\nCharacteristic.StatusFault.GENERAL_FAULT = 1;\n\n/**\n * Characteristic \"Status Jammed\"\n */\n\nCharacteristic.StatusJammed = function() {\n    Characteristic.call(this, 'Status Jammed', '00000078-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.StatusJammed, Characteristic);\n\nCharacteristic.StatusJammed.UUID = '00000078-0000-1000-8000-0026BB765291';\n\n// The value property of StatusJammed must be one of the following:\nCharacteristic.StatusJammed.NOT_JAMMED = 0;\nCharacteristic.StatusJammed.JAMMED = 1;\n\n/**\n * Characteristic \"Status Low Battery\"\n */\n\nCharacteristic.StatusLowBattery = function() {\n    Characteristic.call(this, 'Status Low Battery', '00000079-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.StatusLowBattery, Characteristic);\n\nCharacteristic.StatusLowBattery.UUID = '00000079-0000-1000-8000-0026BB765291';\n\n// The value property of StatusLowBattery must be one of the following:\nCharacteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL = 0;\nCharacteristic.StatusLowBattery.BATTERY_LEVEL_LOW = 1;\n\n/**\n * Characteristic \"Status Tampered\"\n */\n\nCharacteristic.StatusTampered = function() {\n    Characteristic.call(this, 'Status Tampered', '0000007A-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.StatusTampered, Characteristic);\n\nCharacteristic.StatusTampered.UUID = '0000007A-0000-1000-8000-0026BB765291';\n\n// The value property of StatusTampered must be one of the following:\nCharacteristic.StatusTampered.NOT_TAMPERED = 0;\nCharacteristic.StatusTampered.TAMPERED = 1;\n\n/**\n * Characteristic \"Streaming Status\"\n */\n\nCharacteristic.StreamingStatus = function() {\n    Characteristic.call(this, 'Streaming Status', '00000120-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.StreamingStatus, Characteristic);\n\nCharacteristic.StreamingStatus.UUID = '00000120-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Sulphur Dioxide Density\"\n */\n\nCharacteristic.SulphurDioxideDensity = function() {\n    Characteristic.call(this, 'Sulphur Dioxide Density', '000000C5-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 1000,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SulphurDioxideDensity, Characteristic);\n\nCharacteristic.SulphurDioxideDensity.UUID = '000000C5-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Supported Audio Stream Configuration\"\n */\n\nCharacteristic.SupportedAudioStreamConfiguration = function() {\n    Characteristic.call(this, 'Supported Audio Stream Configuration', '00000115-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SupportedAudioStreamConfiguration, Characteristic);\n\nCharacteristic.SupportedAudioStreamConfiguration.UUID = '00000115-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Supported RTP Configuration\"\n */\n\nCharacteristic.SupportedRTPConfiguration = function() {\n    Characteristic.call(this, 'Supported RTP Configuration', '00000116-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SupportedRTPConfiguration, Characteristic);\n\nCharacteristic.SupportedRTPConfiguration.UUID = '00000116-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Supported Video Stream Configuration\"\n */\n\nCharacteristic.SupportedVideoStreamConfiguration = function() {\n    Characteristic.call(this, 'Supported Video Stream Configuration', '00000114-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.TLV8,\n        perms: [Characteristic.Perms.READ]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SupportedVideoStreamConfiguration, Characteristic);\n\nCharacteristic.SupportedVideoStreamConfiguration.UUID = '00000114-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Swing Mode\"\n */\n\nCharacteristic.SwingMode = function() {\n    Characteristic.call(this, 'Swing Mode', '000000B6-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.SwingMode, Characteristic);\n\nCharacteristic.SwingMode.UUID = '000000B6-0000-1000-8000-0026BB765291';\n\n// The value property of SwingMode must be one of the following:\nCharacteristic.SwingMode.SWING_DISABLED = 0;\nCharacteristic.SwingMode.SWING_ENABLED = 1;\n\n/**\n * Characteristic \"Target Air Purifier State\"\n */\n\nCharacteristic.TargetAirPurifierState = function() {\n    Characteristic.call(this, 'Target Air Purifier State', '000000A8-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetAirPurifierState, Characteristic);\n\nCharacteristic.TargetAirPurifierState.UUID = '000000A8-0000-1000-8000-0026BB765291';\n\n// The value property of TargetAirPurifierState must be one of the following:\nCharacteristic.TargetAirPurifierState.MANUAL = 0;\nCharacteristic.TargetAirPurifierState.AUTO = 1;\n\n/**\n * Characteristic \"Target Air Quality\"\n */\n\nCharacteristic.TargetAirQuality = function() {\n    Characteristic.call(this, 'Target Air Quality', '000000AE-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetAirQuality, Characteristic);\n\nCharacteristic.TargetAirQuality.UUID = '000000AE-0000-1000-8000-0026BB765291';\n\n// The value property of TargetAirQuality must be one of the following:\nCharacteristic.TargetAirQuality.EXCELLENT = 0;\nCharacteristic.TargetAirQuality.GOOD = 1;\nCharacteristic.TargetAirQuality.FAIR = 2;\n\n/**\n * Characteristic \"Target Door State\"\n */\n\nCharacteristic.TargetDoorState = function() {\n    Characteristic.call(this, 'Target Door State', '00000032-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetDoorState, Characteristic);\n\nCharacteristic.TargetDoorState.UUID = '00000032-0000-1000-8000-0026BB765291';\n\n// The value property of TargetDoorState must be one of the following:\nCharacteristic.TargetDoorState.OPEN = 0;\nCharacteristic.TargetDoorState.CLOSED = 1;\n\n/**\n * Characteristic \"Target Fan State\"\n */\n\nCharacteristic.TargetFanState = function() {\n    Characteristic.call(this, 'Target Fan State', '000000BF-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetFanState, Characteristic);\n\nCharacteristic.TargetFanState.UUID = '000000BF-0000-1000-8000-0026BB765291';\n\n// The value property of TargetFanState must be one of the following:\nCharacteristic.TargetFanState.MANUAL = 0;\nCharacteristic.TargetFanState.AUTO = 1;\n\n/**\n * Characteristic \"Target Heater Cooler State\"\n */\n\nCharacteristic.TargetHeaterCoolerState = function() {\n    Characteristic.call(this, 'Target Heater Cooler State', '000000B2-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetHeaterCoolerState, Characteristic);\n\nCharacteristic.TargetHeaterCoolerState.UUID = '000000B2-0000-1000-8000-0026BB765291';\n\n// The value property of TargetHeaterCoolerState must be one of the following:\nCharacteristic.TargetHeaterCoolerState.AUTO = 0;\nCharacteristic.TargetHeaterCoolerState.HEAT = 1;\nCharacteristic.TargetHeaterCoolerState.COOL = 2;\n\n/**\n * Characteristic \"Target Heating Cooling State\"\n */\n\nCharacteristic.TargetHeatingCoolingState = function() {\n    Characteristic.call(this, 'Target Heating Cooling State', '00000033-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 3,\n        minValue: 0,\n        validValues: [0, 1, 2, 3],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetHeatingCoolingState, Characteristic);\n\nCharacteristic.TargetHeatingCoolingState.UUID = '00000033-0000-1000-8000-0026BB765291';\n\n// The value property of TargetHeatingCoolingState must be one of the following:\nCharacteristic.TargetHeatingCoolingState.OFF = 0;\nCharacteristic.TargetHeatingCoolingState.HEAT = 1;\nCharacteristic.TargetHeatingCoolingState.COOL = 2;\nCharacteristic.TargetHeatingCoolingState.AUTO = 3;\n\n/**\n * Characteristic \"Target Horizontal Tilt Angle\"\n */\n\nCharacteristic.TargetHorizontalTiltAngle = function() {\n    Characteristic.call(this, 'Target Horizontal Tilt Angle', '0000007B-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 90,\n        minValue: -90,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetHorizontalTiltAngle, Characteristic);\n\nCharacteristic.TargetHorizontalTiltAngle.UUID = '0000007B-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Target Humidifier Dehumidifier State\"\n */\n\nCharacteristic.TargetHumidifierDehumidifierState = function() {\n    Characteristic.call(this, 'Target Humidifier Dehumidifier State', '000000B4-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 2,\n        minValue: 0,\n        validValues: [0, 1, 2],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetHumidifierDehumidifierState, Characteristic);\n\nCharacteristic.TargetHumidifierDehumidifierState.UUID = '000000B4-0000-1000-8000-0026BB765291';\n\n// The value property of TargetHumidifierDehumidifierState must be one of the following:\nCharacteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER = 0;\nCharacteristic.TargetHumidifierDehumidifierState.HUMIDIFIER = 1;\nCharacteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER = 2;\n\n/**\n * Characteristic \"Target Position\"\n */\n\nCharacteristic.TargetPosition = function() {\n    Characteristic.call(this, 'Target Position', '0000007C-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetPosition, Characteristic);\n\nCharacteristic.TargetPosition.UUID = '0000007C-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Target Relative Humidity\"\n */\n\nCharacteristic.TargetRelativeHumidity = function() {\n    Characteristic.call(this, 'Target Relative Humidity', '00000034-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetRelativeHumidity, Characteristic);\n\nCharacteristic.TargetRelativeHumidity.UUID = '00000034-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Target Slat State\"\n */\n\nCharacteristic.TargetSlatState = function() {\n    Characteristic.call(this, 'Target Slat State', '000000BE-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetSlatState, Characteristic);\n\nCharacteristic.TargetSlatState.UUID = '000000BE-0000-1000-8000-0026BB765291';\n\n// The value property of TargetSlatState must be one of the following:\nCharacteristic.TargetSlatState.MANUAL = 0;\nCharacteristic.TargetSlatState.AUTO = 1;\n\n/**\n * Characteristic \"Target Temperature\"\n */\n\nCharacteristic.TargetTemperature = function() {\n    Characteristic.call(this, 'Target Temperature', '00000035-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        unit: Characteristic.Units.CELSIUS,\n        maxValue: 38,\n        minValue: 10,\n        minStep: 0.1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetTemperature, Characteristic);\n\nCharacteristic.TargetTemperature.UUID = '00000035-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Target Tilt Angle\"\n */\n\nCharacteristic.TargetTiltAngle = function() {\n    Characteristic.call(this, 'Target Tilt Angle', '000000C2-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 90,\n        minValue: -90,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetTiltAngle, Characteristic);\n\nCharacteristic.TargetTiltAngle.UUID = '000000C2-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Target Vertical Tilt Angle\"\n */\n\nCharacteristic.TargetVerticalTiltAngle = function() {\n    Characteristic.call(this, 'Target Vertical Tilt Angle', '0000007D-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.INT,\n        unit: Characteristic.Units.ARC_DEGREE,\n        maxValue: 90,\n        minValue: -90,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TargetVerticalTiltAngle, Characteristic);\n\nCharacteristic.TargetVerticalTiltAngle.UUID = '0000007D-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Temperature Display Units\"\n */\n\nCharacteristic.TemperatureDisplayUnits = function() {\n    Characteristic.call(this, 'Temperature Display Units', '00000036-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 1,\n        minValue: 0,\n        validValues: [0, 1],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.TemperatureDisplayUnits, Characteristic);\n\nCharacteristic.TemperatureDisplayUnits.UUID = '00000036-0000-1000-8000-0026BB765291';\n\n// The value property of TemperatureDisplayUnits must be one of the following:\nCharacteristic.TemperatureDisplayUnits.CELSIUS = 0;\nCharacteristic.TemperatureDisplayUnits.FAHRENHEIT = 1;\n\n/**\n * Characteristic \"Valve Type\"\n */\n\nCharacteristic.ValveType = function() {\n    Characteristic.call(this, 'Valve Type', '000000D5-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        maxValue: 3,\n        minValue: 0,\n        validValues: [0, 1, 2, 3],\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.ValveType, Characteristic);\n\nCharacteristic.ValveType.UUID = '000000D5-0000-1000-8000-0026BB765291';\n\n// The value property of ValveType must be one of the following:\nCharacteristic.ValveType.GENERIC_VALVE = 0;\nCharacteristic.ValveType.IRRIGATION = 1;\nCharacteristic.ValveType.SHOWER_HEAD = 2;\nCharacteristic.ValveType.WATER_FAUCET = 3;\n\n/**\n * Characteristic \"Version\"\n */\n\nCharacteristic.Version = function() {\n    Characteristic.call(this, 'Version', '00000037-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.STRING,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Version, Characteristic);\n\nCharacteristic.Version.UUID = '00000037-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"VOC Density\"\n */\n\nCharacteristic.VOCDensity = function() {\n    Characteristic.call(this, 'VOC Density', '000000C8-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 1000,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.VOCDensity, Characteristic);\n\nCharacteristic.VOCDensity.UUID = '000000C8-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Volume\"\n */\n\nCharacteristic.Volume = function() {\n    Characteristic.call(this, 'Volume', '00000119-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.UINT8,\n        unit: Characteristic.Units.PERCENTAGE,\n        maxValue: 100,\n        minValue: 0,\n        minStep: 1,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.Volume, Characteristic);\n\nCharacteristic.Volume.UUID = '00000119-0000-1000-8000-0026BB765291';\n\n/**\n * Characteristic \"Water Level\"\n */\n\nCharacteristic.WaterLevel = function() {\n    Characteristic.call(this, 'Water Level', '000000B5-0000-1000-8000-0026BB765291');\n    this.setProps({\n        format: Characteristic.Formats.FLOAT,\n        maxValue: 100,\n        minValue: 0,\n        perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]\n    });\n    this.value = this.getDefaultValue();\n};\n\ninherits(Characteristic.WaterLevel, Characteristic);\n\nCharacteristic.WaterLevel.UUID = '000000B5-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Accessory Information\"\n */\n\nService.AccessoryInformation = function(displayName, subtype) {\n    Service.call(this, displayName, '0000003E-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Identify);\n    this.addCharacteristic(Characteristic.Manufacturer);\n    this.addCharacteristic(Characteristic.Model);\n    this.addCharacteristic(Characteristic.Name);\n    this.addCharacteristic(Characteristic.SerialNumber);\n    this.addCharacteristic(Characteristic.FirmwareRevision);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.HardwareRevision);\n    this.addOptionalCharacteristic(Characteristic.AccessoryFlags);\n};\n\ninherits(Service.AccessoryInformation, Service);\n\nService.AccessoryInformation.UUID = '0000003E-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Air Purifier\"\n */\n\nService.AirPurifier = function(displayName, subtype) {\n    Service.call(this, displayName, '000000BB-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Active);\n    this.addCharacteristic(Characteristic.CurrentAirPurifierState);\n    this.addCharacteristic(Characteristic.TargetAirPurifierState);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.LockPhysicalControls);\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.SwingMode);\n    this.addOptionalCharacteristic(Characteristic.RotationSpeed);\n};\n\ninherits(Service.AirPurifier, Service);\n\nService.AirPurifier.UUID = '000000BB-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Air Quality Sensor\"\n */\n\nService.AirQualitySensor = function(displayName, subtype) {\n    Service.call(this, displayName, '0000008D-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.AirQuality);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.OzoneDensity);\n    this.addOptionalCharacteristic(Characteristic.NitrogenDioxideDensity);\n    this.addOptionalCharacteristic(Characteristic.SulphurDioxideDensity);\n    this.addOptionalCharacteristic(Characteristic.PM2_5Density);\n    this.addOptionalCharacteristic(Characteristic.PM10Density);\n    this.addOptionalCharacteristic(Characteristic.VOCDensity);\n    this.addOptionalCharacteristic(Characteristic.CarbonMonoxideLevel);\n    this.addOptionalCharacteristic(Characteristic.CarbonDioxideLevel);\n};\n\ninherits(Service.AirQualitySensor, Service);\n\nService.AirQualitySensor.UUID = '0000008D-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Battery Service\"\n */\n\nService.BatteryService = function(displayName, subtype) {\n    Service.call(this, displayName, '00000096-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.BatteryLevel);\n    this.addCharacteristic(Characteristic.ChargingState);\n    this.addCharacteristic(Characteristic.StatusLowBattery);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.BatteryService, Service);\n\nService.BatteryService.UUID = '00000096-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Camera RTP Stream Management\"\n */\n\nService.CameraRTPStreamManagement = function(displayName, subtype) {\n    Service.call(this, displayName, '00000110-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.SupportedVideoStreamConfiguration);\n    this.addCharacteristic(Characteristic.SupportedAudioStreamConfiguration);\n    this.addCharacteristic(Characteristic.SupportedRTPConfiguration);\n    this.addCharacteristic(Characteristic.SelectedRTPStreamConfiguration);\n    this.addCharacteristic(Characteristic.StreamingStatus);\n    this.addCharacteristic(Characteristic.SetupEndpoints);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.CameraRTPStreamManagement, Service);\n\nService.CameraRTPStreamManagement.UUID = '00000110-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Carbon Dioxide Sensor\"\n */\n\nService.CarbonDioxideSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000097-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CarbonDioxideDetected);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.CarbonDioxideLevel);\n    this.addOptionalCharacteristic(Characteristic.CarbonDioxidePeakLevel);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.CarbonDioxideSensor, Service);\n\nService.CarbonDioxideSensor.UUID = '00000097-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Carbon Monoxide Sensor\"\n */\n\nService.CarbonMonoxideSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '0000007F-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CarbonMonoxideDetected);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.CarbonMonoxideLevel);\n    this.addOptionalCharacteristic(Characteristic.CarbonMonoxidePeakLevel);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.CarbonMonoxideSensor, Service);\n\nService.CarbonMonoxideSensor.UUID = '0000007F-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Contact Sensor\"\n */\n\nService.ContactSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000080-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.ContactSensorState);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.ContactSensor, Service);\n\nService.ContactSensor.UUID = '00000080-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Door\"\n */\n\nService.Door = function(displayName, subtype) {\n    Service.call(this, displayName, '00000081-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentPosition);\n    this.addCharacteristic(Characteristic.PositionState);\n    this.addCharacteristic(Characteristic.TargetPosition);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.HoldPosition);\n    this.addOptionalCharacteristic(Characteristic.ObstructionDetected);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Door, Service);\n\nService.Door.UUID = '00000081-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Doorbell\"\n */\n\nService.Doorbell = function(displayName, subtype) {\n    Service.call(this, displayName, '00000121-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.ProgrammableSwitchEvent);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Brightness);\n    this.addOptionalCharacteristic(Characteristic.Volume);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Doorbell, Service);\n\nService.Doorbell.UUID = '00000121-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Fan\"\n */\n\nService.Fan = function(displayName, subtype) {\n    Service.call(this, displayName, '00000040-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.On);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.RotationDirection);\n    this.addOptionalCharacteristic(Characteristic.RotationSpeed);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Fan, Service);\n\nService.Fan.UUID = '00000040-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Fan v2\"\n */\n\nService.Fanv2 = function(displayName, subtype) {\n    Service.call(this, displayName, '000000B7-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Active);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.CurrentFanState);\n    this.addOptionalCharacteristic(Characteristic.TargetFanState);\n    this.addOptionalCharacteristic(Characteristic.LockPhysicalControls);\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.RotationDirection);\n    this.addOptionalCharacteristic(Characteristic.RotationSpeed);\n    this.addOptionalCharacteristic(Characteristic.SwingMode);\n};\n\ninherits(Service.Fanv2, Service);\n\nService.Fanv2.UUID = '000000B7-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Filter Maintenance\"\n */\n\nService.FilterMaintenance = function(displayName, subtype) {\n    Service.call(this, displayName, '000000BA-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.FilterChangeIndication);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.FilterLifeLevel);\n    this.addOptionalCharacteristic(Characteristic.ResetFilterIndication);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.FilterMaintenance, Service);\n\nService.FilterMaintenance.UUID = '000000BA-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Faucet\"\n */\n\nService.Faucet = function(displayName, subtype) {\n    Service.call(this, displayName, '000000D7-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Active);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n};\n\ninherits(Service.Faucet, Service);\n\nService.Faucet.UUID = '000000D7-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Garage Door Opener\"\n */\n\nService.GarageDoorOpener = function(displayName, subtype) {\n    Service.call(this, displayName, '00000041-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentDoorState);\n    this.addCharacteristic(Characteristic.TargetDoorState);\n    this.addCharacteristic(Characteristic.ObstructionDetected);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.LockCurrentState);\n    this.addOptionalCharacteristic(Characteristic.LockTargetState);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.GarageDoorOpener, Service);\n\nService.GarageDoorOpener.UUID = '00000041-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Heater Cooler\"\n */\n\nService.HeaterCooler = function(displayName, subtype) {\n    Service.call(this, displayName, '000000BC-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Active);\n    this.addCharacteristic(Characteristic.CurrentHeaterCoolerState);\n    this.addCharacteristic(Characteristic.TargetHeaterCoolerState);\n    this.addCharacteristic(Characteristic.CurrentTemperature);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.LockPhysicalControls);\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.SwingMode);\n    this.addOptionalCharacteristic(Characteristic.CoolingThresholdTemperature);\n    this.addOptionalCharacteristic(Characteristic.HeatingThresholdTemperature);\n    this.addOptionalCharacteristic(Characteristic.TemperatureDisplayUnits);\n    this.addOptionalCharacteristic(Characteristic.RotationSpeed);\n};\n\ninherits(Service.HeaterCooler, Service);\n\nService.HeaterCooler.UUID = '000000BC-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Humidifier Dehumidifier\"\n */\n\nService.HumidifierDehumidifier = function(displayName, subtype) {\n    Service.call(this, displayName, '000000BD-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentRelativeHumidity);\n    this.addCharacteristic(Characteristic.CurrentHumidifierDehumidifierState);\n    this.addCharacteristic(Characteristic.TargetHumidifierDehumidifierState);\n    this.addCharacteristic(Characteristic.Active);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.LockPhysicalControls);\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.SwingMode);\n    this.addOptionalCharacteristic(Characteristic.WaterLevel);\n    this.addOptionalCharacteristic(Characteristic.RelativeHumidityDehumidifierThreshold);\n    this.addOptionalCharacteristic(Characteristic.RelativeHumidityHumidifierThreshold);\n    this.addOptionalCharacteristic(Characteristic.RotationSpeed);\n};\n\ninherits(Service.HumidifierDehumidifier, Service);\n\nService.HumidifierDehumidifier.UUID = '000000BD-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Humidity Sensor\"\n */\n\nService.HumiditySensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000082-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentRelativeHumidity);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.HumiditySensor, Service);\n\nService.HumiditySensor.UUID = '00000082-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Irrigation System\"\n */\n\nService.IrrigationSystem = function(displayName, subtype) {\n    Service.call(this, displayName, '000000CF-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Active);\n    this.addCharacteristic(Characteristic.ProgramMode);\n    this.addCharacteristic(Characteristic.InUse);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.RemainingDuration);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n};\n\ninherits(Service.IrrigationSystem, Service);\n\nService.IrrigationSystem.UUID = '000000CF-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Leak Sensor\"\n */\n\nService.LeakSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000083-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.LeakDetected);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.LeakSensor, Service);\n\nService.LeakSensor.UUID = '00000083-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Light Sensor\"\n */\n\nService.LightSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000084-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentAmbientLightLevel);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.LightSensor, Service);\n\nService.LightSensor.UUID = '00000084-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Lightbulb\"\n */\n\nService.Lightbulb = function(displayName, subtype) {\n    Service.call(this, displayName, '00000043-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.On);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Brightness);\n    this.addOptionalCharacteristic(Characteristic.Hue);\n    this.addOptionalCharacteristic(Characteristic.Saturation);\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.ColorTemperature); //Manual fix to add temperature\n};\n\ninherits(Service.Lightbulb, Service);\n\nService.Lightbulb.UUID = '00000043-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Lock Management\"\n */\n\nService.LockManagement = function(displayName, subtype) {\n    Service.call(this, displayName, '00000044-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.LockControlPoint);\n    this.addCharacteristic(Characteristic.Version);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Logs);\n    this.addOptionalCharacteristic(Characteristic.AudioFeedback);\n    this.addOptionalCharacteristic(Characteristic.LockManagementAutoSecurityTimeout);\n    this.addOptionalCharacteristic(Characteristic.AdministratorOnlyAccess);\n    this.addOptionalCharacteristic(Characteristic.LockLastKnownAction);\n    this.addOptionalCharacteristic(Characteristic.CurrentDoorState);\n    this.addOptionalCharacteristic(Characteristic.MotionDetected);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.LockManagement, Service);\n\nService.LockManagement.UUID = '00000044-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Lock Mechanism\"\n */\n\nService.LockMechanism = function(displayName, subtype) {\n    Service.call(this, displayName, '00000045-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.LockCurrentState);\n    this.addCharacteristic(Characteristic.LockTargetState);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.LockMechanism, Service);\n\nService.LockMechanism.UUID = '00000045-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Microphone\"\n */\n\nService.Microphone = function(displayName, subtype) {\n    Service.call(this, displayName, '00000112-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Mute);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Volume);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Microphone, Service);\n\nService.Microphone.UUID = '00000112-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Motion Sensor\"\n */\n\nService.MotionSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000085-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.MotionDetected);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.MotionSensor, Service);\n\nService.MotionSensor.UUID = '00000085-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Occupancy Sensor\"\n */\n\nService.OccupancySensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000086-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.OccupancyDetected);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.OccupancySensor, Service);\n\nService.OccupancySensor.UUID = '00000086-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Outlet\"\n */\n\nService.Outlet = function(displayName, subtype) {\n    Service.call(this, displayName, '00000047-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.On);\n    this.addCharacteristic(Characteristic.OutletInUse);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Outlet, Service);\n\nService.Outlet.UUID = '00000047-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Security System\"\n */\n\nService.SecuritySystem = function(displayName, subtype) {\n    Service.call(this, displayName, '0000007E-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.SecuritySystemCurrentState);\n    this.addCharacteristic(Characteristic.SecuritySystemTargetState);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.SecuritySystemAlarmType);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.SecuritySystem, Service);\n\nService.SecuritySystem.UUID = '0000007E-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Service Label\"\n */\n\nService.ServiceLabel = function(displayName, subtype) {\n    Service.call(this, displayName, '000000CC-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.ServiceLabelNamespace);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.ServiceLabel, Service);\n\nService.ServiceLabel.UUID = '000000CC-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Slat\"\n */\n\nService.Slat = function(displayName, subtype) {\n    Service.call(this, displayName, '000000B9-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.SlatType);\n    this.addCharacteristic(Characteristic.CurrentSlatState);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.CurrentTiltAngle);\n    this.addOptionalCharacteristic(Characteristic.TargetTiltAngle);\n    this.addOptionalCharacteristic(Characteristic.SwingMode);\n};\n\ninherits(Service.Slat, Service);\n\nService.Slat.UUID = '000000B9-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Smoke Sensor\"\n */\n\nService.SmokeSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '00000087-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.SmokeDetected);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.SmokeSensor, Service);\n\nService.SmokeSensor.UUID = '00000087-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Speaker\"\n */\n\nService.Speaker = function(displayName, subtype) {\n    Service.call(this, displayName, '00000113-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Mute);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Volume);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Speaker, Service);\n\nService.Speaker.UUID = '00000113-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Stateless Programmable Switch\"\n */\n\nService.StatelessProgrammableSwitch = function(displayName, subtype) {\n    Service.call(this, displayName, '00000089-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.ProgrammableSwitchEvent);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n    this.addOptionalCharacteristic(Characteristic.ServiceLabelIndex);\n};\n\ninherits(Service.StatelessProgrammableSwitch, Service);\n\nService.StatelessProgrammableSwitch.UUID = '00000089-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Switch\"\n */\n\nService.Switch = function(displayName, subtype) {\n    Service.call(this, displayName, '00000049-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.On);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Switch, Service);\n\nService.Switch.UUID = '00000049-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Temperature Sensor\"\n */\n\nService.TemperatureSensor = function(displayName, subtype) {\n    Service.call(this, displayName, '0000008A-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentTemperature);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.StatusActive);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.StatusLowBattery);\n    this.addOptionalCharacteristic(Characteristic.StatusTampered);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.TemperatureSensor, Service);\n\nService.TemperatureSensor.UUID = '0000008A-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Thermostat\"\n */\n\nService.Thermostat = function(displayName, subtype) {\n    Service.call(this, displayName, '0000004A-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentHeatingCoolingState);\n    this.addCharacteristic(Characteristic.TargetHeatingCoolingState);\n    this.addCharacteristic(Characteristic.CurrentTemperature);\n    this.addCharacteristic(Characteristic.TargetTemperature);\n    this.addCharacteristic(Characteristic.TemperatureDisplayUnits);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.CurrentRelativeHumidity);\n    this.addOptionalCharacteristic(Characteristic.TargetRelativeHumidity);\n    this.addOptionalCharacteristic(Characteristic.CoolingThresholdTemperature);\n    this.addOptionalCharacteristic(Characteristic.HeatingThresholdTemperature);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Thermostat, Service);\n\nService.Thermostat.UUID = '0000004A-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Valve\"\n */\n\nService.Valve = function(displayName, subtype) {\n    Service.call(this, displayName, '000000D0-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.Active);\n    this.addCharacteristic(Characteristic.InUse);\n    this.addCharacteristic(Characteristic.ValveType);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.SetDuration);\n    this.addOptionalCharacteristic(Characteristic.RemainingDuration);\n    this.addOptionalCharacteristic(Characteristic.IsConfigured);\n    this.addOptionalCharacteristic(Characteristic.ServiceLabelIndex);\n    this.addOptionalCharacteristic(Characteristic.StatusFault);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Valve, Service);\n\nService.Valve.UUID = '000000D0-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Window\"\n */\n\nService.Window = function(displayName, subtype) {\n    Service.call(this, displayName, '0000008B-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentPosition);\n    this.addCharacteristic(Characteristic.TargetPosition);\n    this.addCharacteristic(Characteristic.PositionState);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.HoldPosition);\n    this.addOptionalCharacteristic(Characteristic.ObstructionDetected);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.Window, Service);\n\nService.Window.UUID = '0000008B-0000-1000-8000-0026BB765291';\n\n/**\n * Service \"Window Covering\"\n */\n\nService.WindowCovering = function(displayName, subtype) {\n    Service.call(this, displayName, '0000008C-0000-1000-8000-0026BB765291', subtype);\n\n    // Required Characteristics\n    this.addCharacteristic(Characteristic.CurrentPosition);\n    this.addCharacteristic(Characteristic.TargetPosition);\n    this.addCharacteristic(Characteristic.PositionState);\n\n    // Optional Characteristics\n    this.addOptionalCharacteristic(Characteristic.HoldPosition);\n    this.addOptionalCharacteristic(Characteristic.TargetHorizontalTiltAngle);\n    this.addOptionalCharacteristic(Characteristic.TargetVerticalTiltAngle);\n    this.addOptionalCharacteristic(Characteristic.CurrentHorizontalTiltAngle);\n    this.addOptionalCharacteristic(Characteristic.CurrentVerticalTiltAngle);\n    this.addOptionalCharacteristic(Characteristic.ObstructionDetected);\n    this.addOptionalCharacteristic(Characteristic.Name);\n};\n\ninherits(Service.WindowCovering, Service);\n\nService.WindowCovering.UUID = '0000008C-0000-1000-8000-0026BB765291';\n\nvar HomeKitTypesBridge = require('./HomeKitTypes-Bridge');"
  },
  {
    "path": "src/libs/Logger.js",
    "content": "const pluginName = require(\"./Constants\").pluginName,\n    chalk = require('chalk'),\n    { createLogger, format, transports } = require('winston'),\n    { combine } = format,\n    util = require('util'),\n    DailyRotateFile = require('winston-daily-rotate-file');\n\n// rotateFile = require('winston-daily-rotate-file');\nvar DEBUG_ENABLED = false;\nvar TIMESTAMP_ENABLED = true;\nvar logger;\n// var tailWindowsUrl = 'https://download.microsoft.com/download/8/e/c/8ec3a7d8-05b4-440a-a71e-ca3ee25fe057/rktools.exe';\n\nmodule.exports = class Logging {\n    constructor(platform, prefix, config) {\n        this.platform = platform;\n        this.logConfig = config;\n        this.homebridge = platform.homebridge;\n        this.logLevel = 'good';\n        let pre = prefix;\n        if (this.logConfig) {\n            if (this.logConfig.debug === true) {\n                this.logLevel = 'debug';\n                DEBUG_ENABLED = (this.logConfig.debug === true);\n            }\n            TIMESTAMP_ENABLED = (this.logConfig.hideTimestamp === false);\n            pre = (this.logConfig.hideNamePrefix === true) ? '' : pre;\n        }\n        this.options = {\n            levels: {\n                error: 0,\n                warn: 1,\n                info: 2,\n                notice: 3,\n                alert: 4,\n                good: 5,\n                debug: 6\n            }\n        };\n        this.prefix = pre;\n    }\n\n    getLogger() {\n        let that = this;\n        let trans = [\n            new transports.Console({\n                level: this.logLevel,\n                colorize: true,\n                handleExceptions: true,\n                format: combine(\n                    format.timestamp({ format: 'M/D/YYYY, h:mm:ss a' }),\n                    format.printf((info) => {\n                        const timestamp = (TIMESTAMP_ENABLED === true) ? chalk.white(\"[\" + info.timestamp.trim() + \"] \") : '';\n                        const prefix = that.prefix ? chalk.cyan(\"[\" + that.prefix + \"] \") : '';\n                        const strArgs = (info[Symbol.for('splat')] || []).map((arg) => {\n                            return util.inspect(arg, { colors: true });\n                        }).join(' ');\n                        const message = (`${this.colorMsgLevel(info.level, info.message + ' ' + strArgs)}`).trim();\n                        return `${timestamp}${prefix}${this.levelColor(info.level.toUpperCase())}: ${message}`;\n                    })\n                )\n            })\n        ];\n        if (this.logConfig && this.logConfig.file && this.logConfig.file.enabled) {\n            trans.push(new DailyRotateFile({\n                filename: `${this.homebridge.user.storagePath()}/${pluginName}-%DATE%.log`,\n                datePattern: 'YYYY-MM-DD',\n                createSymlink: true,\n                symlinkName: `${pluginName}.log`,\n                level: this.logConfig.file.level || this.logLevel,\n                auditFile: `${this.homebridge.user.storagePath()}/${pluginName}-logaudit.json`,\n                colorize: false,\n                handleExceptions: true,\n                zippedArchive: (this.logConfig.file.compress !== false),\n                maxFiles: this.logConfig.file.daysToKeep || 5,\n                maxSize: this.logConfig.file.maxFilesize || '10m',\n                format: combine(\n                    format.timestamp({ format: 'M/D/YYYY, h:mm:ss a' }),\n                    format.printf((info) => {\n                        const strArgs = (info[Symbol.for('splat')] || []).map((arg) => {\n                            return util.inspect(arg, { colors: true });\n                        }).join(' ');\n                        return `[${info.timestamp.trim()}] [${info.level.toUpperCase()}]: ${this.removeAnsi(info.message + ' ' + strArgs)}`;\n                    })\n                )\n            }));\n        }\n        logger = createLogger({\n            levels: this.options.levels,\n            colors: this.options.colors,\n            transports: trans,\n            exitOnError: false\n        });\n        return logger;\n    }\n\n    removeAnsi(msg) {\n        // eslint-disable-next-line no-control-regex\n        return msg.replace(/[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');\n    }\n\n    getLogLevel(lvl) {\n        return this.options.level[lvl] || 5;\n    }\n\n    levelColor(lvl) {\n        switch (lvl) {\n            case 'DEBUG':\n                if (DEBUG_ENABLED)\n                    return chalk.bold.gray(lvl);\n                break;\n            case 'WARN':\n                return chalk.bold.keyword('orange')(lvl);\n            case 'ERROR':\n                return chalk.bold.red(lvl);\n            case 'GOOD':\n                return chalk.bold.green(lvl);\n            case 'INFO':\n                return chalk.bold.whiteBright(lvl);\n            case 'ALERT':\n                return chalk.bold.yellow(lvl);\n            case 'NOTICE':\n                return chalk.bold.blueBright(lvl);\n            case 'CUSTOM':\n                return '';\n            default:\n                return lvl;\n        }\n    }\n\n    colorMsgLevel(lvl, msg) {\n        if (msg.startsWith('chalk')) return msg;\n        switch (lvl) {\n            case 'debug':\n                if (DEBUG_ENABLED)\n                    return chalk.gray(msg);\n                break;\n            case 'warn':\n                return chalk.keyword('orange').bold(msg);\n            case 'error':\n                return chalk.bold.red(msg);\n            case 'good':\n                return chalk.green(msg);\n            case 'info':\n                return chalk.white(msg);\n            case 'alert':\n                return chalk.yellow(msg);\n            case 'notice':\n                return chalk.blueBright(msg);\n            case 'custom':\n                return chalk `${msg}`;\n            default:\n                return msg;\n        }\n    }\n\n    enabledDebug() {\n        DEBUG_ENABLED = true;\n    }\n\n    disableDebug() {\n        DEBUG_ENABLED = false;\n    }\n\n    enabledTimestamp() {\n        TIMESTAMP_ENABLED = true;\n    }\n\n    disableTimestamp() {\n        TIMESTAMP_ENABLED = false;\n    }\n};"
  },
  {
    "path": "src/libs/MyUtils.js",
    "content": "const {\n    // platformName,\n    // platformDesc,\n    packageFile\n} = require(\"./Constants\"),\n    _ = require(\"lodash\"),\n    fs = require(\"fs\"),\n    childProcess = require(\"child_process\"),\n    compareVersions = require(\"compare-versions\"),\n    os = require(\"os\");\n\nmodule.exports = class MyUtils {\n    constructor(platform) {\n        this.platform = platform;\n        this.client = platform.client;\n        this.log = platform.log;\n        this.homebridge = platform.homebridge;\n    }\n\n    cleanSpaces(str) {\n        return String(str.replace(/ /g, \"\"));\n    }\n\n    toTitleCase(str) {\n        return str.replace(/\\w\\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());\n    }\n\n    debounce(a, b, c) {\n        let d;\n        return function() {\n            let e = this,\n                f = arguments;\n            clearTimeout(d),\n                (d = setTimeout(function() {\n                    (d = null), c || a.apply(e, f);\n                }, b)),\n                c && !d && a.apply(e, f);\n        };\n    }\n\n    getIPAddress() {\n        let interfaces = os.networkInterfaces();\n        for (let devName in interfaces) {\n            let iface = interfaces[devName];\n            for (let i = 0; i < iface.length; i++) {\n                let alias = iface[i];\n                if (alias.family === \"IPv4\" && alias.address !== \"127.0.0.1\" && !alias.internal) {\n                    return alias.address;\n                }\n            }\n        }\n        return \"0.0.0.0\";\n    }\n\n    updateConfig(newConfig) {\n        const configPath = this.homebridge.user.configPath();\n        const file = fs.readFileSync(configPath);\n        const config = JSON.parse(file);\n        const platConfig = config.platforms.find(x => x.name === this.config.name);\n        _.extend(platConfig, newConfig);\n        const serializedConfig = JSON.stringify(config, null, \"  \");\n        fs.writeFileSync(configPath, serializedConfig, \"utf8\");\n        _.extend(this.config, newConfig);\n    }\n\n    checkVersion() {\n        this.log.info(\"Checking Package Version for Updates...\");\n        return new Promise((resolve) => {\n            childProcess.exec(\n                `npm view ${packageFile.name} version`,\n                (error, stdout) => {\n                    const newVer = stdout && stdout.trim();\n                    if (newVer && compareVersions(stdout.trim(), packageFile.version) > 0) {\n                        this.log.warn(`---------------------------------------------------------------`);\n                        this.log.warn(`NOTICE: New version of ${packageFile.name} available: ${newVer}`);\n                        this.log.warn(`---------------------------------------------------------------`);\n                        resolve({\n                            hasUpdate: true,\n                            newVersion: newVer\n                        });\n                    } else {\n                        this.log.info(`INFO: Your plugin version is up-to-date`);\n                        resolve({\n                            hasUpdate: false,\n                            newVersion: newVer\n                        });\n                    }\n                }\n            );\n        });\n    }\n};"
  }
]