├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── docs ├── AlarmClock.md ├── AlarmClock.png ├── AutomationSwitch.md ├── AutomationSwitch.png ├── Configuration.md ├── CustomCharacteristics.md ├── LockMechanism.md ├── LockMechanism.png ├── Random.md ├── Random.png ├── RandomExample.png ├── SecuritySystem.md ├── SecuritySystem.png ├── Slider.md ├── Slider.png ├── SliderWithSteps.png ├── SolarClock.md ├── SolarClock.png ├── Switch.md └── Switch.png ├── examples ├── alarmclock │ └── config.json ├── securitysystem │ └── config.json ├── slider │ └── config.json └── switch │ └── config.json ├── package-lock.json ├── package.json ├── src ├── AlarmClockAccessory.js ├── AutomationSwitchAccessory.js ├── HomeKitTypes.js ├── LockMechanismAccessory.js ├── RandomAccessory.js ├── SecuritySystemAccessory.js ├── SliderAccessory.js ├── SolarClockAccessory.js ├── SwitchAccessory.js ├── hap │ ├── ClockTypes.js │ ├── RandomTypes.js │ └── SolarTypes.js ├── index.js └── util │ ├── FakeStorageWrapper.js │ ├── NameFactory.js │ ├── SerialNumberGenerator.js │ └── StorageWrapper.js └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "indent": [ 12 | "error", 13 | 2, 14 | { 15 | "SwitchCase": 1 16 | } 17 | ], 18 | "linebreak-style": [ 19 | "error", 20 | "unix" 21 | ], 22 | "quotes": [ 23 | "error", 24 | "single" 25 | ], 26 | "semi": [ 27 | "error", 28 | "always" 29 | ] 30 | } 31 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .vscode/* 4 | 5 | examples/*/accessories/* 6 | examples/*/persist/* 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "9" 4 | 5 | script: 6 | - npm run lint 7 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # homebridge-automation-switches Contributors (sorted alphabetically) 2 | 3 | * [@grover](https://github.com/grover) 4 | 5 | * Forked homebridge-delay-switch 6 | * Added automation switch accessory 7 | * Added lock mechanism accessory 8 | * Added security system accessory 9 | * Added plain switch accessory 10 | * Added persistence support 11 | 12 | * [@nitaybz](https://github.com/nitaybz) 13 | 14 | * Original author of [homebridge-delay-switch](https://github.com/nitaybz/homebridge-delay-switch), which inspired this plugin 15 | 16 | * [@paolotremdio](https://github.com/paolotremadio) 17 | 18 | * Enhanced security system with zone support 19 | * Added arm switches to circumvent HomeKit restrictions for remote arming 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Michael Fröhlich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Automation Switches Platform 3 | 4 | A platform that provides configurable switches for automation purposes. This platform can be created to provide time delayed responses in HomeKit rules or to simulate security systems. 5 | 6 | ## Status 7 | 8 | [![HitCount](http://hits.dwyl.io/grover/homebridge-automation-switches.svg)](https://github.com/grover/homebridge-automation-switches) 9 | [![Build Status](https://travis-ci.org/grover/homebridge-automation-switches.png?branch=master)](https://travis-ci.org/grover/homebridge-automation-switches) 10 | [![Node version](https://img.shields.io/node/v/homebridge-automation-switches.svg?style=flat)](http://nodejs.org/download/) 11 | [![NPM Version](https://badge.fury.io/js/homebridge-automation-switches.svg?style=flat)](https://npmjs.org/package/homebridge-automation-switches) 12 | 13 | ## Why do we need this plugin? 14 | 15 | This platform provides software based, optionally persistent, switches to create DIY HomeKit solutions. 16 | Each switch has specific purposes that are illustrated in their respective documents linked below. 17 | 18 | The plugin provides four different types of switches: A basic on/off switch, a lock mechanism, an automation switch with advanced properties and a security system. All of them are configured ahead 19 | of their use through the configuration file and each one of them potentially saves their state to storage 20 | to keep their state even across crashes, reboots and such. 21 | 22 | ## Installation instructions 23 | 24 | After [Homebridge](https://github.com/nfarina/homebridge) has been installed: 25 | 26 | ```sudo npm install -g homebridge-automation-switches``` 27 | 28 | ## Example config.json: 29 | 30 | ```json 31 | { 32 | "bridge": { 33 | ... 34 | }, 35 | "platforms": [ 36 | { 37 | "platform": "AutomationSwitches", 38 | "switches": [ 39 | { 40 | "type": "automation", 41 | "name": "Automation Switch #1", 42 | "period": 1800, 43 | "autoOff": false 44 | }, 45 | { 46 | "type": "security", 47 | "name": "Home alarm" 48 | } 49 | ] 50 | } 51 | ] 52 | } 53 | ``` 54 | 55 | The platform can provide any number of switches that have to be predefined in the homebridge config.json. 56 | 57 | ### Switch types 58 | 59 | Please see the documentation for each type of switch this plugin is able to create: 60 | 61 | - [Automation switch](docs/AutomationSwitch.md) 62 | - [Lock mechanism](docs/LockMechanism.md) 63 | - [Security system](docs/SecuritySystem.md) 64 | - [Switch](docs/Switch.md) 65 | - [Slider](docs/Slider.md) 66 | - [Alarm Clock](docs/AlarmClock.md) 67 | - [Solar Clock](docs/SolarClock.md) 68 | - [Random value](docs/Random.md) 69 | 70 | An advanced configuration example containing all four switch types can be found [here](docs/Configuration.md). 71 | 72 | ### Storage 73 | 74 | Every type of switch is able to store every state change to disk. This is useful if homebridge is restarted for whatever reason: The switches created by this plugin will retain the state they had before the restart. 75 | 76 | For that the switches create individual files in the persist subfolder of your homebridge configuration folder. 77 | 78 | ## Developer Information 79 | 80 | There's [documentation](docs/CustomCharacteristics.md) of the custom services and characteristics exposed by the switches. 81 | 82 | ## Supported clients 83 | 84 | This platform and the switches it creates have been verified to work with the following apps on iOS 11: 85 | 86 | * Home 87 | * Elgato Eve 88 | 89 | ## Credits 90 | 91 | See [CONTRIBUTORS](CONTRIBUTORS.md) for acknowledgements to the individuals that contributed to this plugin. 92 | 93 | ## Some asks for friendly gestures 94 | 95 | If you use this and like it - please leave a note by staring this package here or on GitHub. 96 | 97 | If you use it and have a problem, file an issue at [GitHub](https://github.com/grover/homebridge-automation-switches/issues) - I'll try to help. 98 | 99 | If you tried this, but don't like it: tell me about it in an issue too. I'll try my best 100 | to address these in my spare time. 101 | 102 | If you fork this, go ahead - I'll accept pull requests for enhancements. 103 | 104 | ## License 105 | 106 | MIT License 107 | 108 | Copyright (c) 2017 Michael Fröhlich 109 | 110 | Permission is hereby granted, free of charge, to any person obtaining a copy 111 | of this software and associated documentation files (the "Software"), to deal 112 | in the Software without restriction, including without limitation the rights 113 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 114 | copies of the Software, and to permit persons to whom the Software is 115 | furnished to do so, subject to the following conditions: 116 | 117 | The above copyright notice and this permission notice shall be included in all 118 | copies or substantial portions of the Software. 119 | 120 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 121 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 122 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 123 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 124 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 125 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 126 | SOFTWARE. 127 | -------------------------------------------------------------------------------- /docs/AlarmClock.md: -------------------------------------------------------------------------------- 1 | # Alarm clock 2 | 3 | This provides a programmable timer for HomeKit. Unlike the HomeKit built in timers, this one can be reprogrammed by 4 | rules. Another reason that I prefer this to the HomeKit builtin timers is that the rules that watch the Alarm state 5 | can actually mention conditions and aren't necessarily blindly executed everytime the schedule expires. 6 | 7 | ## Appearance 8 | 9 | The alarm clock provides an on/off toggle switch, a contact sensor to indicate the alarm and the hour/minute provided as 10 | sliders to control the schedule on a 24h clock. 11 | 12 | ![Preview](AlarmClock.png "Preview") 13 | 14 | (Screenshot: Elgato Eve) 15 | 16 | ## Configuration 17 | 18 | ```json 19 | { 20 | "bridge": { 21 | ... 22 | }, 23 | "platforms": [ 24 | { 25 | "platform": "AutomationSwitches", 26 | "switches": [ 27 | { 28 | "type": "alarmclock", 29 | "name": "Alarm clock", 30 | "stored": true, 31 | "hour": 9, 32 | "minute": 0, 33 | "enabled": true 34 | } 35 | ] 36 | } 37 | ] 38 | } 39 | ``` 40 | 41 | The above example creates an alarm clock, which by default closes the contact sensor every day at 09:00 (in the morning.) 42 | 43 | ## Options 44 | 45 | | Field | Required | Description | 46 | |---|---|---| 47 | | type | Yes | Set this to ```alarmclock``` to make this entry an alarm clock. | 48 | | name | Yes | Set this to the name of the alarm clock as you want it to appear in HomeKit apps. | 49 | | stored | No | Set this to true if you want the switch to retain its settings across restarts. The default setting for the ```alarmclock``` type is ```false```. | 50 | | hour | Yes | The hour to trigger the alarm by default at. A value between 0 and 23. | 51 | | minute | Yes | The minute to trigger the alarm by default at. A value between 0 and 59. | 52 | | enabled | Yes | The default enabled state of the alarm. | 53 | 54 | The defaults are only used if the stored state is unavailable. 55 | 56 | ## Beware of clocks, timezones and drift 57 | 58 | The alarm clock depends upon proper timezone configuration for the alarm to fire at the right time. The sliders configure the 59 | time in your local time. Upon startup lines similar to the following are written to the log: 60 | 61 | ```text 62 | [2018-2-20 11:50:17] [AutomationSwitches] Timezone is Europe/Berlin 63 | [2018-2-20 11:50:17] [AutomationSwitches] Local time is 2018-2-20 11:50:17 64 | [2018-2-20 11:50:17] [AutomationSwitches] UTC time is Tue, 20 Feb 2018 10:50:17 GMT 65 | ``` 66 | 67 | The local time line and the timezone line are important to ensure that the alarms fire off at the right time. Verify that your 68 | timezone is set up correctly and the local time logged matches the actual local wall clock time. 69 | 70 | * [Configure the timezone on Linux](https://unix.stackexchange.com/questions/110522/timezone-setting-in-linux) 71 | 72 | > Node.js and this plugin depends upon the TZ environment variable being set properly. 73 | 74 | Additionally it is recommended to ensure that NTP is working properly and that your device syncs with an NTP server 75 | frequently enough to compensate for [clock drift](https://en.wikipedia.org/wiki/Clock_drift). Ensure that either [systemd timesyncd](https://wiki.archlinux.org/index.php/systemd-timesyncd) 76 | is running or [setup an NTP client](http://raspberrypi.tomasgreno.cz/ntp-client-and-server.html) if you're on Linux. 77 | 78 | ## Usage 79 | 80 | The contact sensor of the above alarm clock is closed every day at 09:00. Attach a HomeKit rule to the contact sensor to 81 | execute any actions as you wish and apply conditions to the rule if needed. 82 | 83 | ## A real alarm clock example 84 | 85 | One could create a real alarm clock in combination with the automation switches: 86 | 87 | ```json 88 | { 89 | "bridge": { 90 | ... 91 | }, 92 | "platforms": [ 93 | { 94 | "platform": "AutomationSwitches", 95 | "switches": [ 96 | { 97 | "type": "alarmclock", 98 | "name": "Alarm clock", 99 | "stored": true, 100 | "hour": 9, 101 | "minute": 0, 102 | "enabled": true 103 | }, 104 | { 105 | "type": "automation", 106 | "name": "Snooze", 107 | "period": 300, 108 | "autoOff": false 109 | } 110 | ] 111 | }, 112 | { 113 | "platform": "DACP", 114 | "devices": [ 115 | { 116 | "name": "iTunes", 117 | "playlists": [ 118 | "Wake up" 119 | ] 120 | } 121 | ] 122 | } 123 | ] 124 | } 125 | ``` 126 | 127 | The above configuration creates an alarm clock that expires at 09:00 everyday. And adds iTunes to the mix via the [homebridge-dacp](https://npmjs.org/packages/homebridge-dacp) with a Wakeup playlist. 128 | 129 | The following rule causes the playlist to be started every day at 09:00 and also starts the snooze timer. 130 | 131 | ```text 132 | If Alarm clock contact is closed 133 | Enable Snooze 134 | Enable wake up playlist 135 | ``` 136 | 137 | And the snooze timer rule causes the playlist to start over again after the snooze period of 5 minutes has expired: 138 | 139 | ```text 140 | If Snooze is detected 141 | Enable wake up playlist 142 | ``` 143 | -------------------------------------------------------------------------------- /docs/AlarmClock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/AlarmClock.png -------------------------------------------------------------------------------- /docs/AutomationSwitch.md: -------------------------------------------------------------------------------- 1 | # Automation Switch 2 | 3 | HomeKit (as of iOS 11.1) does not provide a capability to delay the execution of rules. This plugin provides an automation switch that can be used to build a set of rules that are delayed in execution by a configurable period of time on each switch. The period as well as the repetetiveness and the response can be configured via HomeKit (while the configuration provides sane defaults) and can also be changed in response to rules. 4 | 5 | This switch optionally supports being saved to storage on every state change, the switch will save the settings and restore them when homebridge is restarted. 6 | 7 | > Please note that while the plugin works with the Home app, some features, such as dynamic 8 | > reconfiguration can only be used with other HomeKit apps, such as [Elgato Eve](https://www.elgato.com/en/eve/eve-app). 9 | 10 | ## Appearance 11 | 12 | Each automation switch provides a regular switch, a motion sensor and a configuration service. Activating the switch starts a timer, which will trigger the motion sensor for 1s when the timer elapses. This can be used as a trigger for HomeKit rules. 13 | 14 | The switch support two modes: An automatic shut off mode, where the motion sensor will only be tripped once and the switch is automatically shut off. The other mode is a repeating mode, where the motion sensor will be tripped repeatedly until the switch is shut off again. 15 | 16 | ![Preview](AutomationSwitch.png "Preview") 17 | 18 | (Screenshot: Elgato Eve) 19 | 20 | ## Configuration 21 | 22 | ```json 23 | { 24 | "bridge": { 25 | ... 26 | }, 27 | "platforms": [ 28 | { 29 | "platform": "AutomationSwitches", 30 | "switches": [ 31 | { 32 | "type": "automation", 33 | "name": "Automation Switch #1", 34 | "period": 1800, 35 | "autoOff": false 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | ``` 42 | 43 | ## Options 44 | 45 | | Attributes | Required | Usage | 46 | |------------|----------|-------| 47 | | type | Yes | Set this to ```automation``` to make this entry an automation switch. | 48 | | name | Yes | A unique name for the automation switch. Will be used as the accessory name. | 49 | | period | Yes | The default time delay of the switch in seconds, before the sensor is triggered. | 50 | | autoOff | Yes | Set this to ```true``` to automatically turn the switch off after the period has expired once, ```false``` to keep the triggering the sensor until it has expired. | 51 | | default | No | Specifies the default state of the switch. This is used if the switch is not yet stored, not stored or the storage has become faulty. The default state is ```false``` if not specified, which means the switch is off. Setting this to ```true``` turns the switch on by default. | 52 | | stored | No | Set this to true if you want the switch to retain its on/off state across restarts. The default setting for the ```switch``` type is ```false```. | 53 | 54 | See [configuration](Configuration.md) for more advanced configuration examples. 55 | 56 | ## Usage 57 | 58 | This type of switch is best used if you need to delay the execution of a scene or if you need to execute the scene multiple times until something else has happened. 59 | 60 | I personally have used this to send me notifications until I close the bathroom window, reminding me every 10 minutes that the window is open and I might freeze to the toilet seat. For that I combine this switch type with my [homebridge-telegram](https://www.npmjs.com/packages/homebridge-telegram) plugin. (Shameless plug.) 61 | 62 | ## Notes 63 | 64 | ### Stored switch states 65 | 66 | This switch has logic, which is tied to the state of the switch (e.g. 'On' or 'Off'.) The initial state of the switch is determined either by the "default" field in the configuration or by the value saved just before homebridge terminated the last time. 67 | 68 | If the initial value is `true` the timer controlled by the switch will immediately start as soon as the plugin is initialized. This can also serve to delay initialization just after homebridge has been loaded. 69 | 70 | ### Editing the timeout period 71 | 72 | If the automation switch was stored the default period from the configuration file is also not used any longer. To edit the timeout in those cases, either edit the file that belongs to the switch (located in your homebridge configuration directory under the persist folder) or delete it and then edit the default again. 73 | -------------------------------------------------------------------------------- /docs/AutomationSwitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/AutomationSwitch.png -------------------------------------------------------------------------------- /docs/Configuration.md: -------------------------------------------------------------------------------- 1 | # Advanced configuration 2 | 3 | The following configuration example shows all switches in combination. The configuration was used to make the screenshots in the detailed documentation pages. The configuration shows examples of the 4 | [automation switch](AutomationSwitch.md), the [lock mechanism](LockMechanism.md), the [security system](SecuritySystem.md) and the regular [switch](Switch.md). 5 | 6 | ## Example 7 | 8 | ```json 9 | { 10 | "bridge": { 11 | ... 12 | }, 13 | "platforms": [ 14 | { 15 | "platform": "AutomationSwitches", 16 | "switches": [ 17 | { 18 | "type": "automation", 19 | "name": "WindowOpenAlert", 20 | "period": 600, 21 | "autoOff": false, 22 | "stored": false 23 | }, 24 | { 25 | "type": "automation", 26 | "name": "AutoOff", 27 | "period": 900, 28 | "autoOff": true, 29 | "stored": false 30 | }, 31 | { 32 | "type": "lock", 33 | "name": "ScreenLock", 34 | "default": "unlocked", 35 | "stored": true 36 | }, 37 | { 38 | "type": "security", 39 | "name": "Home Mode", 40 | "default": "armed-stay", 41 | "stored": true 42 | }, 43 | { 44 | "type": "security", 45 | "name": "My DIY security system", 46 | "default": "unarmed", 47 | "stored": true, 48 | "zones": ["Living room", "Bedroom", "Back door"] 49 | } 50 | ] 51 | } 52 | ] 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/CustomCharacteristics.md: -------------------------------------------------------------------------------- 1 | # Custom services and characteristics 2 | 3 | Most of the switch types provided by this plugin use standard HomeKit characteristics. In some cases there was no way (or it made no sense) to bend the standard services/characteristics to enable the use cases supported by this plugin. 4 | 5 | All custom services and characteristics used are listed below. 6 | 7 | ## Custom characteristics 8 | 9 | All custom characteristics are defined in [HomeKitTypes.js](../src/HomeKitTypes.js). 10 | 11 | ### Custom automation switch characteristics 12 | 13 | The automation switch provides a programming service, which allows dynamic changes to the time period between triggers and also control of the automatic shut off. These characteristics can be used in scenes to change the default behavior of the switch. 14 | 15 | | Characteristic | UUID | Service | Permissions | Usage | 16 | |---|---|---|---| 17 | | Period | `B469181F-D796-46B4-8D99-5FBE4BA9DC9C` | READ, WRITE | `FD92B7CF-A343-4D7E-9467-FD251E22C374` | The period of the switch in seconds. This value can be changed between 1s and 3600s. A change will only take effect the next time the switch is turned on. | 18 | | AutomaticOff | `72227266-CA42-4442-AB84-0A7D55A0F08D` | `FD92B7CF-A343-4D7E-9467-FD251E22C374` | READ, WRITE | Determines if the switch is shut off after the period has elapsed. If the switch is not automatically shut off, the timer will be restarted and the motion sensor will be triggered again until the switch is shut off externally. | 19 | 20 | Both of these characteristics are provided on the custom SwitchProgramService. 21 | 22 | ### Custom security system characteristics 23 | 24 | The security system provides an additional characteristic to trigger the Alarm state: 25 | 26 | | Characteristic | UUID | Service | Properties | Description | 27 | | Alarm | `72227266-CA42-4442-AB84-0A7D55A0F08D` | `0000008E-0000-1000-8000-0026BB765291` | READ, WRITE, EVENTS | This characteristic enables the triggering of an alarm. Can also be used for additional rules. | 28 | 29 | This characteristic is provided as an extension on the standard security system service. 30 | 31 | ## Services 32 | 33 | Each switch created by this accessory will provide the standard HomeKit services: 34 | 35 | - AccessoryInformation service 36 | - BridgingState service 37 | 38 | ### Automation switch 39 | 40 | The automation switch additionally provides: 41 | 42 | - Switch service 43 | - MotionSensor service 44 | - SwitchProgram service (see above) 45 | 46 | ### Lock mechanism 47 | 48 | The lock mechanism service provides: 49 | 50 | - LockMechanism service 51 | 52 | ### Security system 53 | 54 | The security system switch provides: 55 | 56 | - SecuritySystem service 57 | 58 | ### Switch 59 | 60 | - Switch Service 61 | -------------------------------------------------------------------------------- /docs/LockMechanism.md: -------------------------------------------------------------------------------- 1 | # Lock mechanism 2 | 3 | This switch type represents a lock. A lock can be locked or unlocked and changing the state of the lock triggers built-in HomeKit notifications. Use it for things that can best be simulated by a lock. 4 | 5 | ## Appearance 6 | 7 | The lock mechanism only provides means to lock/unlock via specifically labelled buttons. 8 | 9 | ![Preview](LockMechanism.png "Preview") 10 | 11 | (Screenshot: Elgato Eve) 12 | 13 | ## Configuration 14 | 15 | ```json 16 | { 17 | "bridge": { 18 | ... 19 | }, 20 | "platforms": [ 21 | { 22 | "platform": "AutomationSwitches", 23 | "switches": [ 24 | { 25 | "type": "lock", 26 | "name": "My simulated door lock", 27 | "default": "unlocked", 28 | "stored": false 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | ``` 35 | 36 | ## Options 37 | 38 | | Field | Required | Description | 39 | |---|---|---| 40 | | type | Yes | Set this to ```lock``` to make this entry a lock mechanism. | 41 | | name | Yes | Set this to the name of the lock as you want it to appear in HomeKit apps. | 42 | | default | No | This configures the default state of the lock if it is not yet stored, never stored or the storage has become faulty. Set this to ```unlocked``` or ```locked``` depending on your needs. By default a lock is ```unlocked``` if this is not specified. | 43 | | stored | No | Set this to true if you want the lock to retain its locked/unlocked state across restarts. The default setting for the ```lock``` type is ```false```. | 44 | 45 | See [configuration](Configuration.md) for more advanced configuration examples. 46 | 47 | ## Usage 48 | 49 | This type is best used to simulate a physical lock or things that are best described by a lock. An example use case for the lock is in conditions for HomeKit rules and thus use it to enable or disable the rules based on other conditions. 50 | 51 | ## HomeKit Notifications 52 | 53 | HomeKit, by default, enables notifications for lock mechanisms. Once enabled you automatically get built-in notifications for this lock too. You can disable these notifications in the Home.app if you do not care for the notifications. To disable/enable the notifications, open the Home app, select the tile that represents the lock and long-press on it, choose Details and scroll down until you get to Notifications. You can disable them there. 54 | 55 | There's unfortunately no way to change the notification text in HomeKit. If you're looking for something to send customized notifications I'd recommend one of the [IFTT plugins](https://www.npmjs.com/search?q=homebridge+ifttt) or my [homebridge-telegram](https://www.npmjs.com/packages/homebridge-telegram) plugin (shameless plug.) 56 | -------------------------------------------------------------------------------- /docs/LockMechanism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/LockMechanism.png -------------------------------------------------------------------------------- /docs/Random.md: -------------------------------------------------------------------------------- 1 | 2 | # Random 3 | 4 | This provides a basic on/off style switch that will select a random number between 1 and the configured maximum value. It is design to help you select a random property by using automation (for example, select a random scene every time the switch is turned on). 5 | 6 | ## Appearance 7 | 8 | You will get a switch and a `Value` property. 9 | 10 | ![Preview](Random.png "Preview") 11 | 12 | (Screenshot: Elgato Eve) 13 | 14 | ## Configuration 15 | 16 | ```json 17 | { 18 | "platforms": [ 19 | { 20 | "platform":"AutomationSwitches", 21 | "switches": [ 22 | { 23 | "type": "random", 24 | "name": "Random", 25 | "max": 15 26 | } 27 | ] 28 | } 29 | ] 30 | } 31 | ``` 32 | 33 | ## Options 34 | 35 | | Field | Required | Description | 36 | |---|---|---| 37 | | type | Yes | Set this to ```random``` to make this entry a random selector. | 38 | | name | Yes | Set this to the name of the switch as you want it to appear in HomeKit apps. | 39 | | max | Yes | The maximum number to be selected randomly | 40 | 41 | ## Usage 42 | 43 | This switch is really useful to trigger random scene. Create an automation that watches for changes in the switch state. When the state changes (e.g. turned on/off), use the `Value` field as **condition** for your automation. Turn on the switch and the automation matching the `Value` will activate. 44 | 45 | ![Preview](RandomExample.png "Example") -------------------------------------------------------------------------------- /docs/Random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/Random.png -------------------------------------------------------------------------------- /docs/RandomExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/RandomExample.png -------------------------------------------------------------------------------- /docs/SecuritySystem.md: -------------------------------------------------------------------------------- 1 | # Security System 2 | 3 | The security system switches enable an easier creation of home alarm systems using already available accessories. Security system switches have default HomeKit notifications associated with them. 4 | 5 | ## Appearance 6 | 7 | The security system provides labelled buttons for the various armed/unarmed states as well 8 | as a button to trigger an alarm. 9 | 10 | ![Preview](SecuritySystem.png "Preview") 11 | 12 | (Screenshot: Elgato Eve) 13 | 14 | ## Configuration 15 | 16 | ```json 17 | { 18 | "bridge": { 19 | ... 20 | }, 21 | "platforms": [ 22 | { 23 | "platform": "AutomationSwitches", 24 | "switches": [ 25 | { 26 | "type": "security", 27 | "name": "My DIY security system", 28 | "default": "unarmed", 29 | "stored": false, 30 | "zones": [ 31 | "Living room", 32 | "Bedroom", 33 | "Back door" 34 | ], 35 | "armAwayButtonLabel": "Arm Away", 36 | "armNightButtonLabel": "Arm Night", 37 | "armStayButtonLabel": "Arm Stay" 38 | } 39 | ] 40 | } 41 | ] 42 | } 43 | ``` 44 | 45 | ## Options 46 | 47 | | Field | Required | Description | 48 | |---|---|---| 49 | | type | Yes | Set this to ```security``` to make this entry a security system. | 50 | | name | Yes | Set this to the name of the security system as you want it to appear in HomeKit apps. | 51 | | default | No | This configures the default state of the security system if it is not yet stored, never stored or the storage has become faulty. Set this to ```unarmed```, or ```armed-away```, ```armed-stay``` or ```armed-night``` depending on your needs. By default a security system is ```unarmed``` if this is not specified. | 52 | | stored | No | The state of security systems are by default stored. Set this to ```false``` if you do not want the security system to retain its armed/unarmed and alarm state across restarts. | 53 | | zones | No | A list of "alarm zones". Each zone will have it's own switch to trigger the alarm. The support for zones will help you detect what has caused the alarm and what to do about it. | 54 | | armAwayButtonLabel | No | The label of the button to Arm the alarm in the "Away" mode (Default "[Name] Arm Away") | 55 | | armNightButtonLabel | No | The label of the button to Arm the alarm in the "Night" mode (Default "[Name] Arm Night") | 56 | | armStayButtonLabel | No | The label of the button to Arm the alarm in the "Home" mode (Default "[Name]Arm Stay") | 57 | 58 | See [configuration](Configuration.md) for more advanced configuration examples. 59 | 60 | ## Usage 61 | 62 | This is best used if you want to build your own home security system. Another use of this is to set up rules and conditions for smoke, leak or gas detectors and have different responses on the state of your home. 63 | 64 | An additional use I have for this switch is to integrate with heating solutions and have centralized control over lighting, heating and security based on the mode of your home. 65 | 66 | ## HomeKit Notifications 67 | 68 | HomeKit, by default, enables notifications for security system mechanisms. Once enabled you automatically get built-in notifications for this security system switch too. You can disable these notifications in the Home.app if you do not care for the notifications. To disable/enable the notifications, open the Home app, select the tile that represents the lock and long-press on it, choose Details and scroll down until you get to Notifications. You can disable them there. 69 | 70 | There's unfortunately no way to change the notification text in HomeKit. If you're looking for something to send customized notifications I'd recommend one of the [IFTT plugins](https://www.npmjs.com/search?q=homebridge+ifttt) or my [homebridge-telegram](https://www.npmjs.com/packages/homebridge-telegram) plugin (shameless plug.) 71 | 72 | ## Arm the alarm when leaving home / Disarm when arriving home. 73 | 74 | Starting from iOS 10, location based automations [cannot change the status of security devices in your home](https://forums.macrumors.com/threads/homekit-automation.2006433/). This behavior was apparently added to prevent automation being accidentally executed due to inaccurate GPS location. 75 | 76 | Linking a location based automation to Arm/Disarm a Security System will make iOS asks the user to manually confirm before running the automation. 77 | 78 | This plugin exposes an ``Arm Away``, ``Arm Night`` and ``Arm Stay`` switches (named for example ``My DIY security system Arm Away``) to work around this limitation. You can now link your location-based automation with this switch to arm/disarm the alarm when leaving/arriving home. 79 | 80 | Please note: the location of your phone might be inaccurate; use these switches at your own risk. 81 | 82 | ## Typical rules for the security system 83 | 84 | The following is an example rule for the security system, showing the versatility: 85 | 86 | ```text 87 | When motion detected 88 | And Security System State is Night Arm 89 | Then Set Alarm to ON 90 | And Turn on all lights 91 | ``` 92 | 93 | In essence you can create rules for each of the security system states and respond differently if the alarm happens during the night, when you're at home or away by building appropriate rules for it. 94 | 95 | 96 | ## Example with multiple zones 97 | 98 | The following is an example rule for the security system, using multiple alarm zones: 99 | 100 | ```text 101 | When motion detected in the Living room 102 | And Security System State is Night Arm 103 | Then Set Living Room Zone to ON 104 | ``` 105 | 106 | ```text 107 | When motion detected in the Bedroom 108 | And Security System State is Night Arm 109 | Then Set Bedroom Zone to ON 110 | ``` 111 | 112 | ## Example configuration 113 | 114 | An example configuration of for the security system, which is ready to run, can be seen [in the examples folder](../examples/securitysystem/config.json). 115 | -------------------------------------------------------------------------------- /docs/SecuritySystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/SecuritySystem.png -------------------------------------------------------------------------------- /docs/Slider.md: -------------------------------------------------------------------------------- 1 | # Switch 2 | 3 | This provides a basic slider without any of the features provided by other switches. It is designed to be this simple on purpose. 4 | 5 | ## Appearance 6 | 7 | A slider can accept a value between a minimum and a maximum bound. The default value, minimum and maximum bounds are configurable. 8 | 9 | ![Preview](Slider.png "Preview") 10 | 11 | (Screenshot: Elgato Eve) 12 | 13 | ## Configuration 14 | 15 | ```json 16 | { 17 | "bridge": { 18 | ... 19 | }, 20 | "platforms": [ 21 | { 22 | "platform": "AutomationSwitches", 23 | "switches": [ 24 | { 25 | "type": "slider", 26 | "name": "My basic slider", 27 | "stored": true, 28 | "default": 5, 29 | "minValue": 1, 30 | "maxValue": 10 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | ``` 37 | 38 | ## Options 39 | 40 | | Field | Required | Description | 41 | |---|---|---| 42 | | type | Yes | Set this to ```slider``` to make this entry a slider. | 43 | | name | Yes | Set this to the name of the slider as you want it to appear in HomeKit apps. | 44 | | default | No | Specifies the default value of the slider. This is used if the slider is not yet stored, not stored or the storage has become faulty. The default value is ```0``` if not specified. | 45 | | stored | No | Set this to true if you want the slider to retain its value across restarts. The default setting for the ```slider``` type is ```false```. | 46 | | minValue | Yes | Sets the minimum boundary of the slider. | 47 | | maxValue | Yes | Sets the maximum boundary of the slider. | 48 | | minStep | No | The minimum step between values of the slider. The default is 1. | 49 | 50 | See [configuration](Configuration.md) for more advanced configuration examples. 51 | 52 | ## Restrictions 53 | 54 | The slider is not useable in the official Apple Home app as it makes use of a custom service and characteristic. 55 | 56 | ## Examples 57 | 58 | ### Slider with steps 59 | 60 | ```json 61 | { 62 | "bridge": { 63 | ... 64 | }, 65 | "platforms": [ 66 | { 67 | "platform": "AutomationSwitches", 68 | "switches": [ 69 | { 70 | "type": "slider", 71 | "name": "My basic slider", 72 | "stored": true, 73 | "default": 5, 74 | "minValue": 0, 75 | "maxValue": 10, 76 | "minStep": 2 77 | } 78 | ] 79 | } 80 | ] 81 | } 82 | ``` 83 | 84 | ![Preview](SliderWithSteps.png "Preview of Slider with steps") 85 | -------------------------------------------------------------------------------- /docs/Slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/Slider.png -------------------------------------------------------------------------------- /docs/SliderWithSteps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/SliderWithSteps.png -------------------------------------------------------------------------------- /docs/SolarClock.md: -------------------------------------------------------------------------------- 1 | # Solar clock 2 | 3 | This provides a programmable timer for HomeKit, 4 | based on the solar period of a location. 5 | It is based on the [Alarm Clock](AlarmClock.md) timer. 6 | 7 | ## Appearance 8 | 9 | The solar clock provides an on/off toggle switch, and a contact sensor to indicate the event. 10 | 11 | ![Preview](SolarClock.png "Preview") 12 | 13 | (Screenshot: Elgato Eve) 14 | 15 | ## Configuration 16 | 17 | ```json 18 | { 19 | "bridge": { 20 | ... 21 | }, 22 | "platforms": [ 23 | { 24 | "platform": "AutomationSwitches", 25 | "switches": [ 26 | { 27 | "type": "solarclock", 28 | "name": "HomeKit Solar Clock", 29 | "location": { "latitude": 39.833333, "longitude": -98.585522 }, 30 | "period": "sunrise", 31 | "offset": -10, 32 | "enabled": true 33 | } 34 | ] 35 | } 36 | ] 37 | } 38 | ``` 39 | 40 | The above example creates an solar clock, 41 | which by default closes the contact sensor every day at ten minutes before sunrise for the geographic center of the 42 | United States. 43 | 44 | ## Options 45 | 46 | | Field | Required | Description | 47 | |---|---|---| 48 | | type | Yes | Set this to ```solarclock``` to make this entry a solar clock. | 49 | | name | Yes | Set this to the name of the solar clock as you want it to appear in HomeKit apps. | 50 | | stored | No | Set this to true if you want the switch to retain its settings across restarts. The default is ```false```. | 51 | | location | Yes |Set this to the location for making solar calculations. | 52 | | period | Yes | Set this to the solar period to trigger the solar clock. One of:```"night"```, ```"morning twilight"```, ```"sunrise"```, ```"daytime"```, ```"sunset"```, or ```"evening twilight"```. | 53 | | offset | No | Set this to the offset in minutes from the start of the period. The default is ```0```. 54 | | enabled | Yes | The default enabled state of the solar clock. | 55 | 56 | The defaults are only used if the stored state is unavailable. 57 | 58 | ## Beware of clocks, timezones and drift 59 | 60 | The solar clock depends on proper geographic configuration for the desired solar period. 61 | 62 | In addition, 63 | the same time-related considerations for the [Alarm Clock](AlarmClock.md) timer apply. 64 | 65 | ## Usage 66 | 67 | The contact sensor of the above solar clock is closed every day at ten minutes before sunrise. 68 | Attach a HomeKit rule to the contact sensor to execute any actions as you wish and apply conditions to the rule if needed. 69 | -------------------------------------------------------------------------------- /docs/SolarClock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/SolarClock.png -------------------------------------------------------------------------------- /docs/Switch.md: -------------------------------------------------------------------------------- 1 | # Switch 2 | 3 | This provides a basic on/off style switch without any of the features provided by other switches. It is designed to be this simple on purpose. If you're looking for advanced features you should take a look at 4 | the [automation switch](AutomationSwitch.md). 5 | 6 | ## Appearance 7 | 8 | A switch is a switch, what did you expect? 9 | 10 | ![Preview](Switch.png "Preview") 11 | 12 | (Screenshot: Elgato Eve) 13 | 14 | ## Configuration 15 | 16 | ```json 17 | { 18 | "bridge": { 19 | ... 20 | }, 21 | "platforms": [ 22 | { 23 | "platform": "AutomationSwitches", 24 | "switches": [ 25 | { 26 | "type": "switch", 27 | "name": "My basic switch", 28 | "stored": true, 29 | "default": false 30 | } 31 | ] 32 | } 33 | ] 34 | } 35 | ``` 36 | 37 | ## Options 38 | 39 | | Field | Required | Description | 40 | |---|---|---| 41 | | type | Yes | Set this to ```switch``` to make this entry a basic switch. | 42 | | name | Yes | Set this to the name of the switch as you want it to appear in HomeKit apps. | 43 | | default | No | Specifies the default state of the switch. This is used if the switch is not yet stored, not stored or the storage has become faulty. The default state is ```false``` if not specified, which means the switch is off. Setting this to ```true``` turns the switch on by default. | 44 | | stored | No | Set this to true if you want the switch to retain its on/off state across restarts. The default setting for the ```switch``` type is ```false```. | 45 | 46 | See [configuration](Configuration.md) for more advanced configuration examples. 47 | 48 | ## Usage 49 | 50 | This switch can be used like any real power switch. You can set rules based on the state of the switch and notifications will be sent for each change of the switch state. 51 | -------------------------------------------------------------------------------- /docs/Switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grover/homebridge-automation-switches/e0af41c19d04c332519f6129c88ca4c213cc81dd/docs/Switch.png -------------------------------------------------------------------------------- /examples/alarmclock/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Alarm-Clock-Bridge", 4 | "username": "BA:32:25:A3:CE:31", 5 | "port": 53718, 6 | "pin": "135-79-863" 7 | }, 8 | "platforms": [ 9 | { 10 | "platform": "AutomationSwitches", 11 | "switches": [ 12 | { 13 | "type": "alarmclock", 14 | "name": "HomeKit Alarm Clock", 15 | "stored": true, 16 | "hour": 9, 17 | "minute": 0, 18 | "enabled": true 19 | } 20 | ] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /examples/securitysystem/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Security-System", 4 | "username": "AD:32:25:A3:CE:31", 5 | "port": 53718, 6 | "pin": "135-79-863" 7 | }, 8 | "platforms": [ 9 | { 10 | "platform": "AutomationSwitches", 11 | "switches": [ 12 | { 13 | "type": "security", 14 | "name": "My DIY security system", 15 | "stored": true, 16 | "default": "unarmed", 17 | "zones": [ 18 | "Living room", 19 | "Bedroom", 20 | "Back door" 21 | ], 22 | "armAwayButtonLabel": "Arm (Away)", 23 | "armStayButtonLabel": "Arm (Stay)" 24 | } 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /examples/slider/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Slider", 4 | "username": "AD:31:25:A3:CE:31", 5 | "port": 53718, 6 | "pin": "135-79-863" 7 | }, 8 | "platforms": [ 9 | { 10 | "platform": "AutomationSwitches", 11 | "switches": [ 12 | { 13 | "type": "slider", 14 | "name": "Example Slider", 15 | "stored": true, 16 | "default": 5, 17 | "minValue": 1, 18 | "maxValue": 10 19 | }, 20 | { 21 | "type": "slider", 22 | "name": "Example Slider with step 2", 23 | "stored": true, 24 | "default": 4, 25 | "minValue": 0, 26 | "maxValue": 10, 27 | "minStep": 2 28 | } 29 | ] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /examples/switch/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Switch", 4 | "username": "CA:31:25:A3:CE:31", 5 | "port": 53718, 6 | "pin": "135-79-864" 7 | }, 8 | "platforms": [ 9 | { 10 | "platform": "AutomationSwitches", 11 | "switches": [ 12 | { 13 | "type": "switch", 14 | "name": "Example Switch", 15 | "stored": true, 16 | "default": false 17 | } 18 | ] 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-automation-switches", 3 | "version": "3.2.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "acorn": { 8 | "version": "5.7.3", 9 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", 10 | "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", 11 | "dev": true 12 | }, 13 | "acorn-jsx": { 14 | "version": "3.0.1", 15 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", 16 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", 17 | "dev": true, 18 | "requires": { 19 | "acorn": "^3.0.4" 20 | }, 21 | "dependencies": { 22 | "acorn": { 23 | "version": "3.3.0", 24 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 25 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", 26 | "dev": true 27 | } 28 | } 29 | }, 30 | "ajv": { 31 | "version": "5.5.2", 32 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 33 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 34 | "dev": true, 35 | "requires": { 36 | "co": "^4.6.0", 37 | "fast-deep-equal": "^1.0.0", 38 | "fast-json-stable-stringify": "^2.0.0", 39 | "json-schema-traverse": "^0.3.0" 40 | } 41 | }, 42 | "ajv-keywords": { 43 | "version": "2.1.1", 44 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", 45 | "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", 46 | "dev": true 47 | }, 48 | "ansi-escapes": { 49 | "version": "3.2.0", 50 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", 51 | "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", 52 | "dev": true 53 | }, 54 | "ansi-regex": { 55 | "version": "2.1.1", 56 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 57 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 58 | "dev": true 59 | }, 60 | "ansi-styles": { 61 | "version": "2.2.1", 62 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 63 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 64 | "dev": true 65 | }, 66 | "argparse": { 67 | "version": "1.0.10", 68 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 69 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 70 | "dev": true, 71 | "requires": { 72 | "sprintf-js": "~1.0.2" 73 | } 74 | }, 75 | "babel-code-frame": { 76 | "version": "6.26.0", 77 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 78 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 79 | "dev": true, 80 | "requires": { 81 | "chalk": "^1.1.3", 82 | "esutils": "^2.0.2", 83 | "js-tokens": "^3.0.2" 84 | }, 85 | "dependencies": { 86 | "chalk": { 87 | "version": "1.1.3", 88 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 89 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 90 | "dev": true, 91 | "requires": { 92 | "ansi-styles": "^2.2.1", 93 | "escape-string-regexp": "^1.0.2", 94 | "has-ansi": "^2.0.0", 95 | "strip-ansi": "^3.0.0", 96 | "supports-color": "^2.0.0" 97 | } 98 | }, 99 | "strip-ansi": { 100 | "version": "3.0.1", 101 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 102 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 103 | "dev": true, 104 | "requires": { 105 | "ansi-regex": "^2.0.0" 106 | } 107 | } 108 | } 109 | }, 110 | "balanced-match": { 111 | "version": "1.0.0", 112 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 113 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 114 | "dev": true 115 | }, 116 | "brace-expansion": { 117 | "version": "1.1.11", 118 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 119 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 120 | "dev": true, 121 | "requires": { 122 | "balanced-match": "^1.0.0", 123 | "concat-map": "0.0.1" 124 | } 125 | }, 126 | "buffer-from": { 127 | "version": "1.1.1", 128 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 129 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 130 | "dev": true 131 | }, 132 | "caller-path": { 133 | "version": "0.1.0", 134 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 135 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 136 | "dev": true, 137 | "requires": { 138 | "callsites": "^0.2.0" 139 | } 140 | }, 141 | "callsites": { 142 | "version": "0.2.0", 143 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 144 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", 145 | "dev": true 146 | }, 147 | "chalk": { 148 | "version": "2.4.2", 149 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 150 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 151 | "dev": true, 152 | "requires": { 153 | "ansi-styles": "^3.2.1", 154 | "escape-string-regexp": "^1.0.5", 155 | "supports-color": "^5.3.0" 156 | }, 157 | "dependencies": { 158 | "ansi-styles": { 159 | "version": "3.2.1", 160 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 161 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 162 | "dev": true, 163 | "requires": { 164 | "color-convert": "^1.9.0" 165 | } 166 | }, 167 | "supports-color": { 168 | "version": "5.5.0", 169 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 170 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 171 | "dev": true, 172 | "requires": { 173 | "has-flag": "^3.0.0" 174 | } 175 | } 176 | } 177 | }, 178 | "chardet": { 179 | "version": "0.4.2", 180 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", 181 | "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", 182 | "dev": true 183 | }, 184 | "circular-json": { 185 | "version": "0.3.3", 186 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", 187 | "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", 188 | "dev": true 189 | }, 190 | "cli-cursor": { 191 | "version": "2.1.0", 192 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", 193 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", 194 | "dev": true, 195 | "requires": { 196 | "restore-cursor": "^2.0.0" 197 | } 198 | }, 199 | "cli-width": { 200 | "version": "2.2.0", 201 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 202 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", 203 | "dev": true 204 | }, 205 | "clone": { 206 | "version": "2.1.1", 207 | "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", 208 | "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=" 209 | }, 210 | "co": { 211 | "version": "4.6.0", 212 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 213 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 214 | "dev": true 215 | }, 216 | "color-convert": { 217 | "version": "1.9.3", 218 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 219 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 220 | "dev": true, 221 | "requires": { 222 | "color-name": "1.1.3" 223 | } 224 | }, 225 | "color-name": { 226 | "version": "1.1.3", 227 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 228 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 229 | "dev": true 230 | }, 231 | "concat-map": { 232 | "version": "0.0.1", 233 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 234 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 235 | "dev": true 236 | }, 237 | "concat-stream": { 238 | "version": "1.6.2", 239 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 240 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 241 | "dev": true, 242 | "requires": { 243 | "buffer-from": "^1.0.0", 244 | "inherits": "^2.0.3", 245 | "readable-stream": "^2.2.2", 246 | "typedarray": "^0.0.6" 247 | } 248 | }, 249 | "core-util-is": { 250 | "version": "1.0.2", 251 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 252 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 253 | "dev": true 254 | }, 255 | "cross-spawn": { 256 | "version": "5.1.0", 257 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 258 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 259 | "dev": true, 260 | "requires": { 261 | "lru-cache": "^4.0.1", 262 | "shebang-command": "^1.2.0", 263 | "which": "^1.2.9" 264 | } 265 | }, 266 | "debug": { 267 | "version": "3.2.6", 268 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 269 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 270 | "dev": true, 271 | "requires": { 272 | "ms": "^2.1.1" 273 | } 274 | }, 275 | "deep-is": { 276 | "version": "0.1.3", 277 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 278 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 279 | "dev": true 280 | }, 281 | "doctrine": { 282 | "version": "2.1.0", 283 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 284 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 285 | "dev": true, 286 | "requires": { 287 | "esutils": "^2.0.2" 288 | } 289 | }, 290 | "escape-string-regexp": { 291 | "version": "1.0.5", 292 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 293 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 294 | "dev": true 295 | }, 296 | "eslint": { 297 | "version": "4.19.1", 298 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", 299 | "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", 300 | "dev": true, 301 | "requires": { 302 | "ajv": "^5.3.0", 303 | "babel-code-frame": "^6.22.0", 304 | "chalk": "^2.1.0", 305 | "concat-stream": "^1.6.0", 306 | "cross-spawn": "^5.1.0", 307 | "debug": "^3.1.0", 308 | "doctrine": "^2.1.0", 309 | "eslint-scope": "^3.7.1", 310 | "eslint-visitor-keys": "^1.0.0", 311 | "espree": "^3.5.4", 312 | "esquery": "^1.0.0", 313 | "esutils": "^2.0.2", 314 | "file-entry-cache": "^2.0.0", 315 | "functional-red-black-tree": "^1.0.1", 316 | "glob": "^7.1.2", 317 | "globals": "^11.0.1", 318 | "ignore": "^3.3.3", 319 | "imurmurhash": "^0.1.4", 320 | "inquirer": "^3.0.6", 321 | "is-resolvable": "^1.0.0", 322 | "js-yaml": "^3.9.1", 323 | "json-stable-stringify-without-jsonify": "^1.0.1", 324 | "levn": "^0.3.0", 325 | "lodash": "^4.17.4", 326 | "minimatch": "^3.0.2", 327 | "mkdirp": "^0.5.1", 328 | "natural-compare": "^1.4.0", 329 | "optionator": "^0.8.2", 330 | "path-is-inside": "^1.0.2", 331 | "pluralize": "^7.0.0", 332 | "progress": "^2.0.0", 333 | "regexpp": "^1.0.1", 334 | "require-uncached": "^1.0.3", 335 | "semver": "^5.3.0", 336 | "strip-ansi": "^4.0.0", 337 | "strip-json-comments": "~2.0.1", 338 | "table": "4.0.2", 339 | "text-table": "~0.2.0" 340 | } 341 | }, 342 | "eslint-scope": { 343 | "version": "3.7.3", 344 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", 345 | "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", 346 | "dev": true, 347 | "requires": { 348 | "esrecurse": "^4.1.0", 349 | "estraverse": "^4.1.1" 350 | } 351 | }, 352 | "eslint-visitor-keys": { 353 | "version": "1.0.0", 354 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", 355 | "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", 356 | "dev": true 357 | }, 358 | "espree": { 359 | "version": "3.5.4", 360 | "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", 361 | "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", 362 | "dev": true, 363 | "requires": { 364 | "acorn": "^5.5.0", 365 | "acorn-jsx": "^3.0.0" 366 | } 367 | }, 368 | "esprima": { 369 | "version": "4.0.1", 370 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 371 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 372 | "dev": true 373 | }, 374 | "esquery": { 375 | "version": "1.0.1", 376 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", 377 | "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", 378 | "dev": true, 379 | "requires": { 380 | "estraverse": "^4.0.0" 381 | } 382 | }, 383 | "esrecurse": { 384 | "version": "4.2.1", 385 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 386 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 387 | "dev": true, 388 | "requires": { 389 | "estraverse": "^4.1.0" 390 | } 391 | }, 392 | "estraverse": { 393 | "version": "4.2.0", 394 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 395 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 396 | "dev": true 397 | }, 398 | "esutils": { 399 | "version": "2.0.2", 400 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 401 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 402 | "dev": true 403 | }, 404 | "external-editor": { 405 | "version": "2.2.0", 406 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", 407 | "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", 408 | "dev": true, 409 | "requires": { 410 | "chardet": "^0.4.0", 411 | "iconv-lite": "^0.4.17", 412 | "tmp": "^0.0.33" 413 | } 414 | }, 415 | "fast-deep-equal": { 416 | "version": "1.1.0", 417 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 418 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", 419 | "dev": true 420 | }, 421 | "fast-json-stable-stringify": { 422 | "version": "2.0.0", 423 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 424 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 425 | "dev": true 426 | }, 427 | "fast-levenshtein": { 428 | "version": "2.0.6", 429 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 430 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 431 | "dev": true 432 | }, 433 | "figures": { 434 | "version": "2.0.0", 435 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", 436 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", 437 | "dev": true, 438 | "requires": { 439 | "escape-string-regexp": "^1.0.5" 440 | } 441 | }, 442 | "file-entry-cache": { 443 | "version": "2.0.0", 444 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", 445 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", 446 | "dev": true, 447 | "requires": { 448 | "flat-cache": "^1.2.1", 449 | "object-assign": "^4.0.1" 450 | } 451 | }, 452 | "flat-cache": { 453 | "version": "1.3.4", 454 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", 455 | "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", 456 | "dev": true, 457 | "requires": { 458 | "circular-json": "^0.3.1", 459 | "graceful-fs": "^4.1.2", 460 | "rimraf": "~2.6.2", 461 | "write": "^0.2.1" 462 | } 463 | }, 464 | "fs.realpath": { 465 | "version": "1.0.0", 466 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 467 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 468 | "dev": true 469 | }, 470 | "functional-red-black-tree": { 471 | "version": "1.0.1", 472 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 473 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 474 | "dev": true 475 | }, 476 | "glob": { 477 | "version": "7.1.4", 478 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 479 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 480 | "dev": true, 481 | "requires": { 482 | "fs.realpath": "^1.0.0", 483 | "inflight": "^1.0.4", 484 | "inherits": "2", 485 | "minimatch": "^3.0.4", 486 | "once": "^1.3.0", 487 | "path-is-absolute": "^1.0.0" 488 | } 489 | }, 490 | "globals": { 491 | "version": "11.12.0", 492 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 493 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 494 | "dev": true 495 | }, 496 | "graceful-fs": { 497 | "version": "4.2.0", 498 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", 499 | "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", 500 | "dev": true 501 | }, 502 | "has-ansi": { 503 | "version": "2.0.0", 504 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 505 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 506 | "dev": true, 507 | "requires": { 508 | "ansi-regex": "^2.0.0" 509 | } 510 | }, 511 | "has-flag": { 512 | "version": "3.0.0", 513 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 514 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 515 | "dev": true 516 | }, 517 | "iconv-lite": { 518 | "version": "0.4.24", 519 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 520 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 521 | "dev": true, 522 | "requires": { 523 | "safer-buffer": ">= 2.1.2 < 3" 524 | } 525 | }, 526 | "ignore": { 527 | "version": "3.3.10", 528 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", 529 | "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", 530 | "dev": true 531 | }, 532 | "imurmurhash": { 533 | "version": "0.1.4", 534 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 535 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 536 | "dev": true 537 | }, 538 | "inflight": { 539 | "version": "1.0.6", 540 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 541 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 542 | "dev": true, 543 | "requires": { 544 | "once": "^1.3.0", 545 | "wrappy": "1" 546 | } 547 | }, 548 | "inherits": { 549 | "version": "2.0.4", 550 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 551 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 552 | "dev": true 553 | }, 554 | "inquirer": { 555 | "version": "3.3.0", 556 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", 557 | "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", 558 | "dev": true, 559 | "requires": { 560 | "ansi-escapes": "^3.0.0", 561 | "chalk": "^2.0.0", 562 | "cli-cursor": "^2.1.0", 563 | "cli-width": "^2.0.0", 564 | "external-editor": "^2.0.4", 565 | "figures": "^2.0.0", 566 | "lodash": "^4.3.0", 567 | "mute-stream": "0.0.7", 568 | "run-async": "^2.2.0", 569 | "rx-lite": "^4.0.8", 570 | "rx-lite-aggregates": "^4.0.8", 571 | "string-width": "^2.1.0", 572 | "strip-ansi": "^4.0.0", 573 | "through": "^2.3.6" 574 | } 575 | }, 576 | "is-fullwidth-code-point": { 577 | "version": "2.0.0", 578 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 579 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 580 | "dev": true 581 | }, 582 | "is-promise": { 583 | "version": "2.1.0", 584 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 585 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", 586 | "dev": true 587 | }, 588 | "is-resolvable": { 589 | "version": "1.1.0", 590 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", 591 | "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", 592 | "dev": true 593 | }, 594 | "isarray": { 595 | "version": "1.0.0", 596 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 597 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 598 | "dev": true 599 | }, 600 | "isexe": { 601 | "version": "2.0.0", 602 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 603 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 604 | "dev": true 605 | }, 606 | "js-tokens": { 607 | "version": "3.0.2", 608 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 609 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 610 | "dev": true 611 | }, 612 | "js-yaml": { 613 | "version": "3.13.1", 614 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 615 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 616 | "dev": true, 617 | "requires": { 618 | "argparse": "^1.0.7", 619 | "esprima": "^4.0.0" 620 | } 621 | }, 622 | "json-schema-traverse": { 623 | "version": "0.3.1", 624 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 625 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", 626 | "dev": true 627 | }, 628 | "json-stable-stringify-without-jsonify": { 629 | "version": "1.0.1", 630 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 631 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 632 | "dev": true 633 | }, 634 | "levn": { 635 | "version": "0.3.0", 636 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 637 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 638 | "dev": true, 639 | "requires": { 640 | "prelude-ls": "~1.1.2", 641 | "type-check": "~0.3.2" 642 | } 643 | }, 644 | "lodash": { 645 | "version": "4.17.15", 646 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 647 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 648 | "dev": true 649 | }, 650 | "lru-cache": { 651 | "version": "4.1.5", 652 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 653 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 654 | "dev": true, 655 | "requires": { 656 | "pseudomap": "^1.0.2", 657 | "yallist": "^2.1.2" 658 | } 659 | }, 660 | "mimic-fn": { 661 | "version": "1.2.0", 662 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", 663 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", 664 | "dev": true 665 | }, 666 | "minimatch": { 667 | "version": "3.0.4", 668 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 669 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 670 | "dev": true, 671 | "requires": { 672 | "brace-expansion": "^1.1.7" 673 | } 674 | }, 675 | "minimist": { 676 | "version": "0.0.8", 677 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 678 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 679 | }, 680 | "mkdirp": { 681 | "version": "0.5.1", 682 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 683 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 684 | "requires": { 685 | "minimist": "0.0.8" 686 | } 687 | }, 688 | "ms": { 689 | "version": "2.1.2", 690 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 691 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 692 | "dev": true 693 | }, 694 | "mute-stream": { 695 | "version": "0.0.7", 696 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", 697 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", 698 | "dev": true 699 | }, 700 | "natural-compare": { 701 | "version": "1.4.0", 702 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 703 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 704 | "dev": true 705 | }, 706 | "node-persist": { 707 | "version": "0.0.11", 708 | "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-0.0.11.tgz", 709 | "integrity": "sha1-1m66Pr72IPB5Uw+nsTB2qQZmWHQ=", 710 | "requires": { 711 | "mkdirp": "~0.5.1", 712 | "q": "~1.1.1" 713 | } 714 | }, 715 | "object-assign": { 716 | "version": "4.1.1", 717 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 718 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 719 | "dev": true 720 | }, 721 | "once": { 722 | "version": "1.4.0", 723 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 724 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 725 | "dev": true, 726 | "requires": { 727 | "wrappy": "1" 728 | } 729 | }, 730 | "onetime": { 731 | "version": "2.0.1", 732 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", 733 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", 734 | "dev": true, 735 | "requires": { 736 | "mimic-fn": "^1.0.0" 737 | } 738 | }, 739 | "optionator": { 740 | "version": "0.8.2", 741 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 742 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 743 | "dev": true, 744 | "requires": { 745 | "deep-is": "~0.1.3", 746 | "fast-levenshtein": "~2.0.4", 747 | "levn": "~0.3.0", 748 | "prelude-ls": "~1.1.2", 749 | "type-check": "~0.3.2", 750 | "wordwrap": "~1.0.0" 751 | } 752 | }, 753 | "os-tmpdir": { 754 | "version": "1.0.2", 755 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 756 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 757 | "dev": true 758 | }, 759 | "path-is-absolute": { 760 | "version": "1.0.1", 761 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 762 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 763 | "dev": true 764 | }, 765 | "path-is-inside": { 766 | "version": "1.0.2", 767 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 768 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 769 | "dev": true 770 | }, 771 | "pluralize": { 772 | "version": "7.0.0", 773 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", 774 | "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", 775 | "dev": true 776 | }, 777 | "prelude-ls": { 778 | "version": "1.1.2", 779 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 780 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 781 | "dev": true 782 | }, 783 | "process-nextick-args": { 784 | "version": "2.0.1", 785 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 786 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 787 | "dev": true 788 | }, 789 | "progress": { 790 | "version": "2.0.3", 791 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 792 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 793 | "dev": true 794 | }, 795 | "pseudomap": { 796 | "version": "1.0.2", 797 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 798 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 799 | "dev": true 800 | }, 801 | "q": { 802 | "version": "1.1.2", 803 | "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz", 804 | "integrity": "sha1-Y1fikSBnAdmfGXq4TlforRlvKok=" 805 | }, 806 | "readable-stream": { 807 | "version": "2.3.6", 808 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 809 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 810 | "dev": true, 811 | "requires": { 812 | "core-util-is": "~1.0.0", 813 | "inherits": "~2.0.3", 814 | "isarray": "~1.0.0", 815 | "process-nextick-args": "~2.0.0", 816 | "safe-buffer": "~5.1.1", 817 | "string_decoder": "~1.1.1", 818 | "util-deprecate": "~1.0.1" 819 | } 820 | }, 821 | "regexpp": { 822 | "version": "1.1.0", 823 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", 824 | "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", 825 | "dev": true 826 | }, 827 | "require-uncached": { 828 | "version": "1.0.3", 829 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 830 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 831 | "dev": true, 832 | "requires": { 833 | "caller-path": "^0.1.0", 834 | "resolve-from": "^1.0.0" 835 | } 836 | }, 837 | "resolve-from": { 838 | "version": "1.0.1", 839 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 840 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", 841 | "dev": true 842 | }, 843 | "restore-cursor": { 844 | "version": "2.0.0", 845 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", 846 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", 847 | "dev": true, 848 | "requires": { 849 | "onetime": "^2.0.0", 850 | "signal-exit": "^3.0.2" 851 | } 852 | }, 853 | "rimraf": { 854 | "version": "2.6.3", 855 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 856 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 857 | "dev": true, 858 | "requires": { 859 | "glob": "^7.1.3" 860 | } 861 | }, 862 | "run-async": { 863 | "version": "2.3.0", 864 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 865 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 866 | "dev": true, 867 | "requires": { 868 | "is-promise": "^2.1.0" 869 | } 870 | }, 871 | "rx-lite": { 872 | "version": "4.0.8", 873 | "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", 874 | "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", 875 | "dev": true 876 | }, 877 | "rx-lite-aggregates": { 878 | "version": "4.0.8", 879 | "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", 880 | "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", 881 | "dev": true, 882 | "requires": { 883 | "rx-lite": "*" 884 | } 885 | }, 886 | "safe-buffer": { 887 | "version": "5.1.2", 888 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 889 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 890 | "dev": true 891 | }, 892 | "safer-buffer": { 893 | "version": "2.1.2", 894 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 895 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 896 | "dev": true 897 | }, 898 | "semver": { 899 | "version": "5.7.0", 900 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 901 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", 902 | "dev": true 903 | }, 904 | "shebang-command": { 905 | "version": "1.2.0", 906 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 907 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 908 | "dev": true, 909 | "requires": { 910 | "shebang-regex": "^1.0.0" 911 | } 912 | }, 913 | "shebang-regex": { 914 | "version": "1.0.0", 915 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 916 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 917 | "dev": true 918 | }, 919 | "signal-exit": { 920 | "version": "3.0.2", 921 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 922 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 923 | "dev": true 924 | }, 925 | "slice-ansi": { 926 | "version": "1.0.0", 927 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", 928 | "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", 929 | "dev": true, 930 | "requires": { 931 | "is-fullwidth-code-point": "^2.0.0" 932 | } 933 | }, 934 | "sprintf-js": { 935 | "version": "1.0.3", 936 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 937 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 938 | "dev": true 939 | }, 940 | "string-width": { 941 | "version": "2.1.1", 942 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 943 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 944 | "dev": true, 945 | "requires": { 946 | "is-fullwidth-code-point": "^2.0.0", 947 | "strip-ansi": "^4.0.0" 948 | } 949 | }, 950 | "string_decoder": { 951 | "version": "1.1.1", 952 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 953 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 954 | "dev": true, 955 | "requires": { 956 | "safe-buffer": "~5.1.0" 957 | } 958 | }, 959 | "strip-ansi": { 960 | "version": "4.0.0", 961 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 962 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 963 | "dev": true, 964 | "requires": { 965 | "ansi-regex": "^3.0.0" 966 | }, 967 | "dependencies": { 968 | "ansi-regex": { 969 | "version": "3.0.0", 970 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 971 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 972 | "dev": true 973 | } 974 | } 975 | }, 976 | "strip-json-comments": { 977 | "version": "2.0.1", 978 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 979 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 980 | "dev": true 981 | }, 982 | "suncalc": { 983 | "version": "1.8.0", 984 | "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz", 985 | "integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U=" 986 | }, 987 | "supports-color": { 988 | "version": "2.0.0", 989 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 990 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 991 | "dev": true 992 | }, 993 | "table": { 994 | "version": "4.0.2", 995 | "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", 996 | "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", 997 | "dev": true, 998 | "requires": { 999 | "ajv": "^5.2.3", 1000 | "ajv-keywords": "^2.1.0", 1001 | "chalk": "^2.1.0", 1002 | "lodash": "^4.17.4", 1003 | "slice-ansi": "1.0.0", 1004 | "string-width": "^2.1.1" 1005 | } 1006 | }, 1007 | "text-table": { 1008 | "version": "0.2.0", 1009 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1010 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1011 | "dev": true 1012 | }, 1013 | "through": { 1014 | "version": "2.3.8", 1015 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1016 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1017 | "dev": true 1018 | }, 1019 | "tmp": { 1020 | "version": "0.0.33", 1021 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 1022 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 1023 | "dev": true, 1024 | "requires": { 1025 | "os-tmpdir": "~1.0.2" 1026 | } 1027 | }, 1028 | "type-check": { 1029 | "version": "0.3.2", 1030 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1031 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1032 | "dev": true, 1033 | "requires": { 1034 | "prelude-ls": "~1.1.2" 1035 | } 1036 | }, 1037 | "typedarray": { 1038 | "version": "0.0.6", 1039 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1040 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1041 | "dev": true 1042 | }, 1043 | "util-deprecate": { 1044 | "version": "1.0.2", 1045 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1046 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1047 | "dev": true 1048 | }, 1049 | "which": { 1050 | "version": "1.3.1", 1051 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1052 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1053 | "dev": true, 1054 | "requires": { 1055 | "isexe": "^2.0.0" 1056 | } 1057 | }, 1058 | "wordwrap": { 1059 | "version": "1.0.0", 1060 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1061 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1062 | "dev": true 1063 | }, 1064 | "wrappy": { 1065 | "version": "1.0.2", 1066 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1067 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1068 | "dev": true 1069 | }, 1070 | "write": { 1071 | "version": "0.2.1", 1072 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1073 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1074 | "dev": true, 1075 | "requires": { 1076 | "mkdirp": "^0.5.1" 1077 | } 1078 | }, 1079 | "yallist": { 1080 | "version": "2.1.2", 1081 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1082 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 1083 | "dev": true 1084 | } 1085 | } 1086 | } 1087 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-automation-switches", 3 | "author": { 4 | "name": "Michael Froehlich" 5 | }, 6 | "version": "3.2.1", 7 | "description": "A platform for automation switches for HomeKit via Homebridge", 8 | "license": "MIT", 9 | "main": "src/index.js", 10 | "keywords": [ 11 | "homebridge-plugin", 12 | "automation", 13 | "periodic", 14 | "switch", 15 | "switches" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/grover/homebridge-automation-switches.git" 20 | }, 21 | "bugs": { 22 | "url": "http://github.com/grover/homebridge-automation-switches/issues" 23 | }, 24 | "engines": { 25 | "node": ">=9.3.0", 26 | "homebridge": ">=0.4.36" 27 | }, 28 | "dependencies": { 29 | "clone": "^2.1.1", 30 | "node-persist": "^0.0.11", 31 | "suncalc": "^1.8.0" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^4.19.1" 35 | }, 36 | "scripts": { 37 | "lint": "./node_modules/.bin/eslint src", 38 | "release": "npm-github-release" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/AlarmClockAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const clone = require('clone'); 4 | 5 | let Accessory, Characteristic, Service; 6 | 7 | class AlarmClockAccessory { 8 | 9 | constructor(api, log, config, storage) { 10 | Accessory = api.hap.Accessory; 11 | Characteristic = api.hap.Characteristic; 12 | Service = api.hap.Service; 13 | 14 | this.log = log; 15 | this.name = config.name; 16 | this._config = config; 17 | 18 | this._alarmValue = Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; 19 | this._noAlarmValue = Characteristic.ContactSensorState.CONTACT_DETECTED; 20 | 21 | this.log(`Timezone is ${process.env.TZ}`); 22 | this.log(`Local time is ${new Date().toLocaleString()}`); 23 | this.log(`UTC time is ${new Date().toUTCString()}`); 24 | 25 | this._storage = storage; 26 | 27 | const defaultValue = { 28 | hour: config.hour || 12, 29 | minute: config.minute || 0, 30 | enabled: config.enabled === undefined ? false : config.enabled 31 | }; 32 | 33 | storage.retrieve(defaultValue, (error, value) => { 34 | this._state = value; 35 | 36 | if (this._state.enabled) { 37 | this._scheduleAlarmClock(); 38 | } 39 | }); 40 | 41 | this._services = this.createServices(); 42 | } 43 | 44 | getServices() { 45 | return this._services; 46 | } 47 | 48 | createServices() { 49 | return [ 50 | this.getAccessoryInformationService(), 51 | this.getBridgingStateService(), 52 | this.getClockService(), 53 | this.getEnabledSwitchService(), 54 | this.getContactSensorService() 55 | ]; 56 | } 57 | 58 | getAccessoryInformationService() { 59 | return new Service.AccessoryInformation() 60 | .setCharacteristic(Characteristic.Name, this.name) 61 | .setCharacteristic(Characteristic.Manufacturer, 'Michael Froehlich') 62 | .setCharacteristic(Characteristic.Model, 'Switch') 63 | .setCharacteristic(Characteristic.SerialNumber, this._config.serialNumber) 64 | .setCharacteristic(Characteristic.FirmwareRevision, this._config.version) 65 | .setCharacteristic(Characteristic.HardwareRevision, this._config.version); 66 | } 67 | 68 | getBridgingStateService() { 69 | return new Service.BridgingState() 70 | .setCharacteristic(Characteristic.Reachable, true) 71 | .setCharacteristic(Characteristic.LinkQuality, 4) 72 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 73 | .setCharacteristic(Characteristic.Category, Accessory.Categories.SWITCH); 74 | } 75 | 76 | getClockService() { 77 | this._clockService = new Service.Clock(`${this.name} Time`); 78 | this._clockService.getCharacteristic(Characteristic.ClockHour) 79 | .on('set', this._setHour.bind(this)) 80 | .updateValue(this._state.hour); 81 | this._clockService.getCharacteristic(Characteristic.ClockMinute) 82 | .on('set', this._setMinute.bind(this)) 83 | .updateValue(this._state.minute); 84 | 85 | return this._clockService; 86 | } 87 | 88 | getEnabledSwitchService() { 89 | this._switchService = new Service.Switch(`${this.name} Enabled`); 90 | this._switchService.getCharacteristic(Characteristic.On) 91 | .on('set', this._setEnabledState.bind(this)) 92 | .updateValue(this._state.enabled); 93 | 94 | this._switchService.isPrimaryService = true; 95 | 96 | return this._switchService; 97 | } 98 | 99 | getContactSensorService() { 100 | this._contactSensor = new Service.ContactSensor(`${this.name} Alarm`); 101 | this._contactSensor.getCharacteristic(Characteristic.ContactSensorState) 102 | .updateValue(this._noAlarmValue); 103 | 104 | return this._contactSensor; 105 | } 106 | 107 | identify(callback) { 108 | this.log(`Identify requested on ${this.name}`); 109 | callback(); 110 | } 111 | 112 | _setHour(value, callback) { 113 | this.log(`Change target hour of ${this.name} to ${value}`); 114 | 115 | const data = clone(this._state); 116 | data.hour = value; 117 | 118 | this._persist(data, callback); 119 | } 120 | 121 | _setMinute(value, callback) { 122 | this.log(`Change target minute of ${this.name} to ${value}`); 123 | 124 | const data = clone(this._state); 125 | data.minute = value; 126 | 127 | this._persist(data, callback); 128 | } 129 | 130 | _setEnabledState(value, callback) { 131 | this.log(`Change enabled state of ${this.name} to ${value}`); 132 | 133 | const data = clone(this._state); 134 | data.enabled = value; 135 | 136 | this._persist(data, callback); 137 | } 138 | 139 | _persist(data, callback) { 140 | this._storage.store(data, (error) => { 141 | if (error) { 142 | callback(error); 143 | return; 144 | } 145 | 146 | this._state = data; 147 | callback(); 148 | 149 | this._restartAlarmTimer(); 150 | }); 151 | } 152 | 153 | _restartAlarmTimer() { 154 | if (this._timer) { 155 | clearTimeout(this._timer); 156 | } 157 | 158 | if (this._state.enabled) { 159 | this._scheduleAlarmClock(); 160 | } 161 | } 162 | 163 | _scheduleAlarmClock() { 164 | // Figure out if the time is still today or the next day and set a timer 165 | const now = new Date(); 166 | const alarm = new Date(); 167 | 168 | alarm.setHours(this._state.hour, this._state.minute, 0); 169 | if (now > alarm) { 170 | // Alarm is tomorrow - add a day 171 | alarm.setDate(alarm.getDate() + 1); 172 | } 173 | 174 | this.log(`Raising next alarm at ${alarm.toLocaleString()}`); 175 | 176 | const diff = alarm.valueOf() - now.valueOf(); 177 | // this.log(`Diff=${diff}, now=${now.valueOf()} (${now.toLocaleString()}), alarm=${alarm.valueOf()}`); 178 | this._timer = setTimeout(this._alarm.bind(this), diff); 179 | } 180 | 181 | _alarm() { 182 | this.log('Alarm!'); 183 | this._contactSensor.getCharacteristic(Characteristic.ContactSensorState) 184 | .updateValue(this._alarmValue); 185 | 186 | setTimeout(this._silenceAlarm.bind(this), 1000); 187 | } 188 | 189 | _silenceAlarm() { 190 | this.log('Alarm silenced!'); 191 | this._contactSensor.getCharacteristic(Characteristic.ContactSensorState) 192 | .updateValue(this._noAlarmValue); 193 | this._scheduleAlarmClock(); 194 | } 195 | } 196 | 197 | module.exports = AlarmClockAccessory; 198 | -------------------------------------------------------------------------------- /src/AutomationSwitchAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const clone = require('clone'); 4 | 5 | let Accessory, Characteristic, Service; 6 | 7 | class SwitchAccessory { 8 | 9 | constructor(api, log, config, storage) { 10 | Accessory = api.hap.Accessory; 11 | Characteristic = api.hap.Characteristic; 12 | Service = api.hap.Service; 13 | 14 | this.log = log; 15 | this.name = config.name; 16 | this._config = config; 17 | 18 | this._storage = storage; 19 | 20 | const defaultValue = { 21 | autoOff: config.autoOff, 22 | period: config.period, 23 | state: config.default === undefined ? false : config.default 24 | }; 25 | 26 | storage.retrieve(defaultValue, (error, value) => { 27 | this._state = value; 28 | }); 29 | 30 | 31 | this._services = this.createServices(); 32 | this._timer = undefined; 33 | 34 | if (this._state.state) { 35 | this._startTimer(); 36 | } 37 | } 38 | 39 | getServices() { 40 | return this._services; 41 | } 42 | 43 | createServices() { 44 | return [ 45 | this.getAccessoryInformationService(), 46 | this.getBridgingStateService(), 47 | this.getSwitchService(), 48 | this.getSwitchProgramService(), 49 | this.getMotionSensorService() 50 | ]; 51 | } 52 | 53 | getAccessoryInformationService() { 54 | return new Service.AccessoryInformation() 55 | .setCharacteristic(Characteristic.Name, this.name) 56 | .setCharacteristic(Characteristic.Manufacturer, 'Michael Froehlich') 57 | .setCharacteristic(Characteristic.Model, 'Automation Switch') 58 | .setCharacteristic(Characteristic.SerialNumber, this._config.serialNumber) 59 | .setCharacteristic(Characteristic.FirmwareRevision, this._config.version) 60 | .setCharacteristic(Characteristic.HardwareRevision, this._config.version); 61 | } 62 | 63 | getBridgingStateService() { 64 | return new Service.BridgingState() 65 | .setCharacteristic(Characteristic.Reachable, true) 66 | .setCharacteristic(Characteristic.LinkQuality, 4) 67 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 68 | .setCharacteristic(Characteristic.Category, Accessory.Categories.PROGRAMMABLE_SWITCH); 69 | } 70 | 71 | getSwitchService() { 72 | this._switchService = new Service.Switch(this.name); 73 | this._switchService.getCharacteristic(Characteristic.On) 74 | .on('set', this._setOn.bind(this)) 75 | .updateValue(this._state.state); 76 | 77 | return this._switchService; 78 | } 79 | 80 | getSwitchProgramService() { 81 | const program = new Service.SwitchProgram(this.name); 82 | program.getCharacteristic(Characteristic.PeriodInSeconds) 83 | .on('set', this._setPeriod.bind(this)) 84 | .updateValue(this._state.period); 85 | 86 | program.getCharacteristic(Characteristic.AutomaticOff) 87 | .on('set', this._setAutoOff.bind(this)) 88 | .updateValue(this._state.autoOff); 89 | 90 | return program; 91 | } 92 | 93 | getMotionSensorService() { 94 | this._motionSensor = new Service.MotionSensor(this.name); 95 | return this._motionSensor; 96 | } 97 | 98 | identify(callback) { 99 | this.log(`Identify requested on ${this.name}`); 100 | callback(); 101 | } 102 | 103 | _setOn(on, callback) { 104 | this.log(`Setting switch state to ${on}`); 105 | 106 | this._resetTimer(); 107 | 108 | const data = clone(this._state); 109 | data.state = on; 110 | 111 | this._persist(data, (error) => { 112 | if (error) { 113 | this.log('Storing the state change has failed.'); 114 | callback(error); 115 | return; 116 | } 117 | 118 | if (on) { 119 | this._startTimer(); 120 | } 121 | }); 122 | 123 | callback(); 124 | } 125 | 126 | _setPeriod(value, callback) { 127 | this.log(`Setting period value: d=${value}s`); 128 | 129 | const data = clone(this._state); 130 | data.period = value; 131 | 132 | this._persist(data, callback); 133 | } 134 | 135 | _setAutoOff(value, callback) { 136 | this.log(`Setting auto off value ${value}`); 137 | 138 | const data = clone(this._state); 139 | data.autoOff = value; 140 | 141 | this._persist(data, callback); 142 | } 143 | 144 | _startTimer() { 145 | const delay = this._state.period * 1000; 146 | 147 | this.log(`Starting timer for ${delay}ms`); 148 | this._timer = setTimeout(this._onTimeout.bind(this), delay); 149 | } 150 | 151 | _resetTimer() { 152 | if (this._timer) { 153 | clearTimeout(this._timer); 154 | this._timer = undefined; 155 | } 156 | } 157 | 158 | _onTimeout() { 159 | this._timer = undefined; 160 | this.onTimerExpired(); 161 | } 162 | 163 | onTimerExpired() { 164 | if (this._state.autoOff) { 165 | this.log('Reseting switch'); 166 | 167 | const data = clone(this._state); 168 | data.state = false; 169 | 170 | this._persist(data, (error) => { 171 | if (error) { 172 | this.log('Storing the state change has failed.'); 173 | return; 174 | } 175 | 176 | this._switchService 177 | .getCharacteristic(Characteristic.On) 178 | .updateValue(false, undefined, undefined); 179 | }); 180 | } 181 | 182 | this.signalMotion(true); 183 | setTimeout(this.nextPeriod.bind(this), 1000); 184 | } 185 | 186 | signalMotion(motion) { 187 | this._motionSensor 188 | .getCharacteristic(Characteristic.MotionDetected) 189 | .updateValue(motion, undefined, undefined); 190 | } 191 | 192 | nextPeriod() { 193 | this.signalMotion(false); 194 | if (!this._state.autoOff) { 195 | this._startTimer(); 196 | } 197 | } 198 | 199 | _persist(data, callback) { 200 | this._storage.store(data, (error) => { 201 | if (error) { 202 | callback(error); 203 | return; 204 | } 205 | 206 | this._state = data; 207 | callback(); 208 | }); 209 | } 210 | } 211 | 212 | module.exports = SwitchAccessory; -------------------------------------------------------------------------------- /src/HomeKitTypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var inherits = require('util').inherits; 4 | 5 | module.exports = { 6 | registerWith: function (hap) { 7 | 8 | const Characteristic = hap.Characteristic; 9 | const Service = hap.Service; 10 | 11 | 12 | Characteristic.AutomaticOff = function () { 13 | Characteristic.call(this, 'Automatic Off', '72227266-CA42-4442-AB84-0A7D55A0F08D'); 14 | 15 | this.setProps({ 16 | format: Characteristic.Formats.BOOL, 17 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE], 18 | }); 19 | 20 | this.value = this.getDefaultValue(); 21 | }; 22 | inherits(Characteristic.AutomaticOff, Characteristic); 23 | Characteristic.AutomaticOff.UUID = '72227266-CA42-4442-AB84-0A7D55A0F08D'; 24 | 25 | Characteristic.PeriodInSeconds = function () { 26 | Characteristic.call(this, 'Period', 'B469181F-D796-46B4-8D99-5FBE4BA9DC9C'); 27 | 28 | this.setProps({ 29 | format: Characteristic.Formats.INT, 30 | unit: Characteristic.Units.SECONDS, 31 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE], 32 | minValue: 1, 33 | maxValue: 3600, 34 | }); 35 | 36 | this.value = this.getDefaultValue(); 37 | }; 38 | inherits(Characteristic.PeriodInSeconds, Characteristic); 39 | Characteristic.PeriodInSeconds.UUID = 'B469181F-D796-46B4-8D99-5FBE4BA9DC9C'; 40 | 41 | Service.SwitchProgram = function (displayName, subtype) { 42 | Service.call(this, displayName, 'FD92B7CF-A343-4D7E-9467-FD251E22C374', subtype); 43 | 44 | // Required Characteristics 45 | this.addCharacteristic(Characteristic.PeriodInSeconds); 46 | this.addCharacteristic(Characteristic.AutomaticOff); 47 | }; 48 | inherits(Service.SwitchProgram, Service); 49 | Service.SwitchProgram.UUID = 'FD92B7CF-A343-4D7E-9467-FD251E22C374'; 50 | 51 | /****************************************************** 52 | * Slider switch 53 | */ 54 | 55 | Characteristic.SliderValue = function () { 56 | Characteristic.call(this, 'Value', '38AFD9A5-A0C5-42D9-ACD0-1BE08D4FF3F7'); 57 | 58 | this.setProps({ 59 | format: Characteristic.Formats.INT, 60 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE], 61 | }); 62 | 63 | this.value = this.getDefaultValue(); 64 | }; 65 | inherits(Characteristic.SliderValue, Characteristic); 66 | Characteristic.SliderValue.UUID = '38AFD9A5-A0C5-42D9-ACD0-1BE08D4FF3F7'; 67 | 68 | 69 | Service.Slider = function (displayName, subtype) { 70 | Service.call(this, displayName, 'DDFC25B3-3624-44CA-9477-FDC977FC7C81', subtype); 71 | 72 | // Required Characteristics 73 | this.addCharacteristic(Characteristic.SliderValue); 74 | }; 75 | inherits(Service.Slider, Service); 76 | Service.Slider.UUID = 'DDFC25B3-3624-44CA-9477-FDC977FC7C81'; 77 | } 78 | }; -------------------------------------------------------------------------------- /src/LockMechanismAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const clone = require('clone'); 4 | 5 | let Accessory, Characteristic, Service; 6 | 7 | const LockMechanismStates = [ 8 | 'Unsecured', 9 | 'Secured', 10 | 'Jammed', 11 | 'Unknown', 12 | 'Alarm triggered' 13 | ]; 14 | 15 | class LockMechanismAccessory { 16 | 17 | constructor(api, log, config, storage) { 18 | Accessory = api.hap.Accessory; 19 | Characteristic = api.hap.Characteristic; 20 | Service = api.hap.Service; 21 | 22 | this.log = log; 23 | this.name = config.name; 24 | this._config = config; 25 | 26 | this._storage = storage; 27 | 28 | const defaultValue = { 29 | targetState: this._pickDefault(config.default) 30 | }; 31 | 32 | storage.retrieve(defaultValue, (error, value) => { 33 | this._state = value; 34 | }); 35 | 36 | this._services = this.createServices(); 37 | } 38 | 39 | _pickDefault(value) { 40 | if (value === 'locked') { 41 | return 1; 42 | } 43 | 44 | if (value === 'unlocked' || value === undefined) { 45 | return 0; 46 | } 47 | 48 | throw new Error('Unsupported default value in configuration of lock.'); 49 | } 50 | 51 | getServices() { 52 | return this._services; 53 | } 54 | 55 | createServices() { 56 | return [ 57 | this.getAccessoryInformationService(), 58 | this.getBridgingStateService(), 59 | this.getLockMechanismService() 60 | ]; 61 | } 62 | 63 | getAccessoryInformationService() { 64 | return new Service.AccessoryInformation() 65 | .setCharacteristic(Characteristic.Name, this.name) 66 | .setCharacteristic(Characteristic.Manufacturer, 'Michael Froehlich') 67 | .setCharacteristic(Characteristic.Model, 'Lock Mechanism') 68 | .setCharacteristic(Characteristic.SerialNumber, this._config.serialNumber) 69 | .setCharacteristic(Characteristic.FirmwareRevision, this._config.version) 70 | .setCharacteristic(Characteristic.HardwareRevision, this._config.version); 71 | } 72 | 73 | getBridgingStateService() { 74 | return new Service.BridgingState() 75 | .setCharacteristic(Characteristic.Reachable, true) 76 | .setCharacteristic(Characteristic.LinkQuality, 4) 77 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 78 | .setCharacteristic(Characteristic.Category, Accessory.Categories.DOOR_LOCK); 79 | } 80 | 81 | getLockMechanismService() { 82 | this._lockMechanismService = new Service.LockMechanism(this.name); 83 | this._lockMechanismService.getCharacteristic(Characteristic.LockTargetState) 84 | .on('set', this._setTargetState.bind(this)) 85 | .updateValue(this._state.targetState); 86 | 87 | this._lockMechanismService.getCharacteristic(Characteristic.LockCurrentState) 88 | .updateValue(this._state.targetState); 89 | 90 | this._lockMechanismService.isPrimaryService = true; 91 | 92 | return this._lockMechanismService; 93 | } 94 | 95 | identify(callback) { 96 | this.log(`Identify requested on ${this.name}`); 97 | callback(); 98 | } 99 | 100 | _setTargetState(value, callback) { 101 | this.log(`Change target state of ${this.name} to ${LockMechanismStates[value]}`); 102 | 103 | const data = clone(this._state); 104 | data.targetState = value; 105 | 106 | this._persist(data, (error) => { 107 | if (error) { 108 | callback(error); 109 | return; 110 | } 111 | 112 | this._state = data; 113 | this._updateCurrentState(); 114 | callback(); 115 | }); 116 | } 117 | 118 | _updateCurrentState() { 119 | let currentState = this._state.targetState; 120 | 121 | this._lockMechanismService 122 | .getCharacteristic(Characteristic.LockCurrentState) 123 | .updateValue(currentState); 124 | } 125 | 126 | _persist(data, callback) { 127 | this._storage.store(data, (error) => { 128 | if (error) { 129 | callback(error); 130 | return; 131 | } 132 | 133 | this._state = data; 134 | callback(); 135 | }); 136 | } 137 | } 138 | 139 | module.exports = LockMechanismAccessory; 140 | -------------------------------------------------------------------------------- /src/RandomAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Accessory, Characteristic, Service; 4 | 5 | class RandomAccessory { 6 | 7 | constructor(api, log, config) { 8 | Accessory = api.hap.Accessory; 9 | Characteristic = api.hap.Characteristic; 10 | Service = api.hap.Service; 11 | 12 | this.log = log; 13 | this.name = config.name; 14 | this.config = config; 15 | 16 | this._state = { 17 | randomValue: 0, 18 | }; 19 | 20 | this._maxValue = config.max || 1; 21 | 22 | this._services = this.createServices(); 23 | } 24 | 25 | getServices() { 26 | return this._services; 27 | } 28 | 29 | createServices() { 30 | return [ 31 | this.getAccessoryInformationService(), 32 | this.getBridgingStateService(), 33 | this.getSwitchService(), 34 | ]; 35 | } 36 | 37 | getAccessoryInformationService() { 38 | return new Service.AccessoryInformation() 39 | .setCharacteristic(Characteristic.Name, this.name) 40 | .setCharacteristic(Characteristic.Manufacturer, 'Michael Froehlich') 41 | .setCharacteristic(Characteristic.Model, 'Switch') 42 | .setCharacteristic(Characteristic.SerialNumber, this.config.serialNumber) 43 | .setCharacteristic(Characteristic.FirmwareRevision, this.config.version) 44 | .setCharacteristic(Characteristic.HardwareRevision, this.config.version); 45 | } 46 | 47 | getBridgingStateService() { 48 | return new Service.BridgingState() 49 | .setCharacteristic(Characteristic.Reachable, true) 50 | .setCharacteristic(Characteristic.LinkQuality, 4) 51 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 52 | .setCharacteristic(Characteristic.Category, Accessory.Categories.SWITCH); 53 | } 54 | 55 | getSwitchService() { 56 | this._switchService = new Service.Switch(this.name); 57 | this._switchService.getCharacteristic(Characteristic.On) 58 | .on('set', this._setState.bind(this)); 59 | 60 | this._switchService.addCharacteristic(Characteristic.RandomValue); 61 | 62 | this._switchService.isPrimaryService = true; 63 | 64 | return this._switchService; 65 | } 66 | 67 | identify(callback) { 68 | this.log(`Identify requested on ${this.name}`); 69 | callback(); 70 | } 71 | 72 | _pickRandomValue() { 73 | const minValue = 1; 74 | const maxValue = this._maxValue; 75 | 76 | this._state.randomValue = parseInt(Math.floor(Math.random() * (maxValue - minValue + 1) + minValue)); 77 | this.log(`Picked random value: ${this._state.randomValue}`); 78 | 79 | this._switchService 80 | .getCharacteristic(Characteristic.RandomValue) 81 | .updateValue(this._state.randomValue); 82 | } 83 | 84 | _setState(value, callback) { 85 | this.log(`Change target state of ${this.name} to ${value}`); 86 | 87 | if (value) { 88 | this._pickRandomValue(); 89 | 90 | // Turn off the switch after 1 second 91 | setTimeout(() => { 92 | this._switchService.setCharacteristic(Characteristic.On, false); 93 | }, 1000); 94 | } 95 | 96 | callback(); 97 | } 98 | } 99 | 100 | module.exports = RandomAccessory; 101 | -------------------------------------------------------------------------------- /src/SecuritySystemAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const clone = require('clone'); 4 | 5 | let Accessory, Characteristic, Service; 6 | 7 | const SecuritySystemStates = [ 8 | 'Armed - Stay', 9 | 'Armed - Away', 10 | 'Armed - Night', 11 | 'Disarmed', 12 | 'Alarm triggered' 13 | ]; 14 | 15 | class SecuritySystemAccessory { 16 | 17 | constructor(api, log, config, storage) { 18 | Accessory = api.hap.Accessory; 19 | Characteristic = api.hap.Characteristic; 20 | Service = api.hap.Service; 21 | 22 | this.log = log; 23 | this.name = config.name; 24 | this._config = config; 25 | 26 | this.zones = config.zones || ['Alarm']; 27 | this.armAwayButtonLabel = config.armAwayButtonLabel || `${this.name} Arm Away`; 28 | this.armStayButtonLabel = config.armStayButtonLabel || `${this.name} Arm Stay`; 29 | this.armNightButtonLabel = config.armNightButtonLabel || `${this.name} Arm Night`; 30 | 31 | this._storage = storage; 32 | 33 | const defaultValue = { 34 | targetState: this._pickDefault(config.default), 35 | zonesAlarm: {}, 36 | }; 37 | 38 | for (let zoneLabel of this.zones) { 39 | defaultValue.zonesAlarm[zoneLabel] = false; 40 | } 41 | 42 | storage.retrieve(defaultValue, (error, value) => { 43 | 44 | value.zonesAlarm = value.zonesAlarm || []; 45 | 46 | // Remove zones from storage that are not in the list anymore 47 | for (let zoneLabel in value.zonesAlarm) { 48 | if (this.zones.indexOf(zoneLabel) === -1) { 49 | delete value.zonesAlarm[zoneLabel]; 50 | } 51 | } 52 | 53 | storage.store(value, () => true); 54 | 55 | this._state = value; 56 | }); 57 | 58 | this._services = this.createServices(); 59 | } 60 | 61 | _pickDefault(value) { 62 | switch (value) { 63 | case 'armed-stay': return 0; 64 | case 'armed-away': return 1; 65 | case 'armed-night': return 2; 66 | case 'unarmed': return 3; 67 | case undefined: return 3; 68 | } 69 | 70 | throw new Error('Unsupported default value in configuration of security system.'); 71 | } 72 | 73 | getServices() { 74 | return this._services; 75 | } 76 | 77 | createServices() { 78 | return [ 79 | this.getAccessoryInformationService(), 80 | this.getBridgingStateService(), 81 | this.getSecuritySystemService(), 82 | ...this.getArmSwitchServices(), 83 | ...this.getZoneServices(), 84 | ]; 85 | } 86 | 87 | getArmSwitchServices() { 88 | this._armAwaySwitchService = new Service.Switch(this.armAwayButtonLabel, 'arm-away'); 89 | this._armAwaySwitchService.getCharacteristic(Characteristic.On) 90 | .on('set', (value, callback) => this._setArm(value, callback, Characteristic.SecuritySystemTargetState.AWAY_ARM)) 91 | .updateValue(this._isArmAway()); 92 | 93 | this._armStaySwitchService = new Service.Switch(this.armStayButtonLabel, 'arm-stay'); 94 | this._armStaySwitchService.getCharacteristic(Characteristic.On) 95 | .on('set', (value, callback) => this._setArm(value, callback, Characteristic.SecuritySystemTargetState.STAY_ARM)) 96 | .updateValue(this._isArmStay()); 97 | 98 | this._armNightSwitchService = new Service.Switch(this.armNightButtonLabel, 'arm-night'); 99 | this._armNightSwitchService.getCharacteristic(Characteristic.On) 100 | .on('set', (value, callback) => this._setArm(value, callback, Characteristic.SecuritySystemTargetState.NIGHT_ARM)) 101 | .updateValue(this._isArmNight()); 102 | 103 | return [this._armAwaySwitchService, this._armStaySwitchService, this._armNightSwitchService]; 104 | } 105 | 106 | getAccessoryInformationService() { 107 | return new Service.AccessoryInformation() 108 | .setCharacteristic(Characteristic.Name, this.name) 109 | .setCharacteristic(Characteristic.Manufacturer, 'Michael Froehlich') 110 | .setCharacteristic(Characteristic.Model, 'Security System') 111 | .setCharacteristic(Characteristic.SerialNumber, this._config.serialNumber) 112 | .setCharacteristic(Characteristic.FirmwareRevision, this._config.version) 113 | .setCharacteristic(Characteristic.HardwareRevision, this._config.version); 114 | } 115 | 116 | getBridgingStateService() { 117 | return new Service.BridgingState() 118 | .setCharacteristic(Characteristic.Reachable, true) 119 | .setCharacteristic(Characteristic.LinkQuality, 4) 120 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 121 | .setCharacteristic(Characteristic.Category, Accessory.Categories.SECURITY_SYSTEM); 122 | } 123 | 124 | getSecuritySystemService() { 125 | this._securitySystemService = new Service.SecuritySystem(this.name); 126 | this._securitySystemService.getCharacteristic(Characteristic.SecuritySystemTargetState) 127 | .on('set', this._setState.bind(this)) 128 | .updateValue(this._state.targetState); 129 | 130 | this._securitySystemService.getCharacteristic(Characteristic.SecuritySystemCurrentState) 131 | .updateValue(this._state.targetState); 132 | 133 | this._securitySystemService.isPrimaryService = true; 134 | 135 | return this._securitySystemService; 136 | } 137 | 138 | getZoneServices() { 139 | let services = []; 140 | 141 | for (let zoneLabel of this.zones) { 142 | const zoneSwitch = new Service.Switch(`${this.name} ${zoneLabel} Zone`, `zone-${zoneLabel}`); 143 | 144 | zoneSwitch.getCharacteristic(Characteristic.On) 145 | .on('set', (value, callback) => this._setAlarm(zoneLabel, value, callback)) 146 | .updateValue(this._state.zonesAlarm[zoneLabel] || false); 147 | 148 | services.push(zoneSwitch); 149 | } 150 | 151 | return services; 152 | } 153 | 154 | identify(callback) { 155 | this.log(`Identify requested on ${this.name}`); 156 | callback(); 157 | } 158 | 159 | _setState(value, callback) { 160 | this.log(`Change target state of ${this.name} to ${SecuritySystemStates[value]}`); 161 | 162 | const data = clone(this._state); 163 | data.targetState = value; 164 | this._persist(data, callback); 165 | } 166 | 167 | _setArm(value, callback, armState) { 168 | let targetState; 169 | if (value) { 170 | targetState = armState; 171 | } else { 172 | targetState = Characteristic.SecuritySystemTargetState.DISARM; 173 | } 174 | 175 | this._securitySystemService 176 | .getCharacteristic(Characteristic.SecuritySystemTargetState) 177 | .updateValue(targetState); 178 | 179 | this._setState(targetState, callback); 180 | } 181 | 182 | _setAlarm(zone, value, callback) { 183 | this.log(`Change alarm state of ${this.name} / Zone ${zone} to ${value}`); 184 | 185 | const data = clone(this._state); 186 | data.zonesAlarm[zone] = value; 187 | this._persist(data, callback); 188 | } 189 | 190 | _isAlarm() { 191 | for (let zoneLabel in this._state.zonesAlarm) { 192 | if (this._state.zonesAlarm[zoneLabel]) { 193 | return true; 194 | } 195 | } 196 | 197 | return false; 198 | } 199 | 200 | _isArmAway() { 201 | return this._state.targetState === Characteristic.SecuritySystemCurrentState.AWAY_ARM; 202 | } 203 | 204 | _isArmStay() { 205 | return this._state.targetState === Characteristic.SecuritySystemCurrentState.STAY_ARM; 206 | } 207 | 208 | _isArmNight() { 209 | return this._state.targetState === Characteristic.SecuritySystemCurrentState.NIGHT_ARM; 210 | } 211 | 212 | _persist(data, callback) { 213 | this._storage.store(data, (error) => { 214 | if (error) { 215 | callback(error); 216 | return; 217 | } 218 | 219 | this._state = data; 220 | this._updateCurrentState(); 221 | callback(); 222 | }); 223 | } 224 | 225 | _isDisarmed() { 226 | let currentState = this._state.targetState; 227 | return currentState === Characteristic.SecuritySystemCurrentState.DISARMED; 228 | } 229 | 230 | _updateCurrentState() { 231 | let currentState = this._state.targetState; 232 | if (this._isAlarm() && !this._isDisarmed()) { 233 | currentState = Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED; 234 | } 235 | 236 | this._securitySystemService 237 | .getCharacteristic(Characteristic.SecuritySystemCurrentState) 238 | .updateValue(currentState); 239 | 240 | this._armAwaySwitchService 241 | .getCharacteristic(Characteristic.On) 242 | .updateValue(this._isArmAway()); 243 | 244 | this._armStaySwitchService 245 | .getCharacteristic(Characteristic.On) 246 | .updateValue(this._isArmStay()); 247 | 248 | this._armNightSwitchService 249 | .getCharacteristic(Characteristic.On) 250 | .updateValue(this._isArmNight()); 251 | } 252 | } 253 | 254 | module.exports = SecuritySystemAccessory; -------------------------------------------------------------------------------- /src/SliderAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const clone = require('clone'); 4 | 5 | let Accessory, Characteristic, Service; 6 | 7 | class SliderAccessory { 8 | 9 | constructor(api, log, config, storage) { 10 | Accessory = api.hap.Accessory; 11 | Characteristic = api.hap.Characteristic; 12 | Service = api.hap.Service; 13 | 14 | this.log = log; 15 | this.name = config.name; 16 | this._config = config; 17 | 18 | this._storage = storage; 19 | 20 | const defaultValue = { 21 | value: config.default === undefined ? false : config.default 22 | }; 23 | 24 | storage.retrieve(defaultValue, (error, value) => { 25 | this._state = value; 26 | }); 27 | 28 | this._services = this.createServices(); 29 | } 30 | 31 | getServices() { 32 | return this._services; 33 | } 34 | 35 | createServices() { 36 | return [ 37 | this.getAccessoryInformationService(), 38 | this.getBridgingStateService(), 39 | this.getSliderService() 40 | ]; 41 | } 42 | 43 | getAccessoryInformationService() { 44 | return new Service.AccessoryInformation() 45 | .setCharacteristic(Characteristic.Name, this.name) 46 | .setCharacteristic(Characteristic.Manufacturer, 'Michael Froehlich') 47 | .setCharacteristic(Characteristic.Model, 'Slider') 48 | .setCharacteristic(Characteristic.SerialNumber, this._config.serialNumber) 49 | .setCharacteristic(Characteristic.FirmwareRevision, this._config.version) 50 | .setCharacteristic(Characteristic.HardwareRevision, this._config.version); 51 | } 52 | 53 | getBridgingStateService() { 54 | return new Service.BridgingState() 55 | .setCharacteristic(Characteristic.Reachable, true) 56 | .setCharacteristic(Characteristic.LinkQuality, 4) 57 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 58 | .setCharacteristic(Characteristic.Category, Accessory.Categories.SWITCH); 59 | } 60 | 61 | getSliderService() { 62 | this._sliderService = new Service.Slider(this.name); 63 | 64 | const props = { 65 | minValue: this._config.minValue, 66 | maxValue: this._config.maxValue 67 | }; 68 | 69 | if (this._config.minStep !== undefined) { 70 | props.minStep = this._config.minStep; 71 | } 72 | 73 | this._sliderService.getCharacteristic(Characteristic.SliderValue) 74 | .on('set', this._setValue.bind(this)) 75 | .setProps(props) 76 | .updateValue(this._state.value); 77 | 78 | this._sliderService.isPrimaryService = true; 79 | 80 | return this._sliderService; 81 | } 82 | 83 | identify(callback) { 84 | this.log(`Identify requested on ${this.name}`); 85 | callback(); 86 | } 87 | 88 | _setValue(value, callback) { 89 | this.log(`Change value of ${this.name} to ${value}`); 90 | 91 | const data = clone(this._state); 92 | data.value = value; 93 | 94 | this._persist(data, callback); 95 | } 96 | 97 | _persist(data, callback) { 98 | this._storage.store(data, (error) => { 99 | if (error) { 100 | callback(error); 101 | return; 102 | } 103 | 104 | this._state = data; 105 | callback(); 106 | }); 107 | } 108 | } 109 | 110 | module.exports = SliderAccessory; 111 | -------------------------------------------------------------------------------- /src/SolarClockAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const clone = require('clone'); 4 | const suncalc = require('suncalc'); 5 | 6 | let Accessory, Characteristic, Service; 7 | 8 | class SolarClockAccessory { 9 | 10 | constructor(api, log, config, storage) { 11 | Accessory = api.hap.Accessory; 12 | Characteristic = api.hap.Characteristic; 13 | Service = api.hap.Service; 14 | 15 | this.log = log; 16 | this.name = config.name; 17 | this.version = config.version; 18 | this.location = config.location || [ 0, 0 ]; 19 | if (Array.isArray(this.location)) this.location = { latitude: this.location[0], longitude: this.location[1] }; 20 | 21 | const latitude = parseFloat(this.location.latitude); 22 | const longitude = parseFloat(this.location.longitude); 23 | if (isNaN(latitude) || (Math.abs(latitude) > 90) || isNaN(longitude)|| (Math.abs(longitude) > 180)) { 24 | throw new Error('invalid location: ' + config.location); 25 | } 26 | this.location = { latitude: latitude, longitude: longitude }; 27 | 28 | this._solarValue = Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; 29 | this._noSolarValue = Characteristic.ContactSensorState.CONTACT_DETECTED; 30 | 31 | this._storage = storage; 32 | 33 | const defaultValue = { 34 | period: 0, 35 | offset: config.offset || 0, 36 | enabled: config.enabled === undefined ? false : config.enabled 37 | }; 38 | config.period = config.period.toLowerCase(); 39 | for (let i = Characteristic.SolarPeriod._SolarPeriods.length - 1; i >= 0; i--) { 40 | if (Characteristic.SolarPeriod._SolarPeriods[i].toLowerCase() !== config.period) continue; 41 | 42 | defaultValue.period = i; 43 | break; 44 | } 45 | 46 | storage.retrieve(defaultValue, (error, value) => { 47 | this._state = value; 48 | 49 | if (this._state.enabled) { 50 | this._scheduleSolarClock(); 51 | } 52 | }); 53 | 54 | this._services = this.createServices(); 55 | } 56 | 57 | getServices() { 58 | return this._services; 59 | } 60 | 61 | createServices() { 62 | return [ 63 | this.getAccessoryInformationService(), 64 | this.getBridgingStateService(), 65 | this.getSolarService(), 66 | this.getSolarLocationService(), 67 | this.getEnabledSwitchService(), 68 | this.getContactSensorService() 69 | ]; 70 | } 71 | 72 | getAccessoryInformationService() { 73 | return new Service.AccessoryInformation() 74 | .setCharacteristic(Characteristic.Name, this.name) 75 | .setCharacteristic(Characteristic.Manufacturer, 'The Homespun') 76 | .setCharacteristic(Characteristic.Model, 'Switch') 77 | .setCharacteristic(Characteristic.SerialNumber, '44') 78 | .setCharacteristic(Characteristic.FirmwareRevision, this.version) 79 | .setCharacteristic(Characteristic.HardwareRevision, this.version); 80 | } 81 | 82 | getBridgingStateService() { 83 | return new Service.BridgingState() 84 | .setCharacteristic(Characteristic.Reachable, true) 85 | .setCharacteristic(Characteristic.LinkQuality, 4) 86 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 87 | .setCharacteristic(Characteristic.Category, Accessory.Categories.SWITCH); 88 | } 89 | 90 | getSolarService() { 91 | this._solarService = new Service.Solar(`${this.name} Period`); 92 | this._solarService.getCharacteristic(Characteristic.SolarPeriod) 93 | .on('set', this._setPeriod.bind(this)) 94 | .updateValue(this._state.period); 95 | this._solarService.getCharacteristic(Characteristic.SolarMinutesOffset) 96 | .on('set', this._setOffset.bind(this)) 97 | .updateValue(this._state.offset); 98 | 99 | return this._solarService; 100 | } 101 | 102 | getSolarLocationService() { 103 | this._solarLocationService = new Service.SolarLocation(`${this.name} Location`) 104 | .setCharacteristic(Characteristic.SolarLatitude, this.location.latitude) 105 | .setCharacteristic(Characteristic.SolarLongitude, this.location.longitude); 106 | 107 | return this._solarLocationService; 108 | } 109 | 110 | getEnabledSwitchService() { 111 | this._switchService = new Service.Switch(`${this.name} Enabled`); 112 | this._switchService.getCharacteristic(Characteristic.On) 113 | .on('set', this._setEnabledState.bind(this)) 114 | .updateValue(this._state.enabled); 115 | 116 | this._switchService.isPrimaryService = true; 117 | 118 | return this._switchService; 119 | } 120 | 121 | getContactSensorService() { 122 | this._contactSensor = new Service.ContactSensor(`${this.name} Solar`); 123 | this._contactSensor.getCharacteristic(Characteristic.ContactSensorState) 124 | .updateValue(this._noSolarValue); 125 | 126 | return this._contactSensor; 127 | } 128 | 129 | identify(callback) { 130 | this.log(`Identify requested on ${this.name}`); 131 | callback(); 132 | } 133 | 134 | _setPeriod(value, callback) { 135 | this.log(`Change target period of ${this.name} to ${value} ${Characteristic.SolarPeriod._SolarPeriods[value]}`); 136 | 137 | const data = clone(this._state); 138 | data.period = value; 139 | 140 | this._persist(data, callback); 141 | } 142 | 143 | _setOffset(value, callback) { 144 | this.log(`Change target offset of ${this.name} to ${value}`); 145 | 146 | const data = clone(this._state); 147 | data.offset = value; 148 | 149 | this._persist(data, callback); 150 | } 151 | 152 | _setEnabledState(value, callback) { 153 | this.log(`Change enabled state of ${this.name} to ${value}`); 154 | 155 | const data = clone(this._state); 156 | data.enabled = value; 157 | 158 | this._persist(data, callback); 159 | } 160 | 161 | _persist(data, callback) { 162 | this._storage.store(data, (error) => { 163 | if (error) { 164 | callback(error); 165 | return; 166 | } 167 | 168 | this._state = data; 169 | callback(); 170 | 171 | this._restartSolarTimer(); 172 | }); 173 | } 174 | 175 | _restartSolarTimer() { 176 | if (this._timer) { 177 | clearTimeout(this._timer); 178 | } 179 | 180 | if (this._state.enabled) { 181 | this._scheduleSolarClock(); 182 | } 183 | } 184 | 185 | _scheduleSolarClock() { 186 | /////////////////////////////////////////////////////////////////////////////////////////// 187 | // algorithm based on https://github.com/kcharwood/homebridge-suncalc 188 | /////////////////////////////////////////////////////////////////////////////////////////// 189 | 190 | const today = new Date(); 191 | const now = today.getTime(); 192 | const solarTime = Characteristic.SolarPeriod._SolarTimes[this._state.period]; 193 | 194 | let sunDates = suncalc.getTimes(today, this.location.latitude, this.location.longitude); 195 | let timeout = sunDates[solarTime].getTime() + (this._state.offset * 60 * 1000); 196 | let diff = timeout - now; 197 | 198 | if (timeout <= now) { 199 | const tomorrow = new Date(); 200 | 201 | tomorrow.setDate(tomorrow.getDate() + 1); 202 | sunDates = suncalc.getTimes(tomorrow, this.location.latitude, this.location.longitude); 203 | timeout = sunDates[solarTime].getTime() + (this._state.offset * 60 * 1000); 204 | diff = timeout - now; 205 | } 206 | 207 | this.log(`Raising next solar timer for ${solarTime} at ${new Date(timeout).toLocaleString()} in ${Math.round(diff / (60 * 1000))}mins`); 208 | this._timer = setTimeout(this._solar.bind(this), diff); 209 | } 210 | 211 | _solar() { 212 | this.log('Solar!'); 213 | this._contactSensor.getCharacteristic(Characteristic.ContactSensorState) 214 | .updateValue(this._solarValue); 215 | 216 | setTimeout(this._silenceSolar.bind(this), 1000); 217 | } 218 | 219 | _silenceSolar() { 220 | this.log('Solar silenced!'); 221 | this._contactSensor.getCharacteristic(Characteristic.ContactSensorState) 222 | .updateValue(this._noSolarValue); 223 | } 224 | } 225 | 226 | module.exports = SolarClockAccessory; 227 | -------------------------------------------------------------------------------- /src/SwitchAccessory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const clone = require('clone'); 4 | 5 | let Accessory, Characteristic, Service; 6 | 7 | class SwitchAccessory { 8 | 9 | constructor(api, log, config, storage) { 10 | Accessory = api.hap.Accessory; 11 | Characteristic = api.hap.Characteristic; 12 | Service = api.hap.Service; 13 | 14 | this.log = log; 15 | this.name = config.name; 16 | this._config = config; 17 | 18 | this._storage = storage; 19 | 20 | const defaultValue = { 21 | state: config.default === undefined ? false : config.default 22 | }; 23 | 24 | storage.retrieve(defaultValue, (error, value) => { 25 | this._state = value; 26 | }); 27 | 28 | this._services = this.createServices(); 29 | } 30 | 31 | getServices() { 32 | return this._services; 33 | } 34 | 35 | createServices() { 36 | return [ 37 | this.getAccessoryInformationService(), 38 | this.getBridgingStateService(), 39 | this.getSwitchService() 40 | ]; 41 | } 42 | 43 | getAccessoryInformationService() { 44 | return new Service.AccessoryInformation() 45 | .setCharacteristic(Characteristic.Name, this.name) 46 | .setCharacteristic(Characteristic.Manufacturer, 'Michael Froehlich') 47 | .setCharacteristic(Characteristic.Model, 'Switch') 48 | .setCharacteristic(Characteristic.SerialNumber, this._config.serialNumber) 49 | .setCharacteristic(Characteristic.FirmwareRevision, this._config.version) 50 | .setCharacteristic(Characteristic.HardwareRevision, this._config.version); 51 | } 52 | 53 | getBridgingStateService() { 54 | return new Service.BridgingState() 55 | .setCharacteristic(Characteristic.Reachable, true) 56 | .setCharacteristic(Characteristic.LinkQuality, 4) 57 | .setCharacteristic(Characteristic.AccessoryIdentifier, this.name) 58 | .setCharacteristic(Characteristic.Category, Accessory.Categories.SWITCH); 59 | } 60 | 61 | getSwitchService() { 62 | this._switchService = new Service.Switch(this.name); 63 | this._switchService.getCharacteristic(Characteristic.On) 64 | .on('set', this._setState.bind(this)) 65 | .updateValue(this._state.state); 66 | 67 | this._switchService.isPrimaryService = true; 68 | 69 | return this._switchService; 70 | } 71 | 72 | identify(callback) { 73 | this.log(`Identify requested on ${this.name}`); 74 | callback(); 75 | } 76 | 77 | _setState(value, callback) { 78 | this.log(`Change target state of ${this.name} to ${value}`); 79 | 80 | const data = clone(this._state); 81 | data.state = value; 82 | 83 | this._persist(data, callback); 84 | } 85 | 86 | _persist(data, callback) { 87 | this._storage.store(data, (error) => { 88 | if (error) { 89 | callback(error); 90 | return; 91 | } 92 | 93 | this._state = data; 94 | callback(); 95 | }); 96 | } 97 | } 98 | 99 | module.exports = SwitchAccessory; 100 | -------------------------------------------------------------------------------- /src/hap/ClockTypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var inherits = require('util').inherits; 4 | 5 | module.exports = { 6 | registerWith: function (hap) { 7 | 8 | const Characteristic = hap.Characteristic; 9 | const Service = hap.Service; 10 | 11 | /////////////////////////////////////////////////////////////////////////////////////////// 12 | // Clock: Hour 13 | /////////////////////////////////////////////////////////////////////////////////////////// 14 | Characteristic.ClockHour = function () { 15 | Characteristic.call(this, 'Hour', Characteristic.ClockHour.UUID); 16 | 17 | this.setProps({ 18 | format: Characteristic.Formats.INT, 19 | minValue: 0, 20 | maxValue: 23, 21 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE], 22 | }); 23 | 24 | this.value = this.getDefaultValue(); 25 | }; 26 | inherits(Characteristic.ClockHour, Characteristic); 27 | Characteristic.ClockHour.UUID = 'B534E0E3-2CB9-4A66-9353-EC886C949485'; 28 | 29 | /////////////////////////////////////////////////////////////////////////////////////////// 30 | // Clock: Minute 31 | /////////////////////////////////////////////////////////////////////////////////////////// 32 | Characteristic.ClockMinute = function () { 33 | Characteristic.call(this, 'Minute', Characteristic.ClockMinute.UUID); 34 | 35 | this.setProps({ 36 | format: Characteristic.Formats.INT, 37 | minValue: 0, 38 | maxValue: 59, 39 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE], 40 | }); 41 | 42 | this.value = this.getDefaultValue(); 43 | }; 44 | inherits(Characteristic.ClockMinute, Characteristic); 45 | Characteristic.ClockMinute.UUID = '9DD407F8-C090-4D57-9305-03F0679897B3'; 46 | 47 | 48 | Service.Clock = function (displayName, subtype) { 49 | Service.call(this, displayName, Service.Clock.UUID, subtype); 50 | 51 | // Required Characteristics 52 | this.addCharacteristic(Characteristic.ClockHour); 53 | this.addCharacteristic(Characteristic.ClockMinute); 54 | }; 55 | inherits(Service.Clock, Service); 56 | Service.Clock.UUID = '4FA3884A-D165-4248-8D0B-850F6086DDD4'; 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/hap/RandomTypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var inherits = require('util').inherits; 4 | 5 | module.exports = { 6 | registerWith: function (hap) { 7 | 8 | const Characteristic = hap.Characteristic; 9 | 10 | /////////////////////////////////////////////////////////////////////////////////////////// 11 | // Random: value 12 | /////////////////////////////////////////////////////////////////////////////////////////// 13 | Characteristic.RandomValue = function () { 14 | Characteristic.call(this, 'Value', Characteristic.RandomValue.UUID); 15 | 16 | this.setProps({ 17 | format: Characteristic.Formats.INT, 18 | minValue: 0, 19 | maxValue: 10000, 20 | perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY], 21 | }); 22 | 23 | this.value = this.getDefaultValue(); 24 | }; 25 | inherits(Characteristic.RandomValue, Characteristic); 26 | Characteristic.RandomValue.UUID = '6B9FAFFF-1F2D-45D8-A8C7-521309858F56'; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/hap/SolarTypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var inherits = require('util').inherits; 4 | 5 | module.exports = { 6 | registerWith: function (hap) { 7 | 8 | const Characteristic = hap.Characteristic; 9 | const Service = hap.Service; 10 | 11 | /////////////////////////////////////////////////////////////////////////////////////////// 12 | // Solar: Latitude 13 | /////////////////////////////////////////////////////////////////////////////////////////// 14 | Characteristic.SolarLatitude = function () { 15 | Characteristic.call(this, 'Latitude', Characteristic.SolarLatitude.UUID); 16 | 17 | this.setProps({ 18 | format: Characteristic.Formats.FLOAT, 19 | unit: Characteristic.Units.ARC_DEGREE, 20 | minValue: -90, 21 | maxValue: 90, 22 | minStep: 0.000001, 23 | perms: [Characteristic.Perms.READ], 24 | }); 25 | 26 | this.value = this.getDefaultValue(); 27 | }; 28 | inherits(Characteristic.SolarLatitude, Characteristic); 29 | Characteristic.SolarLatitude.UUID = '6FE198BF-29F2-493F-8B27-B60FE795C3A3'; 30 | 31 | /////////////////////////////////////////////////////////////////////////////////////////// 32 | // Solar: Longitude 33 | /////////////////////////////////////////////////////////////////////////////////////////// 34 | Characteristic.SolarLongitude = function () { 35 | Characteristic.call(this, 'Longitude', Characteristic.SolarLongitude.UUID); 36 | 37 | this.setProps({ 38 | format: Characteristic.Formats.FLOAT, 39 | unit: Characteristic.Units.ARC_DEGREE, 40 | minValue: -180, 41 | maxValue: 180, 42 | minStep: 0.000001, 43 | perms: [Characteristic.Perms.READ], 44 | }); 45 | 46 | this.value = this.getDefaultValue(); 47 | }; 48 | inherits(Characteristic.SolarLongitude, Characteristic); 49 | Characteristic.SolarLongitude.UUID = '9611DA9C-8F1A-4B23-A391-0973B7A9039D'; 50 | 51 | /////////////////////////////////////////////////////////////////////////////////////////// 52 | // Solar: MinutesOffset 53 | /////////////////////////////////////////////////////////////////////////////////////////// 54 | Characteristic.SolarMinutesOffset = function () { 55 | Characteristic.call(this, 'Offset', Characteristic.SolarMinutesOffset.UUID); 56 | 57 | this.setProps({ 58 | format: Characteristic.Formats.INT, 59 | unit: 'mins', 60 | minValue: -15, 61 | maxValue: 15, 62 | minStep: 1, 63 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE], 64 | }); 65 | 66 | this.value = this.getDefaultValue(); 67 | }; 68 | inherits(Characteristic.SolarMinutesOffset, Characteristic); 69 | Characteristic.SolarMinutesOffset.UUID = '09147249-07BC-4DB4-B916-31B77CD6EE13'; 70 | 71 | 72 | /////////////////////////////////////////////////////////////////////////////////////////// 73 | // constants taken from https://github.com/kcharwood/homebridge-suncalc 74 | /////////////////////////////////////////////////////////////////////////////////////////// 75 | 76 | const SolarPeriods = [ 'Night', 'Morning Twilight', 'Sunrise', 'Daytime', 'Sunset', 'Evening Twilight' ]; 77 | 78 | /////////////////////////////////////////////////////////////////////////////////////////// 79 | // Solar: Period 80 | /////////////////////////////////////////////////////////////////////////////////////////// 81 | Characteristic.SolarPeriod = function () { 82 | Characteristic.call(this, 'Period', Characteristic.SolarPeriod.UUID); 83 | 84 | this.setProps({ 85 | format: Characteristic.Formats.UINT8, 86 | minValue: 0, 87 | maxValue: SolarPeriods.length - 1, 88 | minStep: 1, 89 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY], 90 | }); 91 | 92 | this.value = this.getDefaultValue(); 93 | }; 94 | inherits(Characteristic.SolarPeriod, Characteristic); 95 | Characteristic.SolarPeriod.UUID = '4D640A06-34FE-45D7-BF7C-736BB2CF5693'; 96 | Characteristic.SolarPeriod._SolarPeriods = SolarPeriods; 97 | Characteristic.SolarPeriod._SolarTimes = [ 'night', 'dawn', 'sunrise', 'sunriseEnd', 'sunsetStart', 'sunset' ]; 98 | 99 | 100 | Service.Solar = function (displayName, subtype) { 101 | Service.call(this, displayName, Service.Solar.UUID, subtype); 102 | 103 | // Required Characteristics 104 | this.addCharacteristic(Characteristic.SolarPeriod); 105 | this.addCharacteristic(Characteristic.SolarMinutesOffset); 106 | }; 107 | inherits(Service.Solar, Service); 108 | Service.Solar.UUID = 'F9305C45-DBC5-4BD1-B4DA-C67A495288CD'; 109 | 110 | Service.SolarLocation = function (displayName, subtype) { 111 | Service.call(this, displayName, Service.SolarLocation.UUID, subtype); 112 | 113 | // Required Characteristics 114 | this.addCharacteristic(Characteristic.SolarLatitude); 115 | this.addCharacteristic(Characteristic.SolarLongitude); 116 | }; 117 | inherits(Service.SolarLocation, Service); 118 | Service.SolarLocation.UUID = '0C982673-8293-4CE4-8AC3-9371980D81A7'; 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | const util = require('util'); 3 | 4 | const version = require('../package.json').version; 5 | 6 | const AutomationSwitchAccessory = require('./AutomationSwitchAccessory'); 7 | const SecuritySystemAccessory = require('./SecuritySystemAccessory'); 8 | const LockMechanismAccessory = require('./LockMechanismAccessory'); 9 | const SwitchAccessory = require('./SwitchAccessory'); 10 | const SliderAccessory = require('./SliderAccessory'); 11 | const AlarmClockAccessory = require('./AlarmClockAccessory'); 12 | const SolarClockAccessory = require('./SolarClockAccessory'); 13 | const RandomAccessory = require('./RandomAccessory'); 14 | 15 | const StorageWrapper = require('./util/StorageWrapper'); 16 | const FakeStorageWrapper = require('./util/FakeStorageWrapper'); 17 | const SerialNumberGenerator = require('./util/SerialNumberGenerator'); 18 | 19 | const HomeKitTypes = require('./HomeKitTypes'); 20 | const ClockTypes = require('./hap/ClockTypes'); 21 | const SolarTypes = require('./hap/SolarTypes'); 22 | const RandomTypes = require('./hap/RandomTypes'); 23 | 24 | 25 | const HOMEBRIDGE = { 26 | Accessory: null, 27 | Service: null, 28 | Characteristic: null, 29 | UUIDGen: null 30 | }; 31 | 32 | const platformName = 'homebridge-switches'; 33 | const platformPrettyName = 'AutomationSwitches'; 34 | 35 | module.exports = (homebridge) => { 36 | HOMEBRIDGE.Accessory = homebridge.platformAccessory; 37 | HOMEBRIDGE.Service = homebridge.hap.Service; 38 | HOMEBRIDGE.Characteristic = homebridge.hap.Characteristic; 39 | HOMEBRIDGE.UUIDGen = homebridge.hap.uuid; 40 | 41 | homebridge.registerPlatform(platformName, platformPrettyName, AutomationSwitchesPlatform, true); 42 | }; 43 | 44 | const SerialNumberPrefixes = { 45 | automation: 'AU', 46 | lock: 'LK', 47 | security: 'SC', 48 | switch: 'SW', 49 | slider: 'SL', 50 | alarmclock: 'AC', 51 | solarclock: 'BC', 52 | random: 'RA' 53 | }; 54 | 55 | const AutomationSwitchesPlatform = class { 56 | constructor(log, config, api) { 57 | this.log = log; 58 | this.log(`AutomationSwitchesPlatform Plugin Loaded - Version ${version}`); 59 | this.config = config; 60 | this.api = api; 61 | 62 | HomeKitTypes.registerWith(api.hap); 63 | ClockTypes.registerWith(api.hap); 64 | SolarTypes.registerWith(api.hap); 65 | RandomTypes.registerWith(api.hap); 66 | 67 | this._factories = { 68 | automation: this._createAutomationSwitch.bind(this), 69 | lock: this._createLockMechanism.bind(this), 70 | security: this._createSecuritySwitch.bind(this), 71 | switch: this._createSwitch.bind(this), 72 | slider: this._createSlider.bind(this), 73 | alarmclock: this._createAlarmClock.bind(this), 74 | solarclock: this._createSolarClock.bind(this), 75 | random: this._createRandom.bind(this) 76 | }; 77 | } 78 | 79 | accessories(callback) { 80 | let _accessories = []; 81 | const { switches } = this.config; 82 | 83 | switches.forEach(sw => { 84 | this.log(`Found automation switch in config: "${sw.name}"`); 85 | if (sw.name === undefined || sw.name.length === 0) { 86 | throw new Error('Invalid configuration: Automation switch name is invalid.'); 87 | } 88 | 89 | if (sw.type === undefined) { 90 | this.log(`Warning: ${sw.name} does not specify a type. Please update your configuration`); 91 | this.log(`to include "type": "automation" for ${sw.name} or it'll fail to run in the future.`); 92 | sw.type = 'automation'; 93 | } 94 | 95 | const factory = this._factories[sw.type]; 96 | if (factory === undefined) { 97 | this.log(`Invalid automation switch - type is unknown: ${util.inspect(sw)}`); 98 | this.log('Skipping.'); 99 | return; 100 | } 101 | 102 | sw.serialNumber = SerialNumberGenerator.generate(SerialNumberPrefixes[sw.type], sw.name); 103 | 104 | const storage = this._createStorage(sw); 105 | 106 | const accessory = factory(sw, storage); 107 | _accessories.push(accessory); 108 | }); 109 | 110 | callback(_accessories); 111 | } 112 | 113 | _createStorage(sw) { 114 | if (this._shouldStoreSwitchState(sw)) { 115 | const type = this._sanitizeTypeForStorage(sw.type); 116 | return new StorageWrapper(this.api, this.log, type, sw.name); 117 | } 118 | 119 | return new FakeStorageWrapper(); 120 | } 121 | 122 | _shouldStoreSwitchState(sw) { 123 | return sw.stored === true 124 | || (sw.type === 'security' && sw.stored !== false); 125 | } 126 | 127 | _sanitizeTypeForStorage(type) { 128 | if (type === 'security') { 129 | type = 'SecuritySystem'; 130 | } 131 | 132 | return type; 133 | } 134 | 135 | _createAutomationSwitch(sw, storage) { 136 | // Make sure minimal configuration is set 137 | sw.autoOff = typeof sw.autoOff !== 'undefined' ? sw.autoOff : true; 138 | sw.period = sw.period || 60; 139 | sw.version = version; 140 | 141 | return new AutomationSwitchAccessory(this.api, this.log, sw, storage); 142 | } 143 | 144 | _createSecuritySwitch(sw, storage) { 145 | sw.version = version; 146 | return new SecuritySystemAccessory(this.api, this.log, sw, storage); 147 | } 148 | 149 | _createLockMechanism(sw, storage) { 150 | sw.version = version; 151 | return new LockMechanismAccessory(this.api, this.log, sw, storage); 152 | } 153 | 154 | _createSwitch(sw, storage) { 155 | sw.version = version; 156 | return new SwitchAccessory(this.api, this.log, sw, storage); 157 | } 158 | 159 | _createSlider(sw, storage) { 160 | sw.version = version; 161 | return new SliderAccessory(this.api, this.log, sw, storage); 162 | } 163 | 164 | _createAlarmClock(sw, storage) { 165 | sw.version = version; 166 | return new AlarmClockAccessory(this.api, this.log, sw, storage); 167 | } 168 | 169 | _createSolarClock(sw, storage) { 170 | sw.version = version; 171 | return new SolarClockAccessory(this.api, this.log, sw, storage); 172 | } 173 | 174 | _createRandom(sw, storage) { 175 | sw.version = version; 176 | return new RandomAccessory(this.api, this.log, sw, storage); 177 | } 178 | }; 179 | -------------------------------------------------------------------------------- /src/util/FakeStorageWrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class FakeStorageWrapper { 4 | store(value, callback) { 5 | callback(undefined); 6 | } 7 | 8 | retrieve(defaultValue, callback) { 9 | callback(undefined, defaultValue); 10 | } 11 | } 12 | 13 | module.exports = FakeStorageWrapper; 14 | -------------------------------------------------------------------------------- /src/util/NameFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var crypto = require('crypto'); 4 | 5 | module.exports = { 6 | generate: generate 7 | }; 8 | 9 | function generate(data) { 10 | var sha1sum = crypto.createHash('sha1'); 11 | sha1sum.update(data); 12 | var s = sha1sum.digest('hex'); 13 | var i = -1; 14 | return 'xxxxxxxxxxxx'.replace(/[x]/g, function () { 15 | i += 1; 16 | return s[i]; 17 | }).toUpperCase(); 18 | } 19 | -------------------------------------------------------------------------------- /src/util/SerialNumberGenerator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var crypto = require('crypto'); 4 | 5 | module.exports = { 6 | generate: generate 7 | }; 8 | 9 | function generate(prefix, name) { 10 | const sha1sum = crypto.createHash('sha1'); 11 | sha1sum.update(prefix); 12 | sha1sum.update(name); 13 | 14 | const s = sha1sum.digest('hex'); 15 | let i = -1; 16 | 17 | const serialNumber = 'xxxxxxxxxxxx'.replace(/[x]/g, () => s[++i]).toUpperCase(); 18 | 19 | return [prefix, serialNumber].join(''); 20 | } 21 | -------------------------------------------------------------------------------- /src/util/StorageWrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Storage = require('node-persist').create(); 4 | const NameFactory = require('./NameFactory'); 5 | 6 | class StorageWrapper { 7 | constructor(api, log, type, name) { 8 | this._key = `${type}.${NameFactory.generate(name)}.json`; 9 | log(`Switch ${name} is stored in file ${this._key}`); 10 | 11 | Storage.initSync({ dir: api.user.persistPath() }); 12 | } 13 | 14 | store(value, callback) { 15 | Storage.setItem(this._key, value, (error) => { 16 | if (error) { 17 | callback(error); 18 | return; 19 | } 20 | 21 | Storage.persistKey(this._key, (error) => { 22 | callback(error); 23 | }); 24 | }); 25 | } 26 | 27 | retrieve(defaultValue, callback) { 28 | Storage.getItem(this._key, (error, data) => { 29 | if (error) { 30 | callback(error, defaultValue); 31 | return; 32 | } 33 | 34 | if (data === undefined) { 35 | data = defaultValue; 36 | } 37 | 38 | callback(undefined, data); 39 | }); 40 | } 41 | } 42 | 43 | module.exports = StorageWrapper; 44 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | acorn-jsx@^3.0.0: 6 | version "3.0.1" 7 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" 8 | integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= 9 | dependencies: 10 | acorn "^3.0.4" 11 | 12 | acorn@^3.0.4: 13 | version "3.3.0" 14 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 15 | integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= 16 | 17 | acorn@^5.5.0: 18 | version "5.7.3" 19 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" 20 | integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== 21 | 22 | ajv-keywords@^2.1.0: 23 | version "2.1.1" 24 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" 25 | integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= 26 | 27 | ajv@^5.2.3, ajv@^5.3.0: 28 | version "5.5.2" 29 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" 30 | integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= 31 | dependencies: 32 | co "^4.6.0" 33 | fast-deep-equal "^1.0.0" 34 | fast-json-stable-stringify "^2.0.0" 35 | json-schema-traverse "^0.3.0" 36 | 37 | ansi-escapes@^3.0.0: 38 | version "3.2.0" 39 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" 40 | integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== 41 | 42 | ansi-regex@^2.0.0: 43 | version "2.1.1" 44 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 45 | integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= 46 | 47 | ansi-regex@^3.0.0: 48 | version "3.0.0" 49 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 50 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 51 | 52 | ansi-styles@^2.2.1: 53 | version "2.2.1" 54 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 55 | integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= 56 | 57 | ansi-styles@^3.2.1: 58 | version "3.2.1" 59 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 60 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 61 | dependencies: 62 | color-convert "^1.9.0" 63 | 64 | argparse@^1.0.7: 65 | version "1.0.10" 66 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 67 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 68 | dependencies: 69 | sprintf-js "~1.0.2" 70 | 71 | babel-code-frame@^6.22.0: 72 | version "6.26.0" 73 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" 74 | integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= 75 | dependencies: 76 | chalk "^1.1.3" 77 | esutils "^2.0.2" 78 | js-tokens "^3.0.2" 79 | 80 | balanced-match@^1.0.0: 81 | version "1.0.0" 82 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 83 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 84 | 85 | brace-expansion@^1.1.7: 86 | version "1.1.11" 87 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 88 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 89 | dependencies: 90 | balanced-match "^1.0.0" 91 | concat-map "0.0.1" 92 | 93 | buffer-from@^1.0.0: 94 | version "1.1.1" 95 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 96 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 97 | 98 | caller-path@^0.1.0: 99 | version "0.1.0" 100 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 101 | integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= 102 | dependencies: 103 | callsites "^0.2.0" 104 | 105 | callsites@^0.2.0: 106 | version "0.2.0" 107 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 108 | integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= 109 | 110 | chalk@^1.1.3: 111 | version "1.1.3" 112 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 113 | integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= 114 | dependencies: 115 | ansi-styles "^2.2.1" 116 | escape-string-regexp "^1.0.2" 117 | has-ansi "^2.0.0" 118 | strip-ansi "^3.0.0" 119 | supports-color "^2.0.0" 120 | 121 | chalk@^2.0.0, chalk@^2.1.0: 122 | version "2.4.2" 123 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 124 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 125 | dependencies: 126 | ansi-styles "^3.2.1" 127 | escape-string-regexp "^1.0.5" 128 | supports-color "^5.3.0" 129 | 130 | chardet@^0.4.0: 131 | version "0.4.2" 132 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" 133 | integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= 134 | 135 | circular-json@^0.3.1: 136 | version "0.3.3" 137 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" 138 | integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== 139 | 140 | cli-cursor@^2.1.0: 141 | version "2.1.0" 142 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 143 | integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= 144 | dependencies: 145 | restore-cursor "^2.0.0" 146 | 147 | cli-width@^2.0.0: 148 | version "2.2.0" 149 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 150 | integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= 151 | 152 | clone@^2.1.1: 153 | version "2.1.2" 154 | resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" 155 | integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= 156 | 157 | co@^4.6.0: 158 | version "4.6.0" 159 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 160 | integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= 161 | 162 | color-convert@^1.9.0: 163 | version "1.9.3" 164 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 165 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 166 | dependencies: 167 | color-name "1.1.3" 168 | 169 | color-name@1.1.3: 170 | version "1.1.3" 171 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 172 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 173 | 174 | concat-map@0.0.1: 175 | version "0.0.1" 176 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 177 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 178 | 179 | concat-stream@^1.6.0: 180 | version "1.6.2" 181 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 182 | integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== 183 | dependencies: 184 | buffer-from "^1.0.0" 185 | inherits "^2.0.3" 186 | readable-stream "^2.2.2" 187 | typedarray "^0.0.6" 188 | 189 | core-util-is@~1.0.0: 190 | version "1.0.2" 191 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 192 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 193 | 194 | cross-spawn@^5.1.0: 195 | version "5.1.0" 196 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" 197 | integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= 198 | dependencies: 199 | lru-cache "^4.0.1" 200 | shebang-command "^1.2.0" 201 | which "^1.2.9" 202 | 203 | debug@^3.1.0: 204 | version "3.2.6" 205 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 206 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 207 | dependencies: 208 | ms "^2.1.1" 209 | 210 | deep-is@~0.1.3: 211 | version "0.1.3" 212 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 213 | integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= 214 | 215 | doctrine@^2.1.0: 216 | version "2.1.0" 217 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" 218 | integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== 219 | dependencies: 220 | esutils "^2.0.2" 221 | 222 | escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 223 | version "1.0.5" 224 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 225 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 226 | 227 | eslint-scope@^3.7.1: 228 | version "3.7.3" 229 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" 230 | integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== 231 | dependencies: 232 | esrecurse "^4.1.0" 233 | estraverse "^4.1.1" 234 | 235 | eslint-visitor-keys@^1.0.0: 236 | version "1.0.0" 237 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" 238 | integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== 239 | 240 | eslint@^4.19.1: 241 | version "4.19.1" 242 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" 243 | integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== 244 | dependencies: 245 | ajv "^5.3.0" 246 | babel-code-frame "^6.22.0" 247 | chalk "^2.1.0" 248 | concat-stream "^1.6.0" 249 | cross-spawn "^5.1.0" 250 | debug "^3.1.0" 251 | doctrine "^2.1.0" 252 | eslint-scope "^3.7.1" 253 | eslint-visitor-keys "^1.0.0" 254 | espree "^3.5.4" 255 | esquery "^1.0.0" 256 | esutils "^2.0.2" 257 | file-entry-cache "^2.0.0" 258 | functional-red-black-tree "^1.0.1" 259 | glob "^7.1.2" 260 | globals "^11.0.1" 261 | ignore "^3.3.3" 262 | imurmurhash "^0.1.4" 263 | inquirer "^3.0.6" 264 | is-resolvable "^1.0.0" 265 | js-yaml "^3.9.1" 266 | json-stable-stringify-without-jsonify "^1.0.1" 267 | levn "^0.3.0" 268 | lodash "^4.17.4" 269 | minimatch "^3.0.2" 270 | mkdirp "^0.5.1" 271 | natural-compare "^1.4.0" 272 | optionator "^0.8.2" 273 | path-is-inside "^1.0.2" 274 | pluralize "^7.0.0" 275 | progress "^2.0.0" 276 | regexpp "^1.0.1" 277 | require-uncached "^1.0.3" 278 | semver "^5.3.0" 279 | strip-ansi "^4.0.0" 280 | strip-json-comments "~2.0.1" 281 | table "4.0.2" 282 | text-table "~0.2.0" 283 | 284 | espree@^3.5.4: 285 | version "3.5.4" 286 | resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" 287 | integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== 288 | dependencies: 289 | acorn "^5.5.0" 290 | acorn-jsx "^3.0.0" 291 | 292 | esprima@^4.0.0: 293 | version "4.0.1" 294 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 295 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 296 | 297 | esquery@^1.0.0: 298 | version "1.0.1" 299 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" 300 | integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== 301 | dependencies: 302 | estraverse "^4.0.0" 303 | 304 | esrecurse@^4.1.0: 305 | version "4.2.1" 306 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 307 | integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== 308 | dependencies: 309 | estraverse "^4.1.0" 310 | 311 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: 312 | version "4.2.0" 313 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 314 | integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= 315 | 316 | esutils@^2.0.2: 317 | version "2.0.2" 318 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 319 | integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= 320 | 321 | external-editor@^2.0.4: 322 | version "2.2.0" 323 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" 324 | integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== 325 | dependencies: 326 | chardet "^0.4.0" 327 | iconv-lite "^0.4.17" 328 | tmp "^0.0.33" 329 | 330 | fast-deep-equal@^1.0.0: 331 | version "1.1.0" 332 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" 333 | integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= 334 | 335 | fast-json-stable-stringify@^2.0.0: 336 | version "2.0.0" 337 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 338 | integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= 339 | 340 | fast-levenshtein@~2.0.4: 341 | version "2.0.6" 342 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 343 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 344 | 345 | figures@^2.0.0: 346 | version "2.0.0" 347 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 348 | integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= 349 | dependencies: 350 | escape-string-regexp "^1.0.5" 351 | 352 | file-entry-cache@^2.0.0: 353 | version "2.0.0" 354 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 355 | integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= 356 | dependencies: 357 | flat-cache "^1.2.1" 358 | object-assign "^4.0.1" 359 | 360 | flat-cache@^1.2.1: 361 | version "1.3.4" 362 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" 363 | integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== 364 | dependencies: 365 | circular-json "^0.3.1" 366 | graceful-fs "^4.1.2" 367 | rimraf "~2.6.2" 368 | write "^0.2.1" 369 | 370 | fs.realpath@^1.0.0: 371 | version "1.0.0" 372 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 373 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 374 | 375 | functional-red-black-tree@^1.0.1: 376 | version "1.0.1" 377 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 378 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 379 | 380 | glob@^7.1.2, glob@^7.1.3: 381 | version "7.1.4" 382 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" 383 | integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== 384 | dependencies: 385 | fs.realpath "^1.0.0" 386 | inflight "^1.0.4" 387 | inherits "2" 388 | minimatch "^3.0.4" 389 | once "^1.3.0" 390 | path-is-absolute "^1.0.0" 391 | 392 | globals@^11.0.1: 393 | version "11.12.0" 394 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 395 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 396 | 397 | graceful-fs@^4.1.2: 398 | version "4.2.0" 399 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" 400 | integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== 401 | 402 | has-ansi@^2.0.0: 403 | version "2.0.0" 404 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 405 | integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= 406 | dependencies: 407 | ansi-regex "^2.0.0" 408 | 409 | has-flag@^3.0.0: 410 | version "3.0.0" 411 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 412 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 413 | 414 | iconv-lite@^0.4.17: 415 | version "0.4.24" 416 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 417 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 418 | dependencies: 419 | safer-buffer ">= 2.1.2 < 3" 420 | 421 | ignore@^3.3.3: 422 | version "3.3.10" 423 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" 424 | integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== 425 | 426 | imurmurhash@^0.1.4: 427 | version "0.1.4" 428 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 429 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 430 | 431 | inflight@^1.0.4: 432 | version "1.0.6" 433 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 434 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 435 | dependencies: 436 | once "^1.3.0" 437 | wrappy "1" 438 | 439 | inherits@2, inherits@^2.0.3, inherits@~2.0.3: 440 | version "2.0.4" 441 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 442 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 443 | 444 | inquirer@^3.0.6: 445 | version "3.3.0" 446 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" 447 | integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== 448 | dependencies: 449 | ansi-escapes "^3.0.0" 450 | chalk "^2.0.0" 451 | cli-cursor "^2.1.0" 452 | cli-width "^2.0.0" 453 | external-editor "^2.0.4" 454 | figures "^2.0.0" 455 | lodash "^4.3.0" 456 | mute-stream "0.0.7" 457 | run-async "^2.2.0" 458 | rx-lite "^4.0.8" 459 | rx-lite-aggregates "^4.0.8" 460 | string-width "^2.1.0" 461 | strip-ansi "^4.0.0" 462 | through "^2.3.6" 463 | 464 | is-fullwidth-code-point@^2.0.0: 465 | version "2.0.0" 466 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 467 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 468 | 469 | is-promise@^2.1.0: 470 | version "2.1.0" 471 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 472 | integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= 473 | 474 | is-resolvable@^1.0.0: 475 | version "1.1.0" 476 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" 477 | integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== 478 | 479 | isarray@~1.0.0: 480 | version "1.0.0" 481 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 482 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 483 | 484 | isexe@^2.0.0: 485 | version "2.0.0" 486 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 487 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 488 | 489 | js-tokens@^3.0.2: 490 | version "3.0.2" 491 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 492 | integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= 493 | 494 | js-yaml@^3.9.1: 495 | version "3.13.1" 496 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 497 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 498 | dependencies: 499 | argparse "^1.0.7" 500 | esprima "^4.0.0" 501 | 502 | json-schema-traverse@^0.3.0: 503 | version "0.3.1" 504 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" 505 | integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= 506 | 507 | json-stable-stringify-without-jsonify@^1.0.1: 508 | version "1.0.1" 509 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 510 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 511 | 512 | levn@^0.3.0, levn@~0.3.0: 513 | version "0.3.0" 514 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 515 | integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= 516 | dependencies: 517 | prelude-ls "~1.1.2" 518 | type-check "~0.3.2" 519 | 520 | lodash@^4.17.4, lodash@^4.3.0: 521 | version "4.17.15" 522 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" 523 | integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== 524 | 525 | lru-cache@^4.0.1: 526 | version "4.1.5" 527 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" 528 | integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== 529 | dependencies: 530 | pseudomap "^1.0.2" 531 | yallist "^2.1.2" 532 | 533 | mimic-fn@^1.0.0: 534 | version "1.2.0" 535 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 536 | integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== 537 | 538 | minimatch@^3.0.2, minimatch@^3.0.4: 539 | version "3.0.4" 540 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 541 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 542 | dependencies: 543 | brace-expansion "^1.1.7" 544 | 545 | minimist@0.0.8: 546 | version "0.0.8" 547 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 548 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 549 | 550 | mkdirp@^0.5.1, mkdirp@~0.5.1: 551 | version "0.5.1" 552 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 553 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 554 | dependencies: 555 | minimist "0.0.8" 556 | 557 | ms@^2.1.1: 558 | version "2.1.2" 559 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 560 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 561 | 562 | mute-stream@0.0.7: 563 | version "0.0.7" 564 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 565 | integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= 566 | 567 | natural-compare@^1.4.0: 568 | version "1.4.0" 569 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 570 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 571 | 572 | node-persist@^0.0.11: 573 | version "0.0.11" 574 | resolved "https://registry.yarnpkg.com/node-persist/-/node-persist-0.0.11.tgz#d66eba3ebef620f079530fa7b13076a906665874" 575 | integrity sha1-1m66Pr72IPB5Uw+nsTB2qQZmWHQ= 576 | dependencies: 577 | mkdirp "~0.5.1" 578 | q "~1.1.1" 579 | 580 | object-assign@^4.0.1: 581 | version "4.1.1" 582 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 583 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 584 | 585 | once@^1.3.0: 586 | version "1.4.0" 587 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 588 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 589 | dependencies: 590 | wrappy "1" 591 | 592 | onetime@^2.0.0: 593 | version "2.0.1" 594 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 595 | integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= 596 | dependencies: 597 | mimic-fn "^1.0.0" 598 | 599 | optionator@^0.8.2: 600 | version "0.8.2" 601 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 602 | integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= 603 | dependencies: 604 | deep-is "~0.1.3" 605 | fast-levenshtein "~2.0.4" 606 | levn "~0.3.0" 607 | prelude-ls "~1.1.2" 608 | type-check "~0.3.2" 609 | wordwrap "~1.0.0" 610 | 611 | os-tmpdir@~1.0.2: 612 | version "1.0.2" 613 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 614 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 615 | 616 | path-is-absolute@^1.0.0: 617 | version "1.0.1" 618 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 619 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 620 | 621 | path-is-inside@^1.0.2: 622 | version "1.0.2" 623 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 624 | integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= 625 | 626 | pluralize@^7.0.0: 627 | version "7.0.0" 628 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" 629 | integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== 630 | 631 | prelude-ls@~1.1.2: 632 | version "1.1.2" 633 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 634 | integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= 635 | 636 | process-nextick-args@~2.0.0: 637 | version "2.0.1" 638 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 639 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 640 | 641 | progress@^2.0.0: 642 | version "2.0.3" 643 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 644 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 645 | 646 | pseudomap@^1.0.2: 647 | version "1.0.2" 648 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 649 | integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= 650 | 651 | q@~1.1.1: 652 | version "1.1.2" 653 | resolved "https://registry.yarnpkg.com/q/-/q-1.1.2.tgz#6357e291206701d99f197ab84e57e8ad196f2a89" 654 | integrity sha1-Y1fikSBnAdmfGXq4TlforRlvKok= 655 | 656 | readable-stream@^2.2.2: 657 | version "2.3.6" 658 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 659 | integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== 660 | dependencies: 661 | core-util-is "~1.0.0" 662 | inherits "~2.0.3" 663 | isarray "~1.0.0" 664 | process-nextick-args "~2.0.0" 665 | safe-buffer "~5.1.1" 666 | string_decoder "~1.1.1" 667 | util-deprecate "~1.0.1" 668 | 669 | regexpp@^1.0.1: 670 | version "1.1.0" 671 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" 672 | integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== 673 | 674 | require-uncached@^1.0.3: 675 | version "1.0.3" 676 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 677 | integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= 678 | dependencies: 679 | caller-path "^0.1.0" 680 | resolve-from "^1.0.0" 681 | 682 | resolve-from@^1.0.0: 683 | version "1.0.1" 684 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 685 | integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= 686 | 687 | restore-cursor@^2.0.0: 688 | version "2.0.0" 689 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 690 | integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= 691 | dependencies: 692 | onetime "^2.0.0" 693 | signal-exit "^3.0.2" 694 | 695 | rimraf@~2.6.2: 696 | version "2.6.3" 697 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 698 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 699 | dependencies: 700 | glob "^7.1.3" 701 | 702 | run-async@^2.2.0: 703 | version "2.3.0" 704 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 705 | integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= 706 | dependencies: 707 | is-promise "^2.1.0" 708 | 709 | rx-lite-aggregates@^4.0.8: 710 | version "4.0.8" 711 | resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" 712 | integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= 713 | dependencies: 714 | rx-lite "*" 715 | 716 | rx-lite@*, rx-lite@^4.0.8: 717 | version "4.0.8" 718 | resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" 719 | integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= 720 | 721 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 722 | version "5.1.2" 723 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 724 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 725 | 726 | "safer-buffer@>= 2.1.2 < 3": 727 | version "2.1.2" 728 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 729 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 730 | 731 | semver@^5.3.0: 732 | version "5.7.0" 733 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" 734 | integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== 735 | 736 | shebang-command@^1.2.0: 737 | version "1.2.0" 738 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 739 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 740 | dependencies: 741 | shebang-regex "^1.0.0" 742 | 743 | shebang-regex@^1.0.0: 744 | version "1.0.0" 745 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 746 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 747 | 748 | signal-exit@^3.0.2: 749 | version "3.0.2" 750 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 751 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= 752 | 753 | slice-ansi@1.0.0: 754 | version "1.0.0" 755 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" 756 | integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== 757 | dependencies: 758 | is-fullwidth-code-point "^2.0.0" 759 | 760 | sprintf-js@~1.0.2: 761 | version "1.0.3" 762 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 763 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 764 | 765 | string-width@^2.1.0, string-width@^2.1.1: 766 | version "2.1.1" 767 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 768 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 769 | dependencies: 770 | is-fullwidth-code-point "^2.0.0" 771 | strip-ansi "^4.0.0" 772 | 773 | string_decoder@~1.1.1: 774 | version "1.1.1" 775 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 776 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 777 | dependencies: 778 | safe-buffer "~5.1.0" 779 | 780 | strip-ansi@^3.0.0: 781 | version "3.0.1" 782 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 783 | integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= 784 | dependencies: 785 | ansi-regex "^2.0.0" 786 | 787 | strip-ansi@^4.0.0: 788 | version "4.0.0" 789 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 790 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 791 | dependencies: 792 | ansi-regex "^3.0.0" 793 | 794 | strip-json-comments@~2.0.1: 795 | version "2.0.1" 796 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 797 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 798 | 799 | suncalc@^1.8.0: 800 | version "1.8.0" 801 | resolved "https://registry.yarnpkg.com/suncalc/-/suncalc-1.8.0.tgz#1d9898109563078750f4994a959e654d876acbf5" 802 | integrity sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U= 803 | 804 | supports-color@^2.0.0: 805 | version "2.0.0" 806 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 807 | integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= 808 | 809 | supports-color@^5.3.0: 810 | version "5.5.0" 811 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 812 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 813 | dependencies: 814 | has-flag "^3.0.0" 815 | 816 | table@4.0.2: 817 | version "4.0.2" 818 | resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" 819 | integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== 820 | dependencies: 821 | ajv "^5.2.3" 822 | ajv-keywords "^2.1.0" 823 | chalk "^2.1.0" 824 | lodash "^4.17.4" 825 | slice-ansi "1.0.0" 826 | string-width "^2.1.1" 827 | 828 | text-table@~0.2.0: 829 | version "0.2.0" 830 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 831 | integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 832 | 833 | through@^2.3.6: 834 | version "2.3.8" 835 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 836 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 837 | 838 | tmp@^0.0.33: 839 | version "0.0.33" 840 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 841 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== 842 | dependencies: 843 | os-tmpdir "~1.0.2" 844 | 845 | type-check@~0.3.2: 846 | version "0.3.2" 847 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 848 | integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= 849 | dependencies: 850 | prelude-ls "~1.1.2" 851 | 852 | typedarray@^0.0.6: 853 | version "0.0.6" 854 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 855 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 856 | 857 | util-deprecate@~1.0.1: 858 | version "1.0.2" 859 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 860 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 861 | 862 | which@^1.2.9: 863 | version "1.3.1" 864 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 865 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 866 | dependencies: 867 | isexe "^2.0.0" 868 | 869 | wordwrap@~1.0.0: 870 | version "1.0.0" 871 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 872 | integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= 873 | 874 | wrappy@1: 875 | version "1.0.2" 876 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 877 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 878 | 879 | write@^0.2.1: 880 | version "0.2.1" 881 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 882 | integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= 883 | dependencies: 884 | mkdirp "^0.5.1" 885 | 886 | yallist@^2.1.2: 887 | version "2.1.2" 888 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 889 | integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= 890 | --------------------------------------------------------------------------------