commit 58891b99f45ae16e31a8918a76a9310bd0bc96e0 Author: jangxx Date: Fri Jan 25 16:14:16 2019 +0100 initial commit diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..607a36b --- /dev/null +++ b/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 6 +} \ No newline at end of file diff --git a/ColorlampBase.js b/ColorlampBase.js new file mode 100644 index 0000000..45fb27d --- /dev/null +++ b/ColorlampBase.js @@ -0,0 +1,34 @@ +const HCControlBase = require('./ControlBase'); +const ColorlampState = require('./states/ColorlampState'); + +class HCColorlamp extends HCControlBase { + constructor(config) { + super(config, new ColorlampState()); + } + + get type() { + return "colorlamp"; + } + + turnOn() { + return Promise.reject(); + } + + turnOff() { + return Promise.reject(); + } + + toggle() { + return Promise.reject(); + } + + changeBrightness(brightness) { + return Promise.reject(); + } + + changeColor(hue, sat, light) { + return Promise.reject(); + } +} + +module.exports = HCColorlamp; \ No newline at end of file diff --git a/ControlBase.js b/ControlBase.js new file mode 100644 index 0000000..e1eea24 --- /dev/null +++ b/ControlBase.js @@ -0,0 +1,33 @@ +const { EventEmitter } = require('events'); +const BaseState = require('./states/BaseState'); + +class HCControlBase extends EventEmitter { + /** + * @param {Object} configuration + * @param {BaseState} state + */ + constructor(configuration, state) { + super(); + + this._configuration = configuration; + this._state = state; + } + + get state() { + return this._state.clone(); + } + + get type() { + return "none"; + } + + /** + * Updates the state by polling the controlled device + * @returns A promise which resolves when the state has been pulled + */ + pullState() { + return Promise.resolve(); + } +} + +module.exports = HCControlBase; \ No newline at end of file diff --git a/SwitchBase.js b/SwitchBase.js new file mode 100644 index 0000000..5b22532 --- /dev/null +++ b/SwitchBase.js @@ -0,0 +1,26 @@ +const HCControlBase = require('./ControlBase'); +const SwitchState = require('./states/SwitchState'); + +class HCSwitchBase extends HCControlBase { + constructor(config) { + super(config, new SwitchState()); + } + + get type() { + return "switch"; + } + + turnOn() { + return Promise.reject(); + } + + turnOff() { + return Promise.reject(); + } + + toggle() { + return Promise.reject(); + } +} + +module.exports = HCSwitchBase; \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..20a320f --- /dev/null +++ b/index.js @@ -0,0 +1,4 @@ +module.exports = { + HCColorLamp: require('./ColorlampBase'), + HCSwitch: require('./SwitchBase'), +}; \ No newline at end of file diff --git a/node_modules/is-plain-obj/index.js b/node_modules/is-plain-obj/index.js new file mode 100644 index 0000000..0d1ba9e --- /dev/null +++ b/node_modules/is-plain-obj/index.js @@ -0,0 +1,7 @@ +'use strict'; +var toString = Object.prototype.toString; + +module.exports = function (x) { + var prototype; + return toString.call(x) === '[object Object]' && (prototype = Object.getPrototypeOf(x), prototype === null || prototype === Object.getPrototypeOf({})); +}; diff --git a/node_modules/is-plain-obj/license b/node_modules/is-plain-obj/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/node_modules/is-plain-obj/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/is-plain-obj/package.json b/node_modules/is-plain-obj/package.json new file mode 100644 index 0000000..750d477 --- /dev/null +++ b/node_modules/is-plain-obj/package.json @@ -0,0 +1,68 @@ +{ + "_from": "is-plain-obj@^1.1", + "_id": "is-plain-obj@1.1.0", + "_inBundle": false, + "_integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "_location": "/is-plain-obj", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "is-plain-obj@^1.1", + "name": "is-plain-obj", + "escapedName": "is-plain-obj", + "rawSpec": "^1.1", + "saveSpec": null, + "fetchSpec": "^1.1" + }, + "_requiredBy": [ + "/merge-options" + ], + "_resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "_shasum": "71a50c8429dfca773c92a390a4a03b39fcd51d3e", + "_spec": "is-plain-obj@^1.1", + "_where": "/Users/jan/Development/nodejs/homecontrol-control-base/node_modules/merge-options", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/is-plain-obj/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Check if a value is a plain object", + "devDependencies": { + "ava": "0.0.4" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/is-plain-obj#readme", + "keywords": [ + "obj", + "object", + "is", + "check", + "test", + "type", + "plain", + "vanilla", + "pure", + "simple" + ], + "license": "MIT", + "name": "is-plain-obj", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/is-plain-obj.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.1.0" +} diff --git a/node_modules/is-plain-obj/readme.md b/node_modules/is-plain-obj/readme.md new file mode 100644 index 0000000..269e56a --- /dev/null +++ b/node_modules/is-plain-obj/readme.md @@ -0,0 +1,35 @@ +# is-plain-obj [![Build Status](https://travis-ci.org/sindresorhus/is-plain-obj.svg?branch=master)](https://travis-ci.org/sindresorhus/is-plain-obj) + +> Check if a value is a plain object + +An object is plain if it's created by either `{}`, `new Object()` or `Object.create(null)`. + + +## Install + +``` +$ npm install --save is-plain-obj +``` + + +## Usage + +```js +var isPlainObj = require('is-plain-obj'); + +isPlainObj({foo: 'bar'}); +//=> true + +isPlainObj([1, 2, 3]); +//=> false +``` + + +## Related + +- [is-obj](https://github.com/sindresorhus/is-obj) - Check if a value is an object + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/node_modules/merge-options/index.js b/node_modules/merge-options/index.js new file mode 100644 index 0000000..8eb954d --- /dev/null +++ b/node_modules/merge-options/index.js @@ -0,0 +1,163 @@ +'use strict'; +const isOptionObject = require('is-plain-obj'); + +const hasOwnProperty = Object.prototype.hasOwnProperty; +const propIsEnumerable = Object.propertyIsEnumerable; +const defineProperty = (obj, name, value) => Object.defineProperty(obj, name, { + value, + writable: true, + enumerable: true, + configurable: true +}); + +const globalThis = this; +const defaultMergeOpts = { + concatArrays: false +}; + +const getEnumerableOwnPropertyKeys = value => { + const keys = []; + + for (const key in value) { + if (hasOwnProperty.call(value, key)) { + keys.push(key); + } + } + + /* istanbul ignore else */ + if (Object.getOwnPropertySymbols) { + const symbols = Object.getOwnPropertySymbols(value); + + for (let i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(value, symbols[i])) { + keys.push(symbols[i]); + } + } + } + + return keys; +}; + +function clone(value) { + if (Array.isArray(value)) { + return cloneArray(value); + } + + if (isOptionObject(value)) { + return cloneOptionObject(value); + } + + return value; +} + +function cloneArray(array) { + const result = array.slice(0, 0); + + getEnumerableOwnPropertyKeys(array).forEach(key => { + defineProperty(result, key, clone(array[key])); + }); + + return result; +} + +function cloneOptionObject(obj) { + const result = Object.getPrototypeOf(obj) === null ? Object.create(null) : {}; + + getEnumerableOwnPropertyKeys(obj).forEach(key => { + defineProperty(result, key, clone(obj[key])); + }); + + return result; +} + +/** + * @param merged {already cloned} + * @return {cloned Object} + */ +const mergeKeys = (merged, source, keys, mergeOpts) => { + keys.forEach(key => { + // Do not recurse into prototype chain of merged + if (key in merged && merged[key] !== Object.getPrototypeOf(merged)) { + defineProperty(merged, key, merge(merged[key], source[key], mergeOpts)); + } else { + defineProperty(merged, key, clone(source[key])); + } + }); + + return merged; +}; + +/** + * @param merged {already cloned} + * @return {cloned Object} + * + * see [Array.prototype.concat ( ...arguments )](http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.concat) + */ +const concatArrays = (merged, source, mergeOpts) => { + let result = merged.slice(0, 0); + let resultIndex = 0; + + [merged, source].forEach(array => { + const indices = []; + + // `result.concat(array)` with cloning + for (let k = 0; k < array.length; k++) { + if (!hasOwnProperty.call(array, k)) { + continue; + } + + indices.push(String(k)); + + if (array === merged) { + // Already cloned + defineProperty(result, resultIndex++, array[k]); + } else { + defineProperty(result, resultIndex++, clone(array[k])); + } + } + + // Merge non-index keys + result = mergeKeys(result, array, getEnumerableOwnPropertyKeys(array).filter(key => { + return indices.indexOf(key) === -1; + }), mergeOpts); + }); + + return result; +}; + +/** + * @param merged {already cloned} + * @return {cloned Object} + */ +function merge(merged, source, mergeOpts) { + if (mergeOpts.concatArrays && Array.isArray(merged) && Array.isArray(source)) { + return concatArrays(merged, source, mergeOpts); + } + + if (!isOptionObject(source) || !isOptionObject(merged)) { + return clone(source); + } + + return mergeKeys(merged, source, getEnumerableOwnPropertyKeys(source), mergeOpts); +} + +module.exports = function () { + const mergeOpts = merge(clone(defaultMergeOpts), (this !== globalThis && this) || {}, defaultMergeOpts); + let merged = {foobar: {}}; + + for (let i = 0; i < arguments.length; i++) { + const option = arguments[i]; + + if (option === undefined) { + continue; + } + + if (!isOptionObject(option)) { + throw new TypeError('`' + option + '` is not an Option Object'); + } + + merged = merge(merged, {foobar: option}, mergeOpts); + } + + return merged.foobar; +}; diff --git a/node_modules/merge-options/license b/node_modules/merge-options/license new file mode 100644 index 0000000..527cbd8 --- /dev/null +++ b/node_modules/merge-options/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016-2018 Michael Mayer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/merge-options/package.json b/node_modules/merge-options/package.json new file mode 100644 index 0000000..35584fe --- /dev/null +++ b/node_modules/merge-options/package.json @@ -0,0 +1,75 @@ +{ + "_from": "merge-options", + "_id": "merge-options@1.0.1", + "_inBundle": false, + "_integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "_location": "/merge-options", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "merge-options", + "name": "merge-options", + "escapedName": "merge-options", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", + "_shasum": "2a64b24457becd4e4dc608283247e94ce589aa32", + "_spec": "merge-options", + "_where": "/Users/jan/Development/nodejs/homecontrol-control-base", + "author": { + "name": "Michael Mayer", + "email": "michael@schnittstabil.de", + "url": "schnittstabil.de" + }, + "bugs": { + "url": "https://github.com/schnittstabil/merge-options/issues" + }, + "bundleDependencies": false, + "dependencies": { + "is-plain-obj": "^1.1" + }, + "deprecated": false, + "description": "Merge Option Objects", + "devDependencies": { + "ava": "^0.25", + "coveralls": "^3.0", + "nyc": "^11.7", + "rimraf": "^2.5", + "xo": "^0.20" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/schnittstabil/merge-options#readme", + "keywords": [ + "merge", + "options", + "deep", + "plain", + "object", + "extend", + "clone" + ], + "license": "MIT", + "name": "merge-options", + "repository": { + "type": "git", + "url": "git+https://github.com/schnittstabil/merge-options.git" + }, + "scripts": { + "clean": "rimraf .nyc_output/ coverage/", + "coverage-html": "nyc ava && nyc report --reporter=html", + "test": "xo && nyc ava" + }, + "version": "1.0.1" +} diff --git a/node_modules/merge-options/readme.md b/node_modules/merge-options/readme.md new file mode 100644 index 0000000..c9c07c8 --- /dev/null +++ b/node_modules/merge-options/readme.md @@ -0,0 +1,102 @@ +# merge-options [![Build Status](https://travis-ci.org/schnittstabil/merge-options.svg?branch=master)](https://travis-ci.org/schnittstabil/merge-options) [![Coverage Status](https://coveralls.io/repos/schnittstabil/merge-options/badge.svg?branch=master&service=github)](https://coveralls.io/github/schnittstabil/merge-options?branch=master) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) + + +> Merge Option Objects + +`merge-options` considers [plain objects](https://github.com/sindresorhus/is-plain-obj) as *Option Objects*, everything else as *Option Values*. + +## Install + +``` +$ npm install --save merge-options +``` + +## Usage + +```js +const mergeOptions = require('merge-options'); + +mergeOptions({foo: 0}, {bar: 1}, {baz: 2}, {bar: 3}) +//=> {foo: 0, bar: 3, baz: 2} + +mergeOptions({nested: {unicorns: 'none'}}, {nested: {unicorns: 'many'}}) +//=> {nested: {unicorns: 'many'}} + +mergeOptions({[Symbol.for('key')]: 0}, {[Symbol.for('key')]: 42}) +//=> {Symbol(key): 42} +``` + +## API + +### mergeOptions(option1, ...options)
mergeOptions.call(config, option1, ...options)
mergeOptions.apply(config, [option1, ...options]) + +`mergeOptions` recursively merges one or more *Option Objects* into a new one and returns that. The `options` are merged in order, thus *Option Values* of additional `options` take precedence over previous ones. + +The merging does not alter the passed `option` arguments, taking roughly the following steps: +* recursively cloning[1] *Option Objects* and [arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) until reaching *Option Values* +* copying[1] references to *Option Values* to the result object + + +```js +const defaultOpts = { + fn: () => false, // functions are Option Values + promise: Promise.reject(new Error()), // all non-plain objects are Option Values + array: ['foo'], // arrays are Option Values + nested: {unicorns: 'none'} // {…} is plain, therefore an Option Object +}; + +const opts = { + fn: () => true, // [1] + promise: Promise.resolve('bar'), // [2] + array: ['baz'], // [3] + nested: {unicorns: 'many'} // [4] +}; + +mergeOptions(defaultOpts, opts) +//=> +{ + fn: [Function], // === [1] + promise: Promise { 'bar' }, // === [2] + array: ['baz'], // !== [3] (arrays are cloned) + nested: {unicorns: 'many'} // !== [4] (Option Objects are cloned) +} +``` + +#### config + +Type: `object` + +##### config.concatArrays + +Type: `boolean`
Default: `false` + +Concatenate arrays: + +```js +mergeOptions({src: ['src/**']}, {src: ['test/**']}) +//=> {src: ['test/**']} + +// Via call +mergeOptions.call({concatArrays: true}, {src: ['src/**']}, {src: ['test/**']}) +//=> {src: ['src/**', 'test/**']} + +// Via apply +mergeOptions.apply({concatArrays: true}, [{src: ['src/**']}, {src: ['test/**']}]) +//=> {src: ['src/**', 'test/**']} +``` + + +## Related + +* See [object-assign](https://github.com/sindresorhus/object-assign) if you need a ES2015 Object.assign() ponyfill +* See [deep-assign](https://github.com/sindresorhus/deep-assign) if you need to do Object.assign() recursively + +## Notes + +
    +
  1. copying and cloning take only enumerable own properties into account
  2. +
+ +## License + +MIT © [Michael Mayer](http://schnittstabil.de) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ecbaf21 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "homecontrol-control-base", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "merge-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", + "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "requires": { + "is-plain-obj": "1.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..23b7a50 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "homecontrol-control-base", + "version": "1.0.0", + "description": "Base class which all hc-controls inherit from", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git@git.literalchaos.de:jan/homecontrol-control-base.git" + }, + "author": "Jan Scheiper", + "license": "UNLICENSED", + "dependencies": { + "merge-options": "^1.0.1" + } +} diff --git a/states/BaseState.js b/states/BaseState.js new file mode 100644 index 0000000..8b96017 --- /dev/null +++ b/states/BaseState.js @@ -0,0 +1,18 @@ +class BaseState { + constructor(cloneObj) { + } + + get asObj() { + return {}; + } + + static get default() { + return {}; + } + + clone() { + return this(this.asObj); + } +} + +module.exports = BaseState; \ No newline at end of file diff --git a/states/ColorlampState.js b/states/ColorlampState.js new file mode 100644 index 0000000..2632897 --- /dev/null +++ b/states/ColorlampState.js @@ -0,0 +1,43 @@ +const mergeOptions = require('merge-options'); + +const BaseState = require('./BaseState'); + +class ColorlampState extends BaseState { + constructor(cloneObj) { + let cloneState = mergeOptions(ColorlampState.default, cloneObj); + + this.on = cloneState.on; + this.brightness = cloneState.brightness; + this.color = { + hue: cloneState.color.hue, + sat: cloneState.color.sat, + l: cloneState.color.l + }; + } + + static get default() { + return { + on: false, + brightness: 0, + color: { + hue: 0, + sat: 0, + l: 0 + } + }; + } + + get asObj() { + return { + on: this.on, + brightness: this.brightness, + color: { + hue: this.color.hue, + sat: this.color.sat, + l: this.color.l + } + }; + } +} + +module.exports = ColorlampState; \ No newline at end of file diff --git a/states/SwitchState.js b/states/SwitchState.js new file mode 100644 index 0000000..2ffd14c --- /dev/null +++ b/states/SwitchState.js @@ -0,0 +1,15 @@ +const BaseState = require('./BaseState'); + +class SwitchState extends BaseState { + constructor() { + this.on = false; + } + + get asObj() { + return { + on: this.on + }; + } +} + +module.exports = SwitchState; \ No newline at end of file