diff --git a/.eslintrc.json b/.eslintrc.json
index a59fcda..07e05be 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -13,7 +13,9 @@
"prettier"
],
"rules": {
- "prettier/prettier": "error",
+ "prettier/prettier": ["error", {
+ "endOfLine": "auto"
+ }],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-non-null-assertion": "off",
diff --git a/.github/workflows/wiki.yml b/.github/workflows/wiki.yml
index 9dffbf4..33fd042 100644
--- a/.github/workflows/wiki.yml
+++ b/.github/workflows/wiki.yml
@@ -15,7 +15,7 @@ jobs:
- run: npm ci
- run: npm run wiki
- name: Push Wiki Changes
- uses: Andrew-Chen-Wang/github-wiki-action@v3
+ uses: Andrew-Chen-Wang/github-wiki-action@v4
env:
WIKI_DIR: wiki/
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index faa2ca3..91567c2 100644
--- a/README.md
+++ b/README.md
@@ -4,25 +4,23 @@
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]
-**WARNING: this library is still under heavy construction**
-
Table of Contents
-
- About
+ About
-
- Example usage
+ Installation
-
- Installation
+ Example usage
-
- Docs
+ Colors
-
- Guides
+ Other
-
Roadmap
@@ -30,43 +28,15 @@
+> ⚠️ Library is under construction and may not work as expected
+
# About
hue.ts is a node module that allows you to easily interact with the hue API (V2).
- Object-oriented
- Written in TypeScript
-- Future 100%-coverage of the hue API
-
-# Docs
-
-Docs can be found on our wiki [here][wiki-url].
-
-# Example usage
-
-```js
-const { Bridge, ApiResourceType } = require('hue.ts');
-
-const bridge = new Bridge({
- connection: {
- ip: 'some-ip',
- applicationKey: 'some-key',
- },
-});
-
-bridge.on('ready', async () => {
- console.log('Ready!');
-
- const scene = bridge.resources.getByName('Best Scene Ever', {
- type: ApiResourceType.Scene,
- force: true,
- });
-
- await scene.recall({ duration: 5000 });
-});
-
-bridge.connect();
-```
+- Future 100%-coverage of the hue API (v2)
# Installation
@@ -77,25 +47,17 @@ the hue app, Settings -> My Hue system -> Select your bridge -> Software
npm install hue.ts
```
-# Guides
-
-## Connecting to a bridge
+# Example usage
-```ts
-import { Bridge } from 'hue.ts';
+This examples goal is to create a new zone, and adding a scene to this zone.
-const bridge = new Bridge({
- connection: {
- ip: 'some-ip',
- applicationKey: 'some-key',
- },
-});
+```shell
+npm install hue.ts
```
-The **some-ip** can be found in the Hue app at Settings -> My hue system -> Select your bridge -> IP
-
-You can get **some-key** by following the steps below:
-
+Before connecting to your hue bridge, its ip address and an application key are required.
+The ip address can be found in the Hue app at Settings -> My hue system -> Select your bridge -> IP
+After, an application key can be acquired by the following method:
1. Open your browser and go to `https:///debug/clip.html`
2. Next fill in the options below:
URL: /api
@@ -107,192 +69,120 @@ You can get **some-key** by following the steps below:
For more information on retrieving this key visit: https://developers.meethue.com/develop/hue-api-v2/getting-started/
-Finally, connect to your bridge by calling `bridge.connect()`.
-
-After the bridge has successfully connected and cached all resources the **ready** event is emitted. You can also listen to other events which can be found [here](https://github.com/S222em/hue.ts/wiki/BridgeEvents).
-
```ts
-import { Bridge, Events } from 'hue.ts';
+import { Hue, ArcheType, SceneAction } from 'hue.ts';
-const bridge = new Bridge({
+// Create new Hue, with the ip address and application key
+const hue = new Hue({
connection: {
ip: 'some-ip',
applicationKey: 'some-key',
},
});
-bridge.on(Events.Ready, () => {
- // Do stuff
+// Listen to the 'ready' event, which is emitted when the socket has connected and cached all resources
+hue.on('ready', async () => {
+ // Get the lights we want in the new zone
+ const light1 = hue.lights.cache.find((light) => light.name == 'Demo Light 1')!;
+ const light2 = hue.lights.cache.find((light) => light.name == 'Demo Light 2')!;
+
+ // Create the zone
+ await hue.zones.create({
+ name: 'Demo',
+ archeType: ArcheType.ManCave,
+ children: [light1.id, light2.id],
+ });
});
-bridge.connect();
-```
-
-### Connecting via Cloud2Cloud
+// Listen to the 'zoneAdd' event, emitted on creation of a new zone
+hue.on('zoneAdd', async (zone) => {
+ // Ignore if the zone is not the one created above
+ if (zone.name !== 'Demo') return;
-> ⚠️ **It is not advised to use Cloud2Cloud at this time**
+ // Find the lights belonging to the zone again, in this case these will be Demo Light 1 & Demo Light 2
+ const lights = hue.lights.cache.filter((light) => zone.childIds.includes(light.id));
-```ts
-const bridge = new Bridge({
- connection: {
- accessToken: 'some-token',
- applicationKey: 'some-key',
- },
-});
-```
+ // Make the actions
+ const actions = lights.map((light) => {
+ const action: SceneAction = {
+ id: light.id,
+ on: true,
+ };
-To find the **accessToken** and **applicationKey** visit https://developers.meethue.com/develop/hue-api-v2/cloud2cloud-getting-started/
+ // Check if the light can do dimming, and if so, set the brightness of the light to 50%
+ if (light.isCapableOfDimming()) action.brightness = 50;
-## Resources
+ // Check if the light can display color, and if so, set the color to #eb403
+ if (light.isCapableOfColor()) action.color = fromHex('#eb403');
-All the resources belonging to the connect bridge are cached after calling `bridge.connect()`.
-You can access resources in the cache with the following methods.
-
-Can be used to find a resource by its unique ID.
-```ts
-const resource = bridge.resources.getById('some-id');
-```
+ return action;
+ });
-Can be used to find a resource by its identifier, similar to an ID, but an identifier also includes the type of the resource.
-```ts
-const resource = bridge.resources.getByIdentifier({
- rid: 'some-id',
- rtype: 'some-type',
+ // Create the scene
+ await zone.createScene({
+ name: 'Awesome scene',
+ actions,
+ });
});
-```
-
-Find multiple resources by identifier.
-```ts
-const resource = bridge.resources.getByIdentifiers([
- {
- rid: 'some-id', rtype: 'some-type',
- },
- {
- rid: 'some-id', rtype: 'some-type',
- }
-]);
-```
-> ⚠️ **.getByName will return the first occurrence**
+// Listen to the sceneAdd event, emitted on creation of a new scene
+hue.on('sceneAdd', async (scene) => {
+ // Ignore if not the scene just created above
+ if (scene.name !== 'Awesome scene') return;
-Find a resource by its name.
-```ts
-const resource = bridge.resources.getByName('some-name');
-```
-
-These methods include additional options as second parameter:
-- **force**, if true, error is thrown if resource does not exist.
-- **type**, the type of the resource, will not return resources of other types.
-
-Here is an example:
-```ts
-const resource = bridge.resources.getById('some-id', {
- type: ApiResourceType.Light,
- force: true,
+ // Recall the scene
+ await scene.recall();
});
```
+# Colors
-## Lights
+Colors... get more complicated. The hue system uses the C.I.E. color representation. This representation is a 2D-colored diagram.
+This also means, to get a color of this diagram, a coordinate (position on the horizontal and vertical axes) is needed.
+A color is therefor represented as `{ x: number; y: number }`. Where x is the horizontal placement and y the vertical.
-### On/Off
+![cie-url]
+As this is a sort of 'non-standard' color representation, utility functions are provided to convert a rgb/hex value to C.I.E.
```ts
-light.isOn();
+const xy = fromHex('#eb4034');
-await light.off();
-await light.on();
-await light.toggle();
-await light.edit({
- on: true,
-});
+await light.setColor(xy);
```
-
-### Brightness
-
```ts
-// Number between 1%-100%
-light.brightness
-
-await light.setBrightness(50);
-await light.edit({
- brightness: 50,
-});
-```
-
-### Color temperature
-
-```ts
-// Number between 153-500
-light.mirek
-
-await light.setMirek(300);
-await light.edit({
- mirek: 300,
+const xy = fromRGB({
+ red: 235,
+ green: 64,
+ blue: 52,
});
-```
-### Color
-
-> ⚠️ **Given color is always transpiled to the closest point in the lights display range**
-
-```ts
-// Number between 153-500
-light.xy
-
-await light.setXy({
- x: 0.3,
- y: 0.5,
-})
-await light.edit({
- xy: {
- x: 0.3,
- y: 0.5,
- },
-});
+await light.setColor(xy);
```
-
-To convert hex or rgb to a xy value the following utility functions can be used:
```ts
-const xy = fromHex('#e62d20');
-const hex = toHex(xy);
-
-await light.setXy(fromHex('#e62d20'));
+const hex = toHex(light.color);
```
```ts
-const xy = fromRGB({
- red: 230,
- green: 45,
- blue: 32,
-});
-const rgb = toRGB(xy);
-
-await light.setXy(fromRGB({
- red: 230,
- green: 45,
- blue: 32,
-}));
+const rgb = toRGB(light.color);
```
-### Type Guards
-
-Not all lights support color or color temperature. To make sure a light supports these, type guards are available
-
+As also visible in the C.I.E. above, defined by the triangles,
+there is a limit to what the light is able to display.
+Because of that, there might be a difference of your input and the color of the light.
+In order to calculate the color the light is currently showing, the following method can be used.
```ts
-light.isCapableOfDimming()
-light.isCapableOfMirek()
-light.isCapableOfXy()
-light.isCapableOfXys()
+const xy = light.colorToRange(light.color);
+
+const hex = toHex(xy);
```
-## Other resources
+# Links
-Not all resources are currently supported. Although most important ones are. Guides on these resources will be added later. For now reference to [documentation](https://github.com/S222em/hue.ts/wiki).
-For further assistance, feel free to open up an issue on our GitHub.
+- [Documentation](documentation-url)
+- [GitHub](github-url)
+- [npm](npm-url)
-# Roadmap
+# Help
-This library is still a work in progress, and I plan to add support for all API features, currently focusing on the structure of the library itself. The Hue API V2 is currently
-not in a finished state yet, so many features **can't** be implemented yet.
+For questions or issues, please open an issue on our [github](issues-url) page.
[contributors-shield]: https://img.shields.io/github/contributors/S222em/hue.js.svg?style=for-the-badge
[contributors-url]: https://github.com/S222em/hue.js/graphs/contributors
@@ -304,4 +194,7 @@ not in a finished state yet, so many features **can't** be implemented yet.
[issues-url]: https://github.com/S222em/hue.js/issues
[license-shield]: https://img.shields.io/github/license/S222em/hue.js.svg?style=for-the-badge
[license-url]: https://github.com/S222em/hue.js/blob/master/LICENSE.txt
-[wiki-url]: https://github.com/S222em/hue.ts/wiki
+[cie-url]: https://developers.meethue.com/wp-content/uploads/2018/02/color.png
+[documentation-url]: https://github.com/S222em/hue.ts/wiki/Exports
+[github-url]: https://github.com/S222em/hue.ts
+[npm-url]: https://www.npmjs.com/package/hue.ts
diff --git a/package-lock.json b/package-lock.json
index 45a821b..7cf2606 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,18 +15,18 @@
},
"devDependencies": {
"@types/node": "20.1.4",
- "@typescript-eslint/eslint-plugin": "5.59.6",
- "@typescript-eslint/parser": "5.59.6",
- "eslint": "8.40.0",
+ "@typescript-eslint/eslint-plugin": "5.59.11",
+ "@typescript-eslint/parser": "5.59.11",
+ "eslint": "8.43.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-prettier": "4.2.1",
"prettier": "2.8.8",
"ts-node": "10.9.1",
- "tslib": "2.5.0",
- "typedoc": "0.24.7",
+ "tslib": "2.5.3",
+ "typedoc": "0.24.8",
"typedoc-github-wiki-theme": "1.1.0",
"typedoc-plugin-markdown": "3.15.3",
- "typescript": "5.0.4"
+ "typescript": "5.1.3"
},
"engines": {
"node": ">=16.6.0",
@@ -101,18 +101,18 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz",
- "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==",
+ "version": "8.43.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz",
+ "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.8",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
- "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
+ "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
@@ -236,9 +236,9 @@
"dev": true
},
"node_modules/@types/json-schema": {
- "version": "7.0.11",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
- "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
+ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==",
"dev": true
},
"node_modules/@types/node": {
@@ -254,15 +254,15 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.6.tgz",
- "integrity": "sha512-sXtOgJNEuRU5RLwPUb1jxtToZbgvq3M6FPpY4QENxoOggK+UpTxUBpj6tD8+Qh2g46Pi9We87E+eHnUw8YcGsw==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz",
+ "integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.4.0",
- "@typescript-eslint/scope-manager": "5.59.6",
- "@typescript-eslint/type-utils": "5.59.6",
- "@typescript-eslint/utils": "5.59.6",
+ "@typescript-eslint/scope-manager": "5.59.11",
+ "@typescript-eslint/type-utils": "5.59.11",
+ "@typescript-eslint/utils": "5.59.11",
"debug": "^4.3.4",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
@@ -288,14 +288,14 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.6.tgz",
- "integrity": "sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz",
+ "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==",
"dev": true,
"dependencies": {
- "@typescript-eslint/scope-manager": "5.59.6",
- "@typescript-eslint/types": "5.59.6",
- "@typescript-eslint/typescript-estree": "5.59.6",
+ "@typescript-eslint/scope-manager": "5.59.11",
+ "@typescript-eslint/types": "5.59.11",
+ "@typescript-eslint/typescript-estree": "5.59.11",
"debug": "^4.3.4"
},
"engines": {
@@ -315,13 +315,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.6.tgz",
- "integrity": "sha512-gLbY3Le9Dxcb8KdpF0+SJr6EQ+hFGYFl6tVY8VxLPFDfUZC7BHFw+Vq7bM5lE9DwWPfx4vMWWTLGXgpc0mAYyQ==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz",
+ "integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "5.59.6",
- "@typescript-eslint/visitor-keys": "5.59.6"
+ "@typescript-eslint/types": "5.59.11",
+ "@typescript-eslint/visitor-keys": "5.59.11"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -332,13 +332,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.6.tgz",
- "integrity": "sha512-A4tms2Mp5yNvLDlySF+kAThV9VTBPCvGf0Rp8nl/eoDX9Okun8byTKoj3fJ52IJitjWOk0fKPNQhXEB++eNozQ==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz",
+ "integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==",
"dev": true,
"dependencies": {
- "@typescript-eslint/typescript-estree": "5.59.6",
- "@typescript-eslint/utils": "5.59.6",
+ "@typescript-eslint/typescript-estree": "5.59.11",
+ "@typescript-eslint/utils": "5.59.11",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
@@ -359,9 +359,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.6.tgz",
- "integrity": "sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz",
+ "integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -372,13 +372,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.6.tgz",
- "integrity": "sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz",
+ "integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "5.59.6",
- "@typescript-eslint/visitor-keys": "5.59.6",
+ "@typescript-eslint/types": "5.59.11",
+ "@typescript-eslint/visitor-keys": "5.59.11",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -399,17 +399,17 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.6.tgz",
- "integrity": "sha512-vzaaD6EXbTS29cVH0JjXBdzMt6VBlv+hE31XktDRMX1j3462wZCJa7VzO2AxXEXcIl8GQqZPcOPuW/Z1tZVogg==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz",
+ "integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
- "@typescript-eslint/scope-manager": "5.59.6",
- "@typescript-eslint/types": "5.59.6",
- "@typescript-eslint/typescript-estree": "5.59.6",
+ "@typescript-eslint/scope-manager": "5.59.11",
+ "@typescript-eslint/types": "5.59.11",
+ "@typescript-eslint/typescript-estree": "5.59.11",
"eslint-scope": "^5.1.1",
"semver": "^7.3.7"
},
@@ -425,12 +425,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "5.59.6",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.6.tgz",
- "integrity": "sha512-zEfbFLzB9ETcEJ4HZEEsCR9HHeNku5/Qw1jSS5McYJv5BR+ftYXwFFAH5Al+xkGaZEqowMwl7uoJjQb1YSPF8Q==",
+ "version": "5.59.11",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz",
+ "integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "5.59.6",
+ "@typescript-eslint/types": "5.59.11",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@@ -715,16 +715,16 @@
}
},
"node_modules/eslint": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz",
- "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==",
+ "version": "8.43.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz",
+ "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.3",
- "@eslint/js": "8.40.0",
- "@humanwhocodes/config-array": "^0.11.8",
+ "@eslint/js": "8.43.0",
+ "@humanwhocodes/config-array": "^0.11.10",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.10.0",
@@ -743,13 +743,12 @@
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"globals": "^13.19.0",
- "grapheme-splitter": "^1.0.4",
+ "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
@@ -1130,6 +1129,12 @@
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true
},
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
"node_modules/handlebars": {
"version": "4.7.7",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
@@ -1255,16 +1260,6 @@
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
- "node_modules/js-sdsl": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
- "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==",
- "dev": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/js-sdsl"
- }
- },
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -1842,9 +1837,9 @@
}
},
"node_modules/tslib": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
- "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
+ "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==",
"dev": true
},
"node_modules/tsutils": {
@@ -1893,9 +1888,9 @@
}
},
"node_modules/typedoc": {
- "version": "0.24.7",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.7.tgz",
- "integrity": "sha512-zzfKDFIZADA+XRIp2rMzLe9xZ6pt12yQOhCr7cD7/PBTjhPmMyMvGrkZ2lPNJitg3Hj1SeiYFNzCsSDrlpxpKw==",
+ "version": "0.24.8",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz",
+ "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==",
"dev": true,
"dependencies": {
"lunr": "^2.3.9",
@@ -1910,7 +1905,7 @@
"node": ">= 14.14"
},
"peerDependencies": {
- "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x"
+ "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x"
}
},
"node_modules/typedoc-github-wiki-theme": {
@@ -1945,9 +1940,9 @@
}
},
"node_modules/typedoc/node_modules/minimatch": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz",
- "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
+ "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -1960,16 +1955,16 @@
}
},
"node_modules/typescript": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
- "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
+ "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
- "node": ">=12.20"
+ "node": ">=14.17"
}
},
"node_modules/uglify-js": {
diff --git a/package.json b/package.json
index 74d49d5..5eaa5cb 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
{
"name": "hue.ts",
- "version": "0.5.0",
+ "version": "0.6.0",
"description": "A powerful library to interact with the Hue API",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": "https://github.com/S222em/hue.ts",
- "homepage": "https://github.com/S222em/hue.ts/wiki",
+ "homepage": "https://github.com/S222em/hue.ts#readme",
"bugs": {
"url": "https://github.com/S222em/hue.ts/issues"
},
@@ -20,7 +20,7 @@
"Hue"
],
"author": "S222em",
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
"@discordjs/collection": "1.5.1",
"@sapphire/async-queue": "1.5.0",
@@ -28,18 +28,18 @@
},
"devDependencies": {
"@types/node": "20.1.4",
- "@typescript-eslint/eslint-plugin": "5.59.6",
- "@typescript-eslint/parser": "5.59.6",
- "eslint": "8.40.0",
+ "@typescript-eslint/eslint-plugin": "5.59.11",
+ "@typescript-eslint/parser": "5.59.11",
+ "eslint": "8.43.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-prettier": "4.2.1",
"prettier": "2.8.8",
"ts-node": "10.9.1",
- "tslib": "2.5.0",
- "typedoc": "0.24.7",
+ "tslib": "2.5.3",
+ "typedoc": "0.24.8",
"typedoc-github-wiki-theme": "1.1.0",
"typedoc-plugin-markdown": "3.15.3",
- "typescript": "5.0.4"
+ "typescript": "5.1.3"
},
"engines": {
"node": ">=16.6.0",
diff --git a/src/api/ApiResourceType.ts b/src/api/ApiResourceType.ts
deleted file mode 100644
index 7428eff..0000000
--- a/src/api/ApiResourceType.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import { ApiLightGet } from './light/get';
-import { ApiLightPut } from './light/put';
-import { ApiSceneGet } from './scene/get';
-import { ApiScenePut } from './scene/put';
-import { ApiScenePost } from './scene/post';
-import { ApiRoomPost } from './room/post';
-import { ApiZonePost } from './zone/post';
-import { ApiRoomPut } from './room/put';
-import { ApiZonePut } from './zone/put';
-import { ApiRoomGet } from './room/get';
-import { ApiZoneGet } from './zone/get';
-import { ApiDeviceGet } from './device/get';
-import { ApiDevicePut } from './device/put';
-import { ApiGroupedLightGet } from './grouped_light/get';
-import { ApiGroupedLightPut } from './grouped_light/put';
-
-export enum ApiResourceType {
- Device = 'device',
- BridgeHome = 'bridge_home',
- Room = 'room',
- Zone = 'zone',
- Light = 'light',
- Button = 'button',
- Temperature = 'temperature',
- LightLevel = 'light_level',
- Motion = 'motion',
- Entertainment = 'entertainment',
- GroupedLight = 'grouped_light',
- DevicePower = 'device_power',
- ZigbeeBridgeConnectivity = 'zigbee_bridge_connectivity',
- ZgpConnectivity = 'zgp_connectivity',
- Bridge = 'bridge',
- Homekit = 'homekit',
- Scene = 'scene',
- EntertainmentConfiguration = 'entertainment_configuration',
- PublicImage = 'public_image',
- BehaviourScript = 'behaviour_script',
- BehaviourInstance = 'behaviour_instance',
- Geofence = 'geofence',
- GeofenceClient = 'geofence_client',
- Geolocation = 'geolocation',
-}
-
-export interface ApiResourceTypesGet {
- [ApiResourceType.Device]: ApiDeviceGet;
- [ApiResourceType.BridgeHome]: never;
- [ApiResourceType.Room]: ApiRoomGet;
- [ApiResourceType.Zone]: ApiZoneGet;
- [ApiResourceType.Light]: ApiLightGet;
- [ApiResourceType.Button]: never;
- [ApiResourceType.Temperature]: never;
- [ApiResourceType.LightLevel]: never;
- [ApiResourceType.Motion]: never;
- [ApiResourceType.Entertainment]: never;
- [ApiResourceType.GroupedLight]: ApiGroupedLightGet;
- [ApiResourceType.DevicePower]: never;
- [ApiResourceType.ZigbeeBridgeConnectivity]: never;
- [ApiResourceType.ZgpConnectivity]: never;
- [ApiResourceType.Bridge]: never;
- [ApiResourceType.Homekit]: never;
- [ApiResourceType.Scene]: ApiSceneGet;
- [ApiResourceType.EntertainmentConfiguration]: never;
- [ApiResourceType.PublicImage]: never;
- [ApiResourceType.BehaviourScript]: never;
- [ApiResourceType.BehaviourInstance]: never;
- [ApiResourceType.Geofence]: never;
- [ApiResourceType.GeofenceClient]: never;
- [ApiResourceType.Geolocation]: never;
-}
-
-export type ApiResourceTypeGet = ApiResourceTypesGet[T];
-
-export interface ApiResourceTypesPut {
- [ApiResourceType.Device]: ApiDevicePut;
- [ApiResourceType.BridgeHome]: never;
- [ApiResourceType.Room]: ApiRoomPut;
- [ApiResourceType.Zone]: ApiZonePut;
- [ApiResourceType.Light]: ApiLightPut;
- [ApiResourceType.Button]: never;
- [ApiResourceType.Temperature]: never;
- [ApiResourceType.LightLevel]: never;
- [ApiResourceType.Motion]: never;
- [ApiResourceType.Entertainment]: never;
- [ApiResourceType.GroupedLight]: ApiGroupedLightPut;
- [ApiResourceType.DevicePower]: never;
- [ApiResourceType.ZigbeeBridgeConnectivity]: never;
- [ApiResourceType.ZgpConnectivity]: never;
- [ApiResourceType.Bridge]: never;
- [ApiResourceType.Homekit]: never;
- [ApiResourceType.Scene]: ApiScenePut;
- [ApiResourceType.EntertainmentConfiguration]: never;
- [ApiResourceType.PublicImage]: never;
- [ApiResourceType.BehaviourScript]: never;
- [ApiResourceType.BehaviourInstance]: never;
- [ApiResourceType.Geofence]: never;
- [ApiResourceType.GeofenceClient]: never;
- [ApiResourceType.Geolocation]: never;
-}
-
-export type ApiResourceTypePut = ApiResourceTypesPut[T];
-
-export interface ApiResourceTypesPost {
- [ApiResourceType.Device]: never;
- [ApiResourceType.BridgeHome]: never;
- [ApiResourceType.Room]: ApiRoomPost;
- [ApiResourceType.Zone]: ApiZonePost;
- [ApiResourceType.Light]: never;
- [ApiResourceType.Button]: never;
- [ApiResourceType.Temperature]: never;
- [ApiResourceType.LightLevel]: never;
- [ApiResourceType.Motion]: never;
- [ApiResourceType.Entertainment]: never;
- [ApiResourceType.GroupedLight]: never;
- [ApiResourceType.DevicePower]: never;
- [ApiResourceType.ZigbeeBridgeConnectivity]: never;
- [ApiResourceType.ZgpConnectivity]: never;
- [ApiResourceType.Bridge]: never;
- [ApiResourceType.Homekit]: never;
- [ApiResourceType.Scene]: ApiScenePost;
- [ApiResourceType.EntertainmentConfiguration]: never;
- [ApiResourceType.PublicImage]: never;
- [ApiResourceType.BehaviourScript]: never;
- [ApiResourceType.BehaviourInstance]: never;
- [ApiResourceType.Geofence]: never;
- [ApiResourceType.GeofenceClient]: never;
- [ApiResourceType.Geolocation]: never;
-}
-
-export type ApiResourceTypePost = ApiResourceTypesPost[T];
diff --git a/src/api/ArcheType.ts b/src/api/ArcheType.ts
new file mode 100644
index 0000000..cc686ba
--- /dev/null
+++ b/src/api/ArcheType.ts
@@ -0,0 +1,91 @@
+export enum ArcheType {
+ Unknown = 'unknown_archetype',
+ ClassicBulb = 'classic_bulb',
+ SultanBulb = 'sultan_bulb',
+ FloodBulb = 'flood_bulb',
+ SpotBulb = 'spot_bulb',
+ CandleBulb = 'candle_bulb',
+ LusterBulb = 'luster_bulb',
+ PendantRound = 'pendant_round',
+ PendantLong = 'pendant_long',
+ CeilingRound = 'ceiling_round',
+ CeilingSquare = 'ceiling_square',
+ FloorShade = 'floor_shade',
+ FloorLantern = 'floor_lantern',
+ TableShade = 'table_shade',
+ RecessedCeiling = 'recessed_ceiling',
+ RecessedFloor = 'recessed_floor',
+ SingleSpot = 'single_spot',
+ DoubleSpot = 'double_spot',
+ TableWash = 'table_wash',
+ WallLantern = 'wall_lantern',
+ WallShade = 'wall_shade',
+ FlexibleLamp = 'flexible_lamp',
+ GroundSpot = 'ground_spot',
+ WallSpot = 'wall_spot',
+ Plug = 'plug',
+ HueGo = 'hue_go',
+ HueLightstrip = 'hue_lightstrip',
+ HueIris = 'hue_iris',
+ HueBloom = 'hue_bloom',
+ Bollard = 'bollard',
+ WallWasher = 'wall_washer',
+ HuePlay = 'hue_play',
+ VintageBulb = 'vintage_bulb',
+ VintageCandleBulb = 'vintage_candle_bulb',
+ EllipseBulb = 'ellipse_bulb',
+ TriangleBulb = 'triangle_bulb',
+ SmallGlobeBulb = 'small_globe_bulb',
+ LargeGlobeBulb = 'large_globe_bulb',
+ EdisonBulb = 'edison_bulb',
+ ChristmasTree = 'christmas_tree',
+ StringLight = 'string_light',
+ HueCentris = 'hue_centris',
+ HueLightstripTV = 'hue_lightstrip_tv',
+ HueLightStripPC = 'hue_lightstrip_pc',
+ HueTube = 'hue_tube',
+ HueSigne = 'hue_signe',
+ PendantSpot = 'pendant_spot',
+ CeilingHorizontal = 'ceiling_horizontal',
+ CeilingTube = 'ceiling_tube',
+ LivingRoom = 'living_room',
+ Kitchen = 'kitchen',
+ Dining = 'dining',
+ Bedroom = 'bedroom',
+ KidsBedroom = 'kids_bedroom',
+ Bathroom = 'bathroom',
+ Nursery = 'nursery',
+ Recreation = 'recreation',
+ Office = 'office',
+ Gym = 'gym',
+ Hallway = 'hallway',
+ Toilet = 'toilet',
+ FrontDoor = 'front_door',
+ Garage = 'garage',
+ Terrace = 'terrace',
+ Garden = 'garden',
+ Driveway = 'driveway',
+ Carport = 'carport',
+ Home = 'home',
+ Downstairs = 'downstairs',
+ Upstairs = 'upstairs',
+ TopFloor = 'top_floor',
+ Attic = 'attic',
+ GuestRoom = 'guest_room',
+ Staircase = 'staircase',
+ Lounge = 'lounge',
+ ManCave = 'man_cave',
+ Computer = 'computer',
+ Studio = 'studio',
+ Music = 'music',
+ TV = 'tv',
+ Reading = 'reading',
+ Closet = 'closet',
+ Storage = 'storage',
+ LaundryRoom = 'laundry_room',
+ Balcony = 'balcony',
+ Porch = 'porch',
+ Barbecue = 'barbecue',
+ Pool = 'pool',
+ Other = 'other',
+}
diff --git a/src/api/ResourceIdentifier.ts b/src/api/ResourceIdentifier.ts
index 6753c0d..6374d7c 100644
--- a/src/api/ResourceIdentifier.ts
+++ b/src/api/ResourceIdentifier.ts
@@ -1,6 +1,6 @@
-import { ApiResourceType } from './ApiResourceType';
+import { ResourceType } from './ResourceType';
-export interface ResourceIdentifier {
+export interface ResourceIdentifier {
rid: string;
rtype: T;
}
diff --git a/src/api/ResourceType.ts b/src/api/ResourceType.ts
new file mode 100644
index 0000000..f71fa0a
--- /dev/null
+++ b/src/api/ResourceType.ts
@@ -0,0 +1,141 @@
+import { DeviceGet } from './get/DeviceGet';
+import { BridgeHomeGet } from './get/BridgeHomeGet';
+import { RoomGet } from './get/RoomGet';
+import { ZoneGet } from './get/ZoneGet';
+import { LightGet } from './get/LightGet';
+import { MotionGet } from './get/MotionGet';
+import { GroupedLightGet } from './get/GroupedLightGet';
+import { DevicePowerGet } from './get/DevicePowerGet';
+import { SceneGet } from './get/SceneGet';
+import { DevicePut } from './put/DevicePut';
+import { RoomPut } from './put/RoomPut';
+import { ZonePut } from './put/ZonePut';
+import { LightPut } from './put/LightPut';
+import { MotionPut } from './put/MotionPut';
+import { GroupedLightPut } from './put/GroupedLightPut';
+import { ScenePut } from './put/ScenePut';
+import { RoomPost } from './post/RoomPost';
+import { ZonePost } from './post/ZonePost';
+import { ScenePost } from './post/ScenePost';
+import { ZigbeeConnectivityGet } from './get/ZigbeeConnectivityGet';
+import { ZigbeeDeviceDiscoveryGet } from './get/ZigbeeDeviceDiscoveryGet';
+import { ZigbeeDeviceDiscoveryPut } from './put/ZigbeeDeviceDiscoveryPut';
+import { BridgeGet } from './get/BridgeGet';
+
+export enum ResourceType {
+ Device = 'device',
+ BridgeHome = 'bridge_home',
+ Room = 'room',
+ Zone = 'zone',
+ Light = 'light',
+ Button = 'button',
+ Temperature = 'temperature',
+ LightLevel = 'light_level',
+ Motion = 'motion',
+ Entertainment = 'entertainment',
+ GroupedLight = 'grouped_light',
+ DevicePower = 'device_power',
+ ZigbeeConnectivity = 'zigbee_connectivity',
+ ZgpConnectivity = 'zgp_connectivity',
+ ZigbeeDeviceDiscovery = 'zigbee_device_discovery',
+ Bridge = 'bridge',
+ Homekit = 'homekit',
+ Scene = 'scene',
+ EntertainmentConfiguration = 'entertainment_configuration',
+ PublicImage = 'public_image',
+ BehaviourScript = 'behaviour_script',
+ BehaviourInstance = 'behaviour_instance',
+ Geofence = 'geofence',
+ GeofenceClient = 'geofence_client',
+ Geolocation = 'geolocation',
+}
+
+export interface ResourceTypesGet {
+ [ResourceType.Device]: DeviceGet;
+ [ResourceType.BridgeHome]: BridgeHomeGet;
+ [ResourceType.Room]: RoomGet;
+ [ResourceType.Zone]: ZoneGet;
+ [ResourceType.Light]: LightGet;
+ [ResourceType.Button]: never;
+ [ResourceType.Temperature]: never;
+ [ResourceType.LightLevel]: never;
+ [ResourceType.Motion]: MotionGet;
+ [ResourceType.Entertainment]: never;
+ [ResourceType.GroupedLight]: GroupedLightGet;
+ [ResourceType.DevicePower]: DevicePowerGet;
+ [ResourceType.ZigbeeConnectivity]: ZigbeeConnectivityGet;
+ [ResourceType.ZgpConnectivity]: never;
+ [ResourceType.ZigbeeDeviceDiscovery]: ZigbeeDeviceDiscoveryGet;
+ [ResourceType.Bridge]: BridgeGet;
+ [ResourceType.Homekit]: never;
+ [ResourceType.Scene]: SceneGet;
+ [ResourceType.EntertainmentConfiguration]: never;
+ [ResourceType.PublicImage]: never;
+ [ResourceType.BehaviourScript]: never;
+ [ResourceType.BehaviourInstance]: never;
+ [ResourceType.Geofence]: never;
+ [ResourceType.GeofenceClient]: never;
+ [ResourceType.Geolocation]: never;
+}
+
+export type ResourceTypeGet = ResourceTypesGet[T];
+
+export interface ResourceTypesPut {
+ [ResourceType.Device]: DevicePut;
+ [ResourceType.BridgeHome]: never;
+ [ResourceType.Room]: RoomPut;
+ [ResourceType.Zone]: ZonePut;
+ [ResourceType.Light]: LightPut;
+ [ResourceType.Button]: never;
+ [ResourceType.Temperature]: never;
+ [ResourceType.LightLevel]: never;
+ [ResourceType.Motion]: MotionPut;
+ [ResourceType.Entertainment]: never;
+ [ResourceType.GroupedLight]: GroupedLightPut;
+ [ResourceType.DevicePower]: never;
+ [ResourceType.ZigbeeConnectivity]: never;
+ [ResourceType.ZgpConnectivity]: never;
+ [ResourceType.ZigbeeDeviceDiscovery]: ZigbeeDeviceDiscoveryPut;
+ [ResourceType.Bridge]: never;
+ [ResourceType.Homekit]: never;
+ [ResourceType.Scene]: ScenePut;
+ [ResourceType.EntertainmentConfiguration]: never;
+ [ResourceType.PublicImage]: never;
+ [ResourceType.BehaviourScript]: never;
+ [ResourceType.BehaviourInstance]: never;
+ [ResourceType.Geofence]: never;
+ [ResourceType.GeofenceClient]: never;
+ [ResourceType.Geolocation]: never;
+}
+
+export type ResourceTypePut = ResourceTypesPut[T];
+
+export interface ResourceTypesPost {
+ [ResourceType.Device]: never;
+ [ResourceType.BridgeHome]: never;
+ [ResourceType.Room]: RoomPost;
+ [ResourceType.Zone]: ZonePost;
+ [ResourceType.Light]: never;
+ [ResourceType.Button]: never;
+ [ResourceType.Temperature]: never;
+ [ResourceType.LightLevel]: never;
+ [ResourceType.Motion]: never;
+ [ResourceType.Entertainment]: never;
+ [ResourceType.GroupedLight]: never;
+ [ResourceType.DevicePower]: never;
+ [ResourceType.ZigbeeConnectivity]: never;
+ [ResourceType.ZgpConnectivity]: never;
+ [ResourceType.ZigbeeDeviceDiscovery]: never;
+ [ResourceType.Bridge]: never;
+ [ResourceType.Homekit]: never;
+ [ResourceType.Scene]: ScenePost;
+ [ResourceType.EntertainmentConfiguration]: never;
+ [ResourceType.PublicImage]: never;
+ [ResourceType.BehaviourScript]: never;
+ [ResourceType.BehaviourInstance]: never;
+ [ResourceType.Geofence]: never;
+ [ResourceType.GeofenceClient]: never;
+ [ResourceType.Geolocation]: never;
+}
+
+export type ResourceTypePost = ResourceTypesPost[T];
diff --git a/src/api/device/put.ts b/src/api/device/put.ts
deleted file mode 100644
index f41bc5f..0000000
--- a/src/api/device/put.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { ApiResourceType } from '../ApiResourceType';
-
-export interface ApiDevicePut {
- type?: ApiResourceType.Device;
- metadata?: {
- archetype?: string;
- name?: string;
- };
- identify?: {
- action: 'identify';
- };
-}
diff --git a/src/api/get/BridgeGet.ts b/src/api/get/BridgeGet.ts
new file mode 100644
index 0000000..85bd6b1
--- /dev/null
+++ b/src/api/get/BridgeGet.ts
@@ -0,0 +1,12 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+
+export interface BridgeGet {
+ type?: ResourceType.Bridge;
+ id: string;
+ owner: ResourceIdentifier;
+ bridge_id: string;
+ time_zone: {
+ time_zone: string;
+ };
+}
diff --git a/src/api/get/BridgeHomeGet.ts b/src/api/get/BridgeHomeGet.ts
new file mode 100644
index 0000000..5ab2988
--- /dev/null
+++ b/src/api/get/BridgeHomeGet.ts
@@ -0,0 +1,9 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+
+export interface BridgeHomeGet {
+ type?: ResourceType.BridgeHome;
+ id: string;
+ children: ResourceIdentifier[];
+ services: ResourceIdentifier[];
+}
diff --git a/src/api/device/get.ts b/src/api/get/DeviceGet.ts
similarity index 61%
rename from src/api/device/get.ts
rename to src/api/get/DeviceGet.ts
index 914f735..e9a72cd 100644
--- a/src/api/device/get.ts
+++ b/src/api/get/DeviceGet.ts
@@ -1,20 +1,21 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
-export interface ApiDeviceGet {
- type?: ApiResourceType.Device;
+export interface DeviceGet {
+ type?: ResourceType.Device;
id: string;
product_data: {
model_id: string;
manufacturer_name: string;
product_name: string;
- product_archetype: string;
+ product_archetype: ArcheType;
certified: boolean;
software_version: string;
hardware_platform_type?: string;
};
metadata: {
- archetype: string;
+ archetype: ArcheType;
name: string;
};
services: ResourceIdentifier[];
diff --git a/src/api/get/DevicePowerGet.ts b/src/api/get/DevicePowerGet.ts
new file mode 100644
index 0000000..c65d374
--- /dev/null
+++ b/src/api/get/DevicePowerGet.ts
@@ -0,0 +1,12 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+
+export interface DevicePowerGet {
+ type?: ResourceType.DevicePower;
+ id: string;
+ owner: ResourceIdentifier;
+ power_state: {
+ battery_state: 'normal' | 'low' | 'critical';
+ battery_level: number;
+ };
+}
diff --git a/src/api/grouped_light/get.ts b/src/api/get/GroupedLightGet.ts
similarity index 63%
rename from src/api/grouped_light/get.ts
rename to src/api/get/GroupedLightGet.ts
index a062454..e20df0b 100644
--- a/src/api/grouped_light/get.ts
+++ b/src/api/get/GroupedLightGet.ts
@@ -1,8 +1,8 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
-export interface ApiGroupedLightGet {
- type?: ApiResourceType.GroupedLight;
+export interface GroupedLightGet {
+ type?: ResourceType.GroupedLight;
id: string;
owner: ResourceIdentifier;
on?: { on: boolean };
diff --git a/src/api/light/get.ts b/src/api/get/LightGet.ts
similarity index 80%
rename from src/api/light/get.ts
rename to src/api/get/LightGet.ts
index 054caa1..63174b4 100644
--- a/src/api/light/get.ts
+++ b/src/api/get/LightGet.ts
@@ -1,12 +1,13 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
-export interface ApiLightGet {
- type?: ApiResourceType.Light;
+export interface LightGet {
+ type?: ResourceType.Light;
id: string;
owner: ResourceIdentifier;
metadata: {
- archetype: string;
+ archetype: ArcheType;
name: string;
};
on: {
@@ -65,15 +66,19 @@ export interface ApiLightGet {
};
effects?: {
effect: 'fire' | 'candle' | 'no_effect';
+ // TODO any[] not correct type
status_value: any[];
status: 'fire' | 'candle' | 'no_effect';
+ // TODO any[] not correct type
effect_values: any[];
};
timed_effects?: {
effect: 'sunrise' | 'no_effect';
duration: number;
+ // TODO any[] not correct type
status_values: any[];
status: 'sunrise' | 'no_effect';
+ // TODO any[] not correct type
effect_values: any[];
};
}
diff --git a/src/api/get/MotionGet.ts b/src/api/get/MotionGet.ts
new file mode 100644
index 0000000..47f5e9c
--- /dev/null
+++ b/src/api/get/MotionGet.ts
@@ -0,0 +1,13 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+
+export interface MotionGet {
+ type?: ResourceType.Motion;
+ id: string;
+ owner: ResourceIdentifier;
+ enabled: boolean;
+ motion: {
+ motion: boolean;
+ motion_valid: boolean;
+ };
+}
diff --git a/src/api/room/get.ts b/src/api/get/RoomGet.ts
similarity index 52%
rename from src/api/room/get.ts
rename to src/api/get/RoomGet.ts
index a66f363..c3a6430 100644
--- a/src/api/room/get.ts
+++ b/src/api/get/RoomGet.ts
@@ -1,13 +1,14 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
-export interface ApiRoomGet {
- type?: ApiResourceType.Room;
+export interface RoomGet {
+ type?: ResourceType.Room;
id: string;
children: Array;
services: Array;
metadata: {
name: string;
- archetype: string;
+ archetype: ArcheType;
};
}
diff --git a/src/api/scene/get.ts b/src/api/get/SceneGet.ts
similarity index 87%
rename from src/api/scene/get.ts
rename to src/api/get/SceneGet.ts
index ff61134..bcd2913 100644
--- a/src/api/scene/get.ts
+++ b/src/api/get/SceneGet.ts
@@ -1,8 +1,8 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
-export interface ApiSceneGet {
- type?: ApiResourceType.Scene;
+export interface SceneGet {
+ type?: ResourceType.Scene;
id: string;
metadata: {
name: string;
diff --git a/src/api/get/ZigbeeConnectivityGet.ts b/src/api/get/ZigbeeConnectivityGet.ts
new file mode 100644
index 0000000..2f0056f
--- /dev/null
+++ b/src/api/get/ZigbeeConnectivityGet.ts
@@ -0,0 +1,10 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+
+export interface ZigbeeConnectivityGet {
+ type?: ResourceType.ZigbeeConnectivity;
+ id: string;
+ owner: ResourceIdentifier;
+ status: 'connected' | 'disconnected' | 'connectivity_issue' | 'unidirectional_incoming';
+ mac_address: string;
+}
diff --git a/src/api/get/ZigbeeDeviceDiscoveryGet.ts b/src/api/get/ZigbeeDeviceDiscoveryGet.ts
new file mode 100644
index 0000000..dedfb82
--- /dev/null
+++ b/src/api/get/ZigbeeDeviceDiscoveryGet.ts
@@ -0,0 +1,9 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+
+export interface ZigbeeDeviceDiscoveryGet {
+ type?: ResourceType.ZigbeeDeviceDiscovery;
+ id: string;
+ owner: ResourceIdentifier;
+ status: 'active' | 'ready';
+}
diff --git a/src/api/zone/get.ts b/src/api/get/ZoneGet.ts
similarity index 52%
rename from src/api/zone/get.ts
rename to src/api/get/ZoneGet.ts
index 655bbf3..671dc9b 100644
--- a/src/api/zone/get.ts
+++ b/src/api/get/ZoneGet.ts
@@ -1,13 +1,14 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
-export interface ApiZoneGet {
- type?: ApiResourceType.Zone;
+export interface ZoneGet {
+ type?: ResourceType.Zone;
id: string;
children: Array;
services: Array;
metadata: {
name: string;
- archetype: string;
+ archetype: ArcheType;
};
}
diff --git a/src/api/post/RoomPost.ts b/src/api/post/RoomPost.ts
new file mode 100644
index 0000000..d228f64
--- /dev/null
+++ b/src/api/post/RoomPost.ts
@@ -0,0 +1,12 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
+
+export interface RoomPost {
+ type?: ResourceType.Room;
+ children: Array;
+ metadata: {
+ name: string;
+ archetype: ArcheType;
+ };
+}
diff --git a/src/api/scene/post.ts b/src/api/post/ScenePost.ts
similarity index 87%
rename from src/api/scene/post.ts
rename to src/api/post/ScenePost.ts
index 33fe33d..7d07c49 100644
--- a/src/api/scene/post.ts
+++ b/src/api/post/ScenePost.ts
@@ -1,8 +1,8 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
-export interface ApiScenePost {
- type?: ApiResourceType.Scene;
+export interface ScenePost {
+ type?: ResourceType.Scene;
metadata?: { name: string; image?: ResourceIdentifier };
group: ResourceIdentifier;
actions: Array<{
diff --git a/src/api/post/ZonePost.ts b/src/api/post/ZonePost.ts
new file mode 100644
index 0000000..c099e4b
--- /dev/null
+++ b/src/api/post/ZonePost.ts
@@ -0,0 +1,12 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
+
+export interface ZonePost {
+ type?: ResourceType.Zone;
+ children: Array;
+ metadata: {
+ name: string;
+ archetype: ArcheType;
+ };
+}
diff --git a/src/api/put/DevicePut.ts b/src/api/put/DevicePut.ts
new file mode 100644
index 0000000..fb744f8
--- /dev/null
+++ b/src/api/put/DevicePut.ts
@@ -0,0 +1,13 @@
+import { ResourceType } from '../ResourceType';
+import { ArcheType } from '../ArcheType';
+
+export interface DevicePut {
+ type?: ResourceType.Device;
+ metadata?: {
+ archetype?: ArcheType;
+ name?: string;
+ };
+ identify?: {
+ action: 'identify';
+ };
+}
diff --git a/src/api/grouped_light/put.ts b/src/api/put/GroupedLightPut.ts
similarity index 71%
rename from src/api/grouped_light/put.ts
rename to src/api/put/GroupedLightPut.ts
index 92da34e..e874ccf 100644
--- a/src/api/grouped_light/put.ts
+++ b/src/api/put/GroupedLightPut.ts
@@ -1,9 +1,10 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
+import { ArcheType } from '../ArcheType';
-export interface ApiGroupedLightPut {
- type?: ApiResourceType.GroupedLight;
+export interface GroupedLightPut {
+ type?: ResourceType.GroupedLight;
metadata?: {
- archetype?: string;
+ archetype?: ArcheType;
name?: string;
};
on?: {
diff --git a/src/api/light/put.ts b/src/api/put/LightPut.ts
similarity index 80%
rename from src/api/light/put.ts
rename to src/api/put/LightPut.ts
index 06cfc96..75bcec3 100644
--- a/src/api/light/put.ts
+++ b/src/api/put/LightPut.ts
@@ -1,9 +1,10 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
+import { ArcheType } from '../ArcheType';
-export interface ApiLightPut {
- type?: ApiResourceType.Light;
+export interface LightPut {
+ type?: ResourceType.Light;
metadata?: {
- archetype?: string;
+ archetype?: ArcheType;
name?: string;
};
on?: {
diff --git a/src/api/put/MotionPut.ts b/src/api/put/MotionPut.ts
new file mode 100644
index 0000000..7172776
--- /dev/null
+++ b/src/api/put/MotionPut.ts
@@ -0,0 +1,6 @@
+import { ResourceType } from '../ResourceType';
+
+export interface MotionPut {
+ type?: ResourceType.Motion;
+ enabled?: boolean;
+}
diff --git a/src/api/put/RoomPut.ts b/src/api/put/RoomPut.ts
new file mode 100644
index 0000000..dbe3220
--- /dev/null
+++ b/src/api/put/RoomPut.ts
@@ -0,0 +1,12 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
+
+export interface RoomPut {
+ type?: ResourceType.Room;
+ children?: Array;
+ metadata?: {
+ name?: string;
+ archetype?: ArcheType;
+ };
+}
diff --git a/src/api/scene/put.ts b/src/api/put/ScenePut.ts
similarity index 88%
rename from src/api/scene/put.ts
rename to src/api/put/ScenePut.ts
index d9d85b6..7588c60 100644
--- a/src/api/scene/put.ts
+++ b/src/api/put/ScenePut.ts
@@ -1,8 +1,8 @@
-import { ApiResourceType } from '../ApiResourceType';
+import { ResourceType } from '../ResourceType';
import { ResourceIdentifier } from '../ResourceIdentifier';
-export interface ApiScenePut {
- type?: ApiResourceType.Scene;
+export interface ScenePut {
+ type?: ResourceType.Scene;
metadata?: { name: string };
actions?: Array<{
target: ResourceIdentifier;
diff --git a/src/api/put/ZigbeeDeviceDiscoveryPut.ts b/src/api/put/ZigbeeDeviceDiscoveryPut.ts
new file mode 100644
index 0000000..d35c56f
--- /dev/null
+++ b/src/api/put/ZigbeeDeviceDiscoveryPut.ts
@@ -0,0 +1,11 @@
+import { ResourceType } from '../ResourceType';
+
+export interface ZigbeeDeviceDiscoveryPut {
+ type?: ResourceType.ZigbeeDeviceDiscovery;
+ action: {
+ action_type: 'search';
+ // TODO not properly documented, type is unknown
+ search_codes: Array;
+ install_codes: Array;
+ };
+}
diff --git a/src/api/put/ZonePut.ts b/src/api/put/ZonePut.ts
new file mode 100644
index 0000000..a9a3b1f
--- /dev/null
+++ b/src/api/put/ZonePut.ts
@@ -0,0 +1,12 @@
+import { ResourceType } from '../ResourceType';
+import { ResourceIdentifier } from '../ResourceIdentifier';
+import { ArcheType } from '../ArcheType';
+
+export interface ZonePut {
+ type?: ResourceType.Zone;
+ children?: Array;
+ metadata?: {
+ name?: string;
+ archetype?: ArcheType;
+ };
+}
diff --git a/src/api/room/post.ts b/src/api/room/post.ts
deleted file mode 100644
index 6e9c5dc..0000000
--- a/src/api/room/post.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { ApiResourceType } from '../ApiResourceType';
-import { ResourceIdentifier } from '../ResourceIdentifier';
-
-export interface ApiRoomPost {
- type?: ApiResourceType.Room;
- children: Array;
- metadata: {
- name: string;
- archetype: string;
- };
-}
diff --git a/src/api/room/put.ts b/src/api/room/put.ts
deleted file mode 100644
index 4a40888..0000000
--- a/src/api/room/put.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { ApiResourceType } from '../ApiResourceType';
-import { ResourceIdentifier } from '../ResourceIdentifier';
-
-export interface ApiRoomPut {
- type?: ApiResourceType.Room;
- children?: Array;
- metadata?: {
- name?: string;
- archetype?: string;
- };
-}
diff --git a/src/api/zone/post.ts b/src/api/zone/post.ts
deleted file mode 100644
index 45a9a67..0000000
--- a/src/api/zone/post.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { ApiResourceType } from '../ApiResourceType';
-import { ResourceIdentifier } from '../ResourceIdentifier';
-
-export interface ApiZonePost {
- type?: ApiResourceType.Zone;
- children: Array;
- metadata: {
- name: string;
- archetype: string;
- };
-}
diff --git a/src/api/zone/put.ts b/src/api/zone/put.ts
deleted file mode 100644
index 800cdfe..0000000
--- a/src/api/zone/put.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { ApiResourceType } from '../ApiResourceType';
-import { ResourceIdentifier } from '../ResourceIdentifier';
-
-export interface ApiZonePut {
- type?: ApiResourceType.Zone;
- children?: Array;
- metadata?: {
- name?: string;
- archetype?: string;
- };
-}
diff --git a/src/bridge/Bridge.ts b/src/bridge/Bridge.ts
deleted file mode 100644
index 0509e31..0000000
--- a/src/bridge/Bridge.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { EventEmitter } from 'node:events';
-import { BridgeEvents, Events } from './BridgeEvents';
-import { Rest } from '../connections/Rest';
-import { ResourceManager } from '../managers/ResourceManager';
-import { Sse } from '../connections/Sse';
-
-export const CA =
- '-----BEGIN CERTIFICATE-----\n' +
- 'MIICMjCCAdigAwIBAgIUO7FSLbaxikuXAljzVaurLXWmFw4wCgYIKoZIzj0EAwIw\n' +
- 'OTELMAkGA1UEBhMCTkwxFDASBgNVBAoMC1BoaWxpcHMgSHVlMRQwEgYDVQQDDAty\n' +
- 'b290LWJyaWRnZTAiGA8yMDE3MDEwMTAwMDAwMFoYDzIwMzgwMTE5MDMxNDA3WjA5\n' +
- 'MQswCQYDVQQGEwJOTDEUMBIGA1UECgwLUGhpbGlwcyBIdWUxFDASBgNVBAMMC3Jv\n' +
- 'b3QtYnJpZGdlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjNw2tx2AplOf9x86\n' +
- 'aTdvEcL1FU65QDxziKvBpW9XXSIcibAeQiKxegpq8Exbr9v6LBnYbna2VcaK0G22\n' +
- 'jOKkTqOBuTCBtjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNV\n' +
- 'HQ4EFgQUZ2ONTFrDT6o8ItRnKfqWKnHFGmQwdAYDVR0jBG0wa4AUZ2ONTFrDT6o8\n' +
- 'ItRnKfqWKnHFGmShPaQ7MDkxCzAJBgNVBAYTAk5MMRQwEgYDVQQKDAtQaGlsaXBz\n' +
- 'IEh1ZTEUMBIGA1UEAwwLcm9vdC1icmlkZ2WCFDuxUi22sYpLlwJY81Wrqy11phcO\n' +
- 'MAoGCCqGSM49BAMCA0gAMEUCIEBYYEOsa07TH7E5MJnGw557lVkORgit2Rm1h3B2\n' +
- 'sFgDAiEA1Fj/C3AN5psFMjo0//mrQebo0eKd3aWRx+pQY08mk48=\n' +
- '-----END CERTIFICATE-----';
-
-export interface BridgeConnectionOptions {
- applicationKey: string;
-}
-
-export interface LocalBridgeConnectionsOptions extends BridgeConnectionOptions {
- ip: string;
-}
-
-export interface ExternalBridgeConnectionOptions extends BridgeConnectionOptions {
- accessToken: string;
-}
-
-export interface BridgeOptions {
- connection: LocalBridgeConnectionsOptions | ExternalBridgeConnectionOptions;
-}
-
-export interface Bridge {
- on: (event: T, listener: (...args: BridgeEvents[T]) => any) => this;
- once: (event: T, listener: (...args: BridgeEvents[T]) => any) => this;
- emit: (event: T, ...args: BridgeEvents[T]) => boolean;
- off: (event: T, listener: (...args: BridgeEvents[T]) => any) => this;
- removeAllListeners: (event?: T) => this;
-}
-
-export class Bridge extends EventEmitter {
- public readonly options: BridgeOptions;
- public readonly rest = new Rest(this);
- public readonly sse = new Sse(this);
- public readonly resources = new ResourceManager(this);
-
- public constructor(options: BridgeOptions) {
- super();
- this.options = options;
- }
-
- get _url(): string {
- if ('ip' in this.options.connection) return `https://${this.options.connection.ip}:443`;
- else return `https://api.meethue.com/route`;
- }
-
- public async connect(): Promise {
- const data = await this.rest.get('/resource');
-
- for (const resource of data) {
- this.resources._create(resource);
- }
-
- await this.sse.connect();
-
- this.emit(Events.Ready, this);
- }
-}
diff --git a/src/util/color/gamut.ts b/src/color/gamut.ts
similarity index 100%
rename from src/util/color/gamut.ts
rename to src/color/gamut.ts
diff --git a/src/util/color/hex.ts b/src/color/hex.ts
similarity index 61%
rename from src/util/color/hex.ts
rename to src/color/hex.ts
index a4aa500..a64f50e 100644
--- a/src/util/color/hex.ts
+++ b/src/color/hex.ts
@@ -6,13 +6,13 @@ export function rgbToHex({ red, green, blue }: RGB) {
}
export function hexToRgb(hex: string): RGB {
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- if (!result) throw new TypeError('Invalid hex');
+ const parsed = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (m, r, g, b) => '#' + r + r + g + g + b + b);
+ if (!parsed) throw new TypeError('Invalid hex');
return {
- red: parseInt(result[1], 16),
- green: parseInt(result[2], 16),
- blue: parseInt(result[3], 16),
+ red: parseInt(parsed.substring(1, 3), 16),
+ green: parseInt(parsed.substring(3, 5), 16),
+ blue: parseInt(parsed.substring(5, 7), 16),
};
}
diff --git a/src/util/color/rgb.ts b/src/color/rgb.ts
similarity index 100%
rename from src/util/color/rgb.ts
rename to src/color/rgb.ts
diff --git a/src/util/color/xy.ts b/src/color/xy.ts
similarity index 100%
rename from src/util/color/xy.ts
rename to src/color/xy.ts
diff --git a/src/connections/Limit.ts b/src/connections/Limit.ts
index 0dbacc8..d17a36c 100644
--- a/src/connections/Limit.ts
+++ b/src/connections/Limit.ts
@@ -7,7 +7,7 @@ export interface RateLimits {
}
export const RateLimits: RateLimits = {
- '/resources/lights': 100,
+ '/resource/light/': 100,
} as const;
export class Limit extends AsyncQueue {
diff --git a/src/connections/Rest.ts b/src/connections/Rest.ts
index ae874a2..1ec5be7 100644
--- a/src/connections/Rest.ts
+++ b/src/connections/Rest.ts
@@ -1,8 +1,8 @@
-import { Bridge, CA } from '../bridge/Bridge';
+import { CA, Hue } from '../hue/Hue';
import { Agent, request } from 'undici';
import { Collection } from '@discordjs/collection';
import { Limit } from './Limit';
-import { Events } from '../bridge/BridgeEvents';
+import { Events } from '../hue/HueEvents';
export interface Request {
route: string;
@@ -23,12 +23,12 @@ export enum RestRequestType {
}
export class Rest {
- public readonly bridge: Bridge;
+ public readonly hue: Hue;
public readonly dispatcher: Agent;
public readonly limits: Collection;
- constructor(bridge: Bridge) {
- this.bridge = bridge;
+ constructor(hue: Hue) {
+ this.hue = hue;
this.dispatcher = new Agent({
connect: {
ca: CA,
@@ -56,8 +56,8 @@ export class Rest {
return await this._queue(route, RestRequestType.Delete);
}
- private async _queue(route: string, method: RestRequestType, data?: Record) {
- this.bridge.emit(Events.Request, {
+ public async _queue(route: string, method: RestRequestType, data?: Record) {
+ this.hue.emit(Events.Request, {
route,
method,
body: data,
@@ -68,15 +68,15 @@ export class Rest {
await limit.wait();
try {
- const { body, statusCode } = await request(`${this.bridge._url}/clip/v2${route}`, {
+ const { body, statusCode } = await request(`${this.hue._url}/clip/v2${route}`, {
method,
body: data ? JSON.stringify(data) : null,
headers: {
Authorization:
- 'accessToken' in this.bridge.options.connection
- ? `Bearer ${this.bridge.options.connection.accessToken}`
+ 'accessToken' in this.hue.options.connection
+ ? `Bearer ${this.hue.options.connection.accessToken}`
: undefined,
- 'hue-application-key': this.bridge.options.connection.applicationKey,
+ 'hue-application-key': this.hue.options.connection.applicationKey,
'Content-Type': 'application/json',
Accept: 'application/json',
},
@@ -85,9 +85,10 @@ export class Rest {
const responseData = await body.json();
- if (statusCode !== 200) throw new Error(`${responseData?.errors?.[0]?.description ?? 'unknown'}`);
+ if (statusCode !== 200 && statusCode !== 201)
+ throw new Error(`${responseData?.errors?.[0]?.description ?? 'unknown'}`);
- this.bridge.emit(Events.Response, {
+ this.hue.emit(Events.Response, {
route,
method,
body: responseData,
@@ -100,13 +101,12 @@ export class Rest {
}
}
- private _getLimit(route: string) {
+ public _getLimit(route: string) {
return this.limits.ensure(route, () => new Limit(this, route));
}
- private _sanitizeRoute(route: string) {
- route.replace(/\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/, '');
-
- return route;
+ public _sanitizeRoute(route: string) {
+ // Remove possible resource ID from the route
+ return route.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/, '');
}
}
diff --git a/src/connections/Sse.ts b/src/connections/Sse.ts
index 4a5bd31..a476e32 100644
--- a/src/connections/Sse.ts
+++ b/src/connections/Sse.ts
@@ -1,43 +1,16 @@
-import { Bridge } from '../bridge/Bridge';
+import { Hue } from '../hue/Hue';
import { Agent, Dispatcher, request } from 'undici';
import BodyReadable from 'undici/types/readable';
-import { Events } from '../bridge/BridgeEvents';
-import { ApiResourceType } from '../api/ApiResourceType';
-
-export const RESOURCES_EVENTS = {
- [ApiResourceType.Device]: [Events.DeviceAdd, Events.DeviceUpdate, Events.DeviceDelete],
- [ApiResourceType.BridgeHome]: undefined,
- [ApiResourceType.Room]: [Events.RoomAdd, Events.RoomUpdate, Events.RoomDelete],
- [ApiResourceType.Zone]: [Events.ZoneAdd, Events.ZoneUpdate, Events.ZoneDelete],
- [ApiResourceType.Light]: [Events.LightAdd, Events.LightUpdate, Events.LightDelete],
- [ApiResourceType.Button]: undefined,
- [ApiResourceType.Temperature]: undefined,
- [ApiResourceType.LightLevel]: undefined,
- [ApiResourceType.Motion]: undefined,
- [ApiResourceType.Entertainment]: undefined,
- [ApiResourceType.GroupedLight]: [Events.GroupedLightAdd, Events.GroupedLightUpdate, Events.GroupedLightDelete],
- [ApiResourceType.DevicePower]: undefined,
- [ApiResourceType.ZigbeeBridgeConnectivity]: undefined,
- [ApiResourceType.ZgpConnectivity]: undefined,
- [ApiResourceType.Bridge]: undefined,
- [ApiResourceType.Homekit]: undefined,
- [ApiResourceType.Scene]: [Events.SceneAdd, Events.SceneUpdate, Events.SceneDelete],
- [ApiResourceType.EntertainmentConfiguration]: undefined,
- [ApiResourceType.PublicImage]: undefined,
- [ApiResourceType.BehaviourScript]: undefined,
- [ApiResourceType.BehaviourInstance]: undefined,
- [ApiResourceType.Geofence]: undefined,
- [ApiResourceType.GeofenceClient]: undefined,
- [ApiResourceType.Geolocation]: undefined,
-};
+import { Events } from '../hue/HueEvents';
+import { RESOURCE_ADD, RESOURCE_DELETE, RESOURCE_UPDATE } from './events';
export class Sse {
- public readonly bridge: Bridge;
+ public readonly hue: Hue;
public readonly dispatcher: Agent;
public connection?: BodyReadable & Dispatcher.BodyMixin;
- constructor(bridge: Bridge) {
- this.bridge = bridge;
+ constructor(hue: Hue) {
+ this.hue = hue;
this.dispatcher = new Agent({
connect: {
// ca: CA,
@@ -51,20 +24,20 @@ export class Sse {
}
public debug(message: string) {
- this.bridge.emit(Events.Debug, `[SSE] ${message}`);
+ this.hue.emit(Events.Debug, `[SSE] ${message}`);
}
public async connect(): Promise {
this.connection = undefined;
- const { body, statusCode } = await request(`${this.bridge._url}/eventstream/clip/v2`, {
+ const { body, statusCode } = await request(`${this.hue._url}/eventstream/clip/v2`, {
method: 'GET',
headers: {
Authorization:
- 'accessToken' in this.bridge.options.connection
- ? `Bearer ${this.bridge.options.connection.accessToken}`
+ 'accessToken' in this.hue.options.connection
+ ? `Bearer ${this.hue.options.connection.accessToken}`
: undefined,
- 'hue-application-key': this.bridge.options.connection.applicationKey,
+ 'hue-application-key': this.hue.options.connection.applicationKey,
Accept: 'text/event-stream',
},
bodyTimeout: 0,
@@ -81,67 +54,32 @@ export class Sse {
public async event(raw: string) {
if (raw === ': hi\n' + '\n') return this.debug('Hi');
- const events = this.parse(raw);
+ const events = this._parse(raw);
+
+ const queue: Array<(() => boolean) | undefined> = [];
for (const event of events) {
this.debug(`Received ${event.data.length} ${event.type} event(s)`);
- for (const data of event.data) {
- switch (event.type) {
- case 'add': {
- this.add(data);
- break;
- }
- case 'update': {
- this.update(data);
- break;
- }
- case 'delete': {
- this.delete(data);
- break;
- }
- }
- }
- }
- this.bridge.emit(Events.Raw, events);
- }
-
- public add(data: Record): void {
- const resource = this.bridge.resources._create(data);
- if (!resource) return;
-
- this.bridge.emit(Events.Add, resource);
-
- const event = RESOURCES_EVENTS[resource.type as ApiResourceType];
- if (event) this.bridge.emit(event[0], resource as any);
- }
- public update(data: Record): void {
- const resource = this.bridge.resources.cache.get(data.id);
- if (!resource) return;
-
- const clone = resource._update(data);
-
- this.bridge.emit(Events.Update, resource, clone);
-
- const event = RESOURCES_EVENTS[resource.type as ApiResourceType];
- if (event) this.bridge.emit(event[1], resource as any, clone as any);
- }
-
- public delete(data: Record) {
- const resource = this.bridge.resources.cache.get(data.id);
- if (!resource) return;
+ for (const data of event.data) {
+ let handler: ((data: any, hue: Hue) => (() => boolean) | undefined) | undefined;
- const clone = resource._clone();
+ if (event.type == 'add') handler = RESOURCE_ADD[data.type];
+ else if (event.type == 'delete') handler = RESOURCE_DELETE[data.type];
+ else if (event.type == 'update') handler = RESOURCE_UPDATE[data.type];
- this.bridge.resources.cache.delete(data.id);
+ if (handler) queue.push(handler(data, this.hue));
+ }
+ }
- this.bridge.emit(Events.Delete, clone);
+ for (const emitter of queue) {
+ if (typeof emitter === 'function') emitter();
+ }
- const event = RESOURCES_EVENTS[clone.type as ApiResourceType];
- if (event) this.bridge.emit(event[2], clone as any);
+ this.hue.emit(Events.Raw, events);
}
- public parse(raw: string): Array> {
+ public _parse(raw: string): Array> {
const regex = /id: \d+:\d+\ndata: /;
const replaced = raw.replace(regex, '');
diff --git a/src/connections/events/add/BridgeAdd.ts b/src/connections/events/add/BridgeAdd.ts
new file mode 100644
index 0000000..99e75ef
--- /dev/null
+++ b/src/connections/events/add/BridgeAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function bridgeAdd(data: any, hue: Hue) {
+ const bridge = hue.bridges._add(data);
+ if (!bridge) return;
+
+ return () => hue.emit(Events.BridgeAdd, bridge);
+}
diff --git a/src/connections/events/add/DeviceAdd.ts b/src/connections/events/add/DeviceAdd.ts
new file mode 100644
index 0000000..5c1a4ed
--- /dev/null
+++ b/src/connections/events/add/DeviceAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function deviceAdd(data: any, hue: Hue) {
+ const device = hue.devices._add(data);
+ if (!device) return;
+
+ return () => hue.emit(Events.DeviceAdd, device);
+}
diff --git a/src/connections/events/add/DevicePowerAdd.ts b/src/connections/events/add/DevicePowerAdd.ts
new file mode 100644
index 0000000..4b19d8a
--- /dev/null
+++ b/src/connections/events/add/DevicePowerAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function devicePowerAdd(data: any, hue: Hue) {
+ const devicePower = hue.devicePowers._add(data);
+ if (!devicePower) return;
+
+ return () => hue.emit(Events.DevicePowerAdd, devicePower);
+}
diff --git a/src/connections/events/add/GroupedLightAdd.ts b/src/connections/events/add/GroupedLightAdd.ts
new file mode 100644
index 0000000..457910d
--- /dev/null
+++ b/src/connections/events/add/GroupedLightAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function groupedLightAdd(data: any, hue: Hue) {
+ const groupedLight = hue.groupedLights._add(data);
+ if (!groupedLight) return;
+
+ return () => hue.emit(Events.GroupedLightAdd, groupedLight);
+}
diff --git a/src/connections/events/add/LightAdd.ts b/src/connections/events/add/LightAdd.ts
new file mode 100644
index 0000000..ca15534
--- /dev/null
+++ b/src/connections/events/add/LightAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function lightAdd(data: any, hue: Hue) {
+ const light = hue.lights._add(data);
+ if (!light) return;
+
+ return () => hue.emit(Events.LightAdd, light);
+}
diff --git a/src/connections/events/add/MotionAdd.ts b/src/connections/events/add/MotionAdd.ts
new file mode 100644
index 0000000..4bfc2fc
--- /dev/null
+++ b/src/connections/events/add/MotionAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function motionAdd(data: any, hue: Hue) {
+ const motion = hue.motions._add(data);
+ if (!motion) return;
+
+ return () => hue.emit(Events.MotionAdd, motion);
+}
diff --git a/src/connections/events/add/RoomAdd.ts b/src/connections/events/add/RoomAdd.ts
new file mode 100644
index 0000000..4aee1e4
--- /dev/null
+++ b/src/connections/events/add/RoomAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function roomAdd(data: any, hue: Hue) {
+ const room = hue.rooms._add(data);
+ if (!room) return;
+
+ return () => hue.emit(Events.RoomAdd, room);
+}
diff --git a/src/connections/events/add/SceneAdd.ts b/src/connections/events/add/SceneAdd.ts
new file mode 100644
index 0000000..5007489
--- /dev/null
+++ b/src/connections/events/add/SceneAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function sceneAdd(data: any, hue: Hue) {
+ const scene = hue.scenes._add(data);
+ if (!scene) return;
+
+ return () => hue.emit(Events.SceneAdd, scene);
+}
diff --git a/src/connections/events/add/ZigbeeConnectivityAdd.ts b/src/connections/events/add/ZigbeeConnectivityAdd.ts
new file mode 100644
index 0000000..115baf9
--- /dev/null
+++ b/src/connections/events/add/ZigbeeConnectivityAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zigbeeConnectivityAdd(data: any, hue: Hue) {
+ const zigbeeConnectivity = hue.zigbeeConnectivities._add(data);
+ if (!zigbeeConnectivity) return;
+
+ return () => hue.emit(Events.ZigbeeConnectivityAdd, zigbeeConnectivity);
+}
diff --git a/src/connections/events/add/ZigbeeDeviceDiscoveryAdd.ts b/src/connections/events/add/ZigbeeDeviceDiscoveryAdd.ts
new file mode 100644
index 0000000..bce1666
--- /dev/null
+++ b/src/connections/events/add/ZigbeeDeviceDiscoveryAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zigbeeDeviceDiscoveryAdd(data: any, hue: Hue) {
+ const zigbeeDeviceDiscovery = hue.zigbeeDeviceDiscoveries._add(data);
+ if (!zigbeeDeviceDiscovery) return;
+
+ return () => hue.emit(Events.ZigbeeDeviceDiscoveryAdd, zigbeeDeviceDiscovery);
+}
diff --git a/src/connections/events/add/ZoneAdd.ts b/src/connections/events/add/ZoneAdd.ts
new file mode 100644
index 0000000..e3f2f1c
--- /dev/null
+++ b/src/connections/events/add/ZoneAdd.ts
@@ -0,0 +1,9 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zoneAdd(data: any, hue: Hue) {
+ const zone = hue.zones._add(data);
+ if (!zone) return;
+
+ return () => hue.emit(Events.ZoneAdd, zone);
+}
diff --git a/src/connections/events/delete/BridgeDelete.ts b/src/connections/events/delete/BridgeDelete.ts
new file mode 100644
index 0000000..e2261c3
--- /dev/null
+++ b/src/connections/events/delete/BridgeDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function bridgeDelete(data: any, hue: Hue) {
+ const bridge = hue.bridges.cache.get(data.id);
+ if (!bridge) return;
+
+ const clone = bridge._clone();
+
+ hue.bridges.cache.delete(data.id);
+
+ return () => hue.emit(Events.BridgeDelete, clone);
+}
diff --git a/src/connections/events/delete/DeviceDelete.ts b/src/connections/events/delete/DeviceDelete.ts
new file mode 100644
index 0000000..04bc916
--- /dev/null
+++ b/src/connections/events/delete/DeviceDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function deviceDelete(data: any, hue: Hue) {
+ const device = hue.devices.cache.get(data.id);
+ if (!device) return;
+
+ const clone = device._clone();
+
+ hue.devices.cache.delete(data.id);
+
+ return () => hue.emit(Events.DeviceDelete, clone);
+}
diff --git a/src/connections/events/delete/DevicePowerDelete.ts b/src/connections/events/delete/DevicePowerDelete.ts
new file mode 100644
index 0000000..b84f5d5
--- /dev/null
+++ b/src/connections/events/delete/DevicePowerDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function devicePowerDelete(data: any, hue: Hue) {
+ const devicePower = hue.devicePowers.cache.get(data.id);
+ if (!devicePower) return;
+
+ const clone = devicePower._clone();
+
+ hue.devicePowers.cache.delete(data.id);
+
+ return () => hue.emit(Events.DevicePowerDelete, clone);
+}
diff --git a/src/connections/events/delete/GroupedLightDelete.ts b/src/connections/events/delete/GroupedLightDelete.ts
new file mode 100644
index 0000000..24d1b12
--- /dev/null
+++ b/src/connections/events/delete/GroupedLightDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function groupedLightDelete(data: any, hue: Hue) {
+ const groupedLight = hue.groupedLights.cache.get(data.id);
+ if (!groupedLight) return;
+
+ const clone = groupedLight._clone();
+
+ hue.groupedLights.cache.delete(data.id);
+
+ return () => hue.emit(Events.GroupedLightDelete, clone);
+}
diff --git a/src/connections/events/delete/LightDelete.ts b/src/connections/events/delete/LightDelete.ts
new file mode 100644
index 0000000..e403cca
--- /dev/null
+++ b/src/connections/events/delete/LightDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function lightDelete(data: any, hue: Hue) {
+ const light = hue.lights.cache.get(data.id);
+ if (!light) return;
+
+ const clone = light._clone();
+
+ hue.devices.cache.delete(data.id);
+
+ return () => hue.emit(Events.LightDelete, clone);
+}
diff --git a/src/connections/events/delete/MotionDelete.ts b/src/connections/events/delete/MotionDelete.ts
new file mode 100644
index 0000000..a5c0c3a
--- /dev/null
+++ b/src/connections/events/delete/MotionDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function motionDelete(data: any, hue: Hue) {
+ const motion = hue.motions.cache.get(data.id);
+ if (!motion) return;
+
+ const clone = motion._clone();
+
+ hue.devices.cache.delete(data.id);
+
+ return () => hue.emit(Events.MotionDelete, clone);
+}
diff --git a/src/connections/events/delete/RoomDelete.ts b/src/connections/events/delete/RoomDelete.ts
new file mode 100644
index 0000000..9a14360
--- /dev/null
+++ b/src/connections/events/delete/RoomDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function roomDelete(data: any, hue: Hue) {
+ const room = hue.rooms.cache.get(data.id);
+ if (!room) return;
+
+ const clone = room._clone();
+
+ hue.rooms.cache.delete(data.id);
+
+ return () => hue.emit(Events.RoomDelete, clone);
+}
diff --git a/src/connections/events/delete/SceneDelete.ts b/src/connections/events/delete/SceneDelete.ts
new file mode 100644
index 0000000..4cd8d9f
--- /dev/null
+++ b/src/connections/events/delete/SceneDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function sceneDelete(data: any, hue: Hue) {
+ const scene = hue.scenes.cache.get(data.id);
+ if (!scene) return;
+
+ const clone = scene._clone();
+
+ hue.scenes.cache.delete(data.id);
+
+ return () => hue.emit(Events.SceneDelete, clone);
+}
diff --git a/src/connections/events/delete/ZigbeeConnectivityDelete.ts b/src/connections/events/delete/ZigbeeConnectivityDelete.ts
new file mode 100644
index 0000000..b0a86e0
--- /dev/null
+++ b/src/connections/events/delete/ZigbeeConnectivityDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zigbeeConnectivityDelete(data: any, hue: Hue) {
+ const zigbeeConnectivity = hue.zigbeeConnectivities.cache.get(data.id);
+ if (!zigbeeConnectivity) return;
+
+ const clone = zigbeeConnectivity._clone();
+
+ hue.zigbeeConnectivities.cache.delete(data.id);
+
+ return () => hue.emit(Events.ZigbeeConnectivityDelete, clone);
+}
diff --git a/src/connections/events/delete/ZigbeeDeviceDiscoveryDelete.ts b/src/connections/events/delete/ZigbeeDeviceDiscoveryDelete.ts
new file mode 100644
index 0000000..1d43ade
--- /dev/null
+++ b/src/connections/events/delete/ZigbeeDeviceDiscoveryDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zigbeeDeviceDiscoveryDelete(data: any, hue: Hue) {
+ const zigbeeDeviceDiscovery = hue.zigbeeDeviceDiscoveries.cache.get(data.id);
+ if (!zigbeeDeviceDiscovery) return;
+
+ const clone = zigbeeDeviceDiscovery._clone();
+
+ hue.zigbeeDeviceDiscoveries.cache.delete(data.id);
+
+ return () => hue.emit(Events.ZigbeeDeviceDiscoveryUpdate, clone);
+}
diff --git a/src/connections/events/delete/ZoneDelete.ts b/src/connections/events/delete/ZoneDelete.ts
new file mode 100644
index 0000000..f60fd08
--- /dev/null
+++ b/src/connections/events/delete/ZoneDelete.ts
@@ -0,0 +1,13 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zoneDelete(data: any, hue: Hue) {
+ const zone = hue.zones.cache.get(data.id);
+ if (!zone) return;
+
+ const clone = zone._clone();
+
+ hue.zones.cache.delete(data.id);
+
+ return () => hue.emit(Events.ZoneDelete, clone);
+}
diff --git a/src/connections/events/index.ts b/src/connections/events/index.ts
new file mode 100644
index 0000000..b8f135e
--- /dev/null
+++ b/src/connections/events/index.ts
@@ -0,0 +1,77 @@
+import { ResourceType } from '../../api/ResourceType';
+import deviceAdd from './add/DeviceAdd';
+import devicePowerAdd from './add/DevicePowerAdd';
+import groupedLightAdd from './add/GroupedLightAdd';
+import lightAdd from './add/LightAdd';
+import motionAdd from './add/MotionAdd';
+import roomAdd from './add/RoomAdd';
+import sceneAdd from './add/SceneAdd';
+import zoneAdd from './add/ZoneAdd';
+import deviceDelete from './delete/DeviceDelete';
+import devicePowerDelete from './delete/DevicePowerDelete';
+import groupedLightDelete from './delete/GroupedLightDelete';
+import lightDelete from './delete/LightDelete';
+import motionDelete from './delete/MotionDelete';
+import roomDelete from './delete/RoomDelete';
+import sceneDelete from './delete/SceneDelete';
+import zoneDelete from './delete/ZoneDelete';
+import deviceUpdate from './update/DeviceUpdate';
+import devicePowerUpdate from './update/DevicePowerUpdate';
+import groupedLightUpdate from './update/GroupedLightUpdate';
+import lightUpdate from './update/LightUpdate';
+import motionUpdate from './update/MotionUpdate';
+import roomUpdate from './update/RoomUpdate';
+import sceneUpdate from './update/SceneUpdate';
+import zoneUpdate from './update/ZoneUpdate';
+import { Hue } from '../../hue/Hue';
+import zigbeeConnectivityAdd from './add/ZigbeeConnectivityAdd';
+import zigbeeConnectivityDelete from './delete/ZigbeeConnectivityDelete';
+import zigbeeConnectivityUpdate from './update/ZigbeeConnectivityUpdate';
+import zigbeeDeviceDiscoveryAdd from './add/ZigbeeDeviceDiscoveryAdd';
+import zigbeeDeviceDiscoveryDelete from './delete/ZigbeeDeviceDiscoveryDelete';
+import zigbeeDeviceDiscoveryUpdate from './update/ZigbeeDeviceDiscoveryUpdate';
+import bridgeAdd from './add/BridgeAdd';
+import bridgeDelete from './delete/BridgeDelete';
+import bridgeUpdate from './update/BridgeUpdate';
+
+export const RESOURCE_ADD: { [key: string]: (data: any, hue: Hue) => (() => boolean) | undefined } = {
+ [ResourceType.Device]: deviceAdd,
+ [ResourceType.DevicePower]: devicePowerAdd,
+ [ResourceType.GroupedLight]: groupedLightAdd,
+ [ResourceType.Light]: lightAdd,
+ [ResourceType.Motion]: motionAdd,
+ [ResourceType.Room]: roomAdd,
+ [ResourceType.Scene]: sceneAdd,
+ [ResourceType.Zone]: zoneAdd,
+ [ResourceType.ZigbeeConnectivity]: zigbeeConnectivityAdd,
+ [ResourceType.ZigbeeDeviceDiscovery]: zigbeeDeviceDiscoveryAdd,
+ [ResourceType.Bridge]: bridgeAdd,
+};
+
+export const RESOURCE_DELETE: { [key: string]: (data: any, hue: Hue) => (() => boolean) | undefined } = {
+ [ResourceType.Device]: deviceDelete,
+ [ResourceType.DevicePower]: devicePowerDelete,
+ [ResourceType.GroupedLight]: groupedLightDelete,
+ [ResourceType.Light]: lightDelete,
+ [ResourceType.Motion]: motionDelete,
+ [ResourceType.Room]: roomDelete,
+ [ResourceType.Scene]: sceneDelete,
+ [ResourceType.Zone]: zoneDelete,
+ [ResourceType.ZigbeeConnectivity]: zigbeeConnectivityDelete,
+ [ResourceType.ZigbeeDeviceDiscovery]: zigbeeDeviceDiscoveryDelete,
+ [ResourceType.Bridge]: bridgeDelete,
+};
+
+export const RESOURCE_UPDATE: { [key: string]: (data: any, hue: Hue) => (() => boolean) | undefined } = {
+ [ResourceType.Device]: deviceUpdate,
+ [ResourceType.DevicePower]: devicePowerUpdate,
+ [ResourceType.GroupedLight]: groupedLightUpdate,
+ [ResourceType.Light]: lightUpdate,
+ [ResourceType.Motion]: motionUpdate,
+ [ResourceType.Room]: roomUpdate,
+ [ResourceType.Scene]: sceneUpdate,
+ [ResourceType.Zone]: zoneUpdate,
+ [ResourceType.ZigbeeConnectivity]: zigbeeConnectivityUpdate,
+ [ResourceType.ZigbeeDeviceDiscovery]: zigbeeDeviceDiscoveryUpdate,
+ [ResourceType.Bridge]: bridgeUpdate,
+};
diff --git a/src/connections/events/update/BridgeUpdate.ts b/src/connections/events/update/BridgeUpdate.ts
new file mode 100644
index 0000000..3f5d2bf
--- /dev/null
+++ b/src/connections/events/update/BridgeUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function bridgeUpdate(data: any, hue: Hue) {
+ const bridge = hue.bridges.cache.get(data.id);
+ if (!bridge) return;
+
+ const clone = bridge._update(data);
+
+ return () => hue.emit(Events.BridgeUpdate, bridge, clone);
+}
diff --git a/src/connections/events/update/DevicePowerUpdate.ts b/src/connections/events/update/DevicePowerUpdate.ts
new file mode 100644
index 0000000..e3b1c2b
--- /dev/null
+++ b/src/connections/events/update/DevicePowerUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function devicePowerUpdate(data: any, hue: Hue) {
+ const devicePower = hue.devicePowers.cache.get(data.id);
+ if (!devicePower) return;
+
+ const clone = devicePower._update(data);
+
+ return () => hue.emit(Events.DevicePowerUpdate, devicePower, clone);
+}
diff --git a/src/connections/events/update/DeviceUpdate.ts b/src/connections/events/update/DeviceUpdate.ts
new file mode 100644
index 0000000..c1affb2
--- /dev/null
+++ b/src/connections/events/update/DeviceUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function deviceUpdate(data: any, hue: Hue) {
+ const device = hue.devices.cache.get(data.id);
+ if (!device) return;
+
+ const clone = device._update(data);
+
+ return () => hue.emit(Events.DeviceUpdate, device, clone);
+}
diff --git a/src/connections/events/update/GroupedLightUpdate.ts b/src/connections/events/update/GroupedLightUpdate.ts
new file mode 100644
index 0000000..f057818
--- /dev/null
+++ b/src/connections/events/update/GroupedLightUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function groupedLightUpdate(data: any, hue: Hue) {
+ const groupedLight = hue.groupedLights.cache.get(data.id);
+ if (!groupedLight) return;
+
+ const clone = groupedLight._update(data);
+
+ return () => hue.emit(Events.GroupedLightUpdate, groupedLight, clone);
+}
diff --git a/src/connections/events/update/LightUpdate.ts b/src/connections/events/update/LightUpdate.ts
new file mode 100644
index 0000000..f8e256c
--- /dev/null
+++ b/src/connections/events/update/LightUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function lightUpdate(data: any, hue: Hue) {
+ const light = hue.lights.cache.get(data.id);
+ if (!light) return;
+
+ const clone = light._update(data);
+
+ return () => hue.emit(Events.LightUpdate, light, clone);
+}
diff --git a/src/connections/events/update/MotionUpdate.ts b/src/connections/events/update/MotionUpdate.ts
new file mode 100644
index 0000000..a701387
--- /dev/null
+++ b/src/connections/events/update/MotionUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function motionUpdate(data: any, hue: Hue) {
+ const motion = hue.motions.cache.get(data.id);
+ if (!motion) return;
+
+ const clone = motion._update(data);
+
+ return () => hue.emit(Events.MotionUpdate, motion, clone);
+}
diff --git a/src/connections/events/update/RoomUpdate.ts b/src/connections/events/update/RoomUpdate.ts
new file mode 100644
index 0000000..4be7f4b
--- /dev/null
+++ b/src/connections/events/update/RoomUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function roomUpdate(data: any, hue: Hue) {
+ const room = hue.rooms.cache.get(data.id);
+ if (!room) return;
+
+ const clone = room._update(data);
+
+ return () => hue.emit(Events.RoomUpdate, room, clone);
+}
diff --git a/src/connections/events/update/SceneUpdate.ts b/src/connections/events/update/SceneUpdate.ts
new file mode 100644
index 0000000..10652ac
--- /dev/null
+++ b/src/connections/events/update/SceneUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function sceneUpdate(data: any, hue: Hue) {
+ const scene = hue.scenes.cache.get(data.id);
+ if (!scene) return;
+
+ const clone = scene._update(data);
+
+ return () => hue.emit(Events.SceneUpdate, scene, clone);
+}
diff --git a/src/connections/events/update/ZigbeeConnectivityUpdate.ts b/src/connections/events/update/ZigbeeConnectivityUpdate.ts
new file mode 100644
index 0000000..212a73f
--- /dev/null
+++ b/src/connections/events/update/ZigbeeConnectivityUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zigbeeConnectivityUpdate(data: any, hue: Hue) {
+ const zigbeeConnectivity = hue.zigbeeConnectivities.cache.get(data.id);
+ if (!zigbeeConnectivity) return;
+
+ const clone = zigbeeConnectivity._update(data);
+
+ return () => hue.emit(Events.ZigbeeConnectivityUpdate, zigbeeConnectivity, clone);
+}
diff --git a/src/connections/events/update/ZigbeeDeviceDiscoveryUpdate.ts b/src/connections/events/update/ZigbeeDeviceDiscoveryUpdate.ts
new file mode 100644
index 0000000..52df37a
--- /dev/null
+++ b/src/connections/events/update/ZigbeeDeviceDiscoveryUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zigbeeDeviceDiscoveryUpdate(data: any, hue: Hue) {
+ const zigbeeDeviceDiscovery = hue.zigbeeDeviceDiscoveries.cache.get(data.id);
+ if (!zigbeeDeviceDiscovery) return;
+
+ const clone = zigbeeDeviceDiscovery._update(data);
+
+ return () => hue.emit(Events.ZigbeeDeviceDiscoveryUpdate, zigbeeDeviceDiscovery, clone);
+}
diff --git a/src/connections/events/update/ZoneUpdate.ts b/src/connections/events/update/ZoneUpdate.ts
new file mode 100644
index 0000000..b4e8469
--- /dev/null
+++ b/src/connections/events/update/ZoneUpdate.ts
@@ -0,0 +1,11 @@
+import { Hue } from '../../../hue/Hue';
+import { Events } from '../../../hue/HueEvents';
+
+export default function zoneUpdate(data: any, hue: Hue) {
+ const zone = hue.zones.cache.get(data.id);
+ if (!zone) return;
+
+ const clone = zone._update(data);
+
+ return () => hue.emit(Events.ZoneUpdate, zone, clone);
+}
diff --git a/src/hue/Hue.ts b/src/hue/Hue.ts
new file mode 100644
index 0000000..5f0bc19
--- /dev/null
+++ b/src/hue/Hue.ts
@@ -0,0 +1,109 @@
+import { EventEmitter } from 'node:events';
+import { Events, HueEvents } from './HueEvents';
+import { Rest } from '../connections/Rest';
+import { Sse } from '../connections/Sse';
+import { ResourceType } from '../api/ResourceType';
+import { NarrowResource } from '../structures/Resource';
+import { LightManager } from '../managers/LightManager';
+import { DeviceManager } from '../managers/DeviceManager';
+import { RoomManager } from '../managers/RoomManager';
+import { ZoneManager } from '../managers/ZoneManager';
+import { DevicePowerManager } from '../managers/DevicePowerManager';
+import { GroupedLightManager } from '../managers/GroupedLightManager';
+import { SceneManager } from '../managers/SceneManager';
+import { MotionManager } from '../managers/MotionManager';
+import { ZigbeeConnectivityManager } from '../managers/ZigbeeConnectivityManager';
+import { ZigbeeDeviceDiscoveryManager } from '../managers/ZigbeeDeviceDiscoveryManager';
+import { BridgeManager } from '../managers/BridgeManager';
+
+export const CA =
+ '-----BEGIN CERTIFICATE-----\n' +
+ 'MIICMjCCAdigAwIBAgIUO7FSLbaxikuXAljzVaurLXWmFw4wCgYIKoZIzj0EAwIw\n' +
+ 'OTELMAkGA1UEBhMCTkwxFDASBgNVBAoMC1BoaWxpcHMgSHVlMRQwEgYDVQQDDAty\n' +
+ 'b290LWJyaWRnZTAiGA8yMDE3MDEwMTAwMDAwMFoYDzIwMzgwMTE5MDMxNDA3WjA5\n' +
+ 'MQswCQYDVQQGEwJOTDEUMBIGA1UECgwLUGhpbGlwcyBIdWUxFDASBgNVBAMMC3Jv\n' +
+ 'b3QtYnJpZGdlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjNw2tx2AplOf9x86\n' +
+ 'aTdvEcL1FU65QDxziKvBpW9XXSIcibAeQiKxegpq8Exbr9v6LBnYbna2VcaK0G22\n' +
+ 'jOKkTqOBuTCBtjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNV\n' +
+ 'HQ4EFgQUZ2ONTFrDT6o8ItRnKfqWKnHFGmQwdAYDVR0jBG0wa4AUZ2ONTFrDT6o8\n' +
+ 'ItRnKfqWKnHFGmShPaQ7MDkxCzAJBgNVBAYTAk5MMRQwEgYDVQQKDAtQaGlsaXBz\n' +
+ 'IEh1ZTEUMBIGA1UEAwwLcm9vdC1icmlkZ2WCFDuxUi22sYpLlwJY81Wrqy11phcO\n' +
+ 'MAoGCCqGSM49BAMCA0gAMEUCIEBYYEOsa07TH7E5MJnGw557lVkORgit2Rm1h3B2\n' +
+ 'sFgDAiEA1Fj/C3AN5psFMjo0//mrQebo0eKd3aWRx+pQY08mk48=\n' +
+ '-----END CERTIFICATE-----';
+
+export interface HueConnectionOptions {
+ applicationKey: string;
+}
+
+export interface LocalHueConnectionsOptions extends HueConnectionOptions {
+ ip: string;
+}
+
+export interface ExternalHueConnectionOptions extends HueConnectionOptions {
+ accessToken: string;
+}
+
+export interface HueOptions {
+ connection: LocalHueConnectionsOptions | ExternalHueConnectionOptions;
+}
+
+export interface Hue {
+ on: (event: T, listener: (...args: HueEvents[T]) => any) => this;
+ once: (event: T, listener: (...args: HueEvents[T]) => any) => this;
+ off: (event: T, listener: (...args: HueEvents[T]) => any) => this;
+ removeAllListeners: (event?: T) => this;
+}
+
+export class Hue extends EventEmitter {
+ public readonly options: HueOptions;
+ public readonly lights = new LightManager(this);
+ public readonly devices = new DeviceManager(this);
+ public readonly rooms = new RoomManager(this);
+ public readonly zones = new ZoneManager(this);
+ public readonly groupedLights = new GroupedLightManager(this);
+ public readonly devicePowers = new DevicePowerManager(this);
+ public readonly scenes = new SceneManager(this);
+ public readonly motions = new MotionManager(this);
+ public readonly zigbeeConnectivities = new ZigbeeConnectivityManager(this);
+ public readonly zigbeeDeviceDiscoveries = new ZigbeeDeviceDiscoveryManager(this);
+ public readonly bridges = new BridgeManager(this);
+ public readonly _rest = new Rest(this);
+ public readonly _sse = new Sse(this);
+
+ public constructor(options: HueOptions) {
+ super();
+ this.options = options;
+ }
+
+ get _url(): string {
+ if ('ip' in this.options.connection) return `https://${this.options.connection.ip}:443`;
+ else return `https://api.meethue.com/route`;
+ }
+
+ public async connect(): Promise {
+ const data = await this._rest.get('/resource');
+
+ for (const resource of data) {
+ this._create(resource);
+ }
+
+ await this._sse.connect();
+
+ this.emit(Events.Ready, this);
+ }
+
+ public _create(data: any): NarrowResource | undefined {
+ if (data.type === ResourceType.Light) return this.lights._add(data);
+ else if (data.type === ResourceType.Device) return this.devices._add(data);
+ else if (data.type === ResourceType.Room) return this.rooms._add(data);
+ else if (data.type === ResourceType.Zone) return this.zones._add(data);
+ else if (data.type === ResourceType.GroupedLight) return this.groupedLights._add(data);
+ else if (data.type === ResourceType.DevicePower) return this.devicePowers._add(data);
+ else if (data.type === ResourceType.Scene) return this.scenes._add(data);
+ else if (data.type === ResourceType.Motion) return this.motions._add(data);
+ else if (data.type === ResourceType.ZigbeeConnectivity) return this.zigbeeConnectivities._add(data);
+ else if (data.type === ResourceType.ZigbeeDeviceDiscovery) return this.zigbeeDeviceDiscoveries._add(data);
+ else if (data.type === ResourceType.Bridge) return this.bridges._add(data);
+ }
+}
diff --git a/src/bridge/BridgeEvents.ts b/src/hue/HueEvents.ts
similarity index 51%
rename from src/bridge/BridgeEvents.ts
rename to src/hue/HueEvents.ts
index 4aab7c4..8b30307 100644
--- a/src/bridge/BridgeEvents.ts
+++ b/src/hue/HueEvents.ts
@@ -1,4 +1,4 @@
-import { Bridge } from './Bridge';
+import { Hue } from './Hue';
import { Request, Response } from '../connections/Rest';
import { Light } from '../structures/Light';
import { NarrowResource } from '../structures/Resource';
@@ -7,6 +7,11 @@ import { Room } from '../structures/Room';
import { Zone } from '../structures/Zone';
import { Scene } from '../structures/Scene';
import { GroupedLight } from '../structures/GroupedLight';
+import { DevicePower } from '../structures/DevicePower';
+import { Motion } from '../structures/Motion';
+import { ZigbeeConnectivity } from '../structures/ZigbeeConnectivity';
+import { ZigbeeDeviceDiscovery } from '../structures/ZigbeeDeviceDiscovery';
+import { Bridge } from '../structures/Bridge';
export const Events = {
Ready: 'ready',
@@ -36,10 +41,25 @@ export const Events = {
ZoneAdd: 'zoneAdd',
ZoneUpdate: 'zoneUpdate',
ZoneDelete: 'zoneDelete',
+ DevicePowerAdd: 'devicePowerAdd',
+ DevicePowerUpdate: 'devicePowerUpdate',
+ DevicePowerDelete: 'devicePowerDelete',
+ MotionAdd: 'motionAdd',
+ MotionUpdate: 'motionUpdate',
+ MotionDelete: 'motionDelete',
+ ZigbeeConnectivityAdd: 'zigbeeConnectivityAdd',
+ ZigbeeConnectivityUpdate: 'zigbeeConnectivityUpdate',
+ ZigbeeConnectivityDelete: 'zigbeeConnectivityDelete',
+ ZigbeeDeviceDiscoveryAdd: 'zigbeeDeviceDiscoveryAdd',
+ ZigbeeDeviceDiscoveryUpdate: 'zigbeeDeviceDiscoveryUpdate',
+ ZigbeeDeviceDiscoveryDelete: 'zigbeeDeviceDiscoveryDelete',
+ BridgeAdd: 'bridgeAdd',
+ BridgeUpdate: 'bridgeUpdate',
+ BridgeDelete: 'bridgeDelete',
} as const;
-export interface BridgeEvents {
- [Events.Ready]: [bridge: Bridge];
+export interface HueEvents {
+ [Events.Ready]: [bridge: Hue];
[Events.Error]: [error: Error];
[Events.Raw]: [data: Record];
[Events.Debug]: [debug: string];
@@ -64,6 +84,27 @@ export interface BridgeEvents {
[Events.SceneUpdate]: [newScene: Scene, oldScene: Scene];
[Events.SceneDelete]: [scene: Scene];
[Events.ZoneAdd]: [zone: Zone];
- [Events.ZoneUpdate]: [newZone: Room, oldZone: Zone];
+ [Events.ZoneUpdate]: [newZone: Zone, oldZone: Zone];
[Events.ZoneDelete]: [zone: Zone];
+ [Events.DevicePowerAdd]: [devicePower: DevicePower];
+ [Events.DevicePowerUpdate]: [newDevicePower: DevicePower, oldDevicePower: DevicePower];
+ [Events.DevicePowerDelete]: [devicePower: DevicePower];
+ [Events.MotionAdd]: [motion: Motion];
+ [Events.MotionUpdate]: [newMotion: Motion, oldMotion: Motion];
+ [Events.MotionDelete]: [motion: Motion];
+ [Events.ZigbeeConnectivityAdd]: [zigbeeConnectivity: ZigbeeConnectivity];
+ [Events.ZigbeeConnectivityUpdate]: [
+ newZigbeeConnectivity: ZigbeeConnectivity,
+ oldZigbeeConnectivity: ZigbeeConnectivity,
+ ];
+ [Events.ZigbeeConnectivityDelete]: [zigbeeConnectivity: ZigbeeConnectivity];
+ [Events.ZigbeeDeviceDiscoveryAdd]: [zigbeeDeviceDiscovery: ZigbeeDeviceDiscovery];
+ [Events.ZigbeeDeviceDiscoveryUpdate]: [
+ newZigbeeDeviceDiscovery: ZigbeeDeviceDiscovery,
+ oldZigbeeDeviceDiscovery: ZigbeeDeviceDiscovery,
+ ];
+ [Events.ZigbeeDeviceDiscoveryDelete]: [zigbeeDeviceDiscovery: ZigbeeDeviceDiscovery];
+ [Events.BridgeAdd]: [bridge: Bridge];
+ [Events.BridgeUpdate]: [newBridge: Bridge, oldBridge: Bridge];
+ [Events.BridgeDelete]: [bridge: Bridge];
}
diff --git a/src/index.ts b/src/index.ts
index d28093a..165f163 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,6 +1,6 @@
-// Bridge
-export * from './bridge/Bridge';
-export * from './bridge/BridgeEvents';
+// Hue
+export * from './hue/Hue';
+export * from './hue/HueEvents';
// Connections
export * from './connections/Rest';
@@ -8,53 +8,40 @@ export * from './connections/Limit';
export * from './connections/Sse';
// Managers
-export * from './managers/ResourceManager';
+export * from './managers/Manager';
+export * from './managers/LightManager';
+export * from './managers/DeviceManager';
+export * from './managers/RoomManager';
+export * from './managers/ZoneManager';
+export * from './managers/GroupedLightManager';
+export * from './managers/DevicePowerManager';
+export * from './managers/MotionManager';
+export * from './managers/SceneManager';
// Resources
export * from './structures/Device';
-export * from './structures/DimmableLight';
+export * from './structures/DevicePower';
export * from './structures/GroupedLight';
export * from './structures/Light';
-export * from './structures/MirekLight';
export * from './structures/NamedResource';
export * from './structures/Resource';
export * from './structures/Room';
export * from './structures/Scene';
-export * from './structures/XyLight';
-export * from './structures/XysLight';
export * from './structures/Zone';
+export * from './structures/Motion';
// API
-export * from './api/ApiResourceType';
+export * from './api/ResourceType';
export * from './api/ResourceIdentifier';
-
-export * from './api/device/get';
-export * from './api/device/put';
-export * from './api/grouped_light/get';
-export * from './api/grouped_light/put';
-export * from './api/light/get';
-export * from './api/light/put';
-export * from './api/room/get';
-export * from './api/room/post';
-export * from './api/room/put';
-export * from './api/scene/get';
-export * from './api/scene/post';
-export * from './api/scene/put';
-export * from './api/zone/get';
-export * from './api/zone/post';
-export * from './api/zone/put';
+export * from './api/ArcheType';
// Util
-export * from './util/util';
export * from './util/clone';
export * from './util/merge';
export * from './util/resourceIdentifier';
-export * from './util/color/xy';
-export * from './util/color/gamut';
-export * from './util/color/rgb';
-export * from './util/color/hex';
-
-// TODO dimming_delta & color_temperature_delta
-// TODO archetypes
-// TODO delete resources
+// Color
+export * from './color/xy';
+export * from './color/gamut';
+export * from './color/rgb';
+export * from './color/hex';
diff --git a/src/managers/BridgeManager.ts b/src/managers/BridgeManager.ts
new file mode 100644
index 0000000..dc35264
--- /dev/null
+++ b/src/managers/BridgeManager.ts
@@ -0,0 +1,8 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { Bridge } from '../structures/Bridge';
+
+export class BridgeManager extends Manager {
+ type = ResourceType.Bridge;
+ holds = Bridge;
+}
diff --git a/src/managers/DeviceManager.ts b/src/managers/DeviceManager.ts
new file mode 100644
index 0000000..a0ad852
--- /dev/null
+++ b/src/managers/DeviceManager.ts
@@ -0,0 +1,23 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { Device, DeviceEditOptions } from '../structures/Device';
+import { transformMetadataWithArcheType } from '../util/Transformers';
+
+export class DeviceManager extends Manager {
+ type = ResourceType.Device;
+ holds = Device;
+
+ public async identify(id: string): Promise {
+ await this._put(id, { identify: { action: 'identify' } });
+ }
+
+ public async edit(id: string, options: DeviceEditOptions): Promise {
+ await this._put(id, {
+ metadata: transformMetadataWithArcheType(options),
+ });
+ }
+
+ public async delete(id: string): Promise {
+ await this._delete(id);
+ }
+}
diff --git a/src/managers/DevicePowerManager.ts b/src/managers/DevicePowerManager.ts
new file mode 100644
index 0000000..1504057
--- /dev/null
+++ b/src/managers/DevicePowerManager.ts
@@ -0,0 +1,8 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { DevicePower } from '../structures/DevicePower';
+
+export class DevicePowerManager extends Manager {
+ type = ResourceType.DevicePower;
+ holds = DevicePower;
+}
diff --git a/src/managers/GroupedLightManager.ts b/src/managers/GroupedLightManager.ts
new file mode 100644
index 0000000..4a30fd0
--- /dev/null
+++ b/src/managers/GroupedLightManager.ts
@@ -0,0 +1,18 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { GroupedLight, GroupedLightEditOptions } from '../structures/GroupedLight';
+import { transformColor, transformColorTemperature, transformDimming, transformOn } from '../util/Transformers';
+
+export class GroupedLightManager extends Manager {
+ type = ResourceType.GroupedLight;
+ holds = GroupedLight;
+
+ public async edit(id: string, options: GroupedLightEditOptions): Promise {
+ await this._put(id, {
+ on: transformOn(options.on),
+ dimming: transformDimming(options.brightness),
+ color_temperature: transformColorTemperature(options.mirek),
+ color: transformColor(options.color),
+ });
+ }
+}
diff --git a/src/managers/LightManager.ts b/src/managers/LightManager.ts
new file mode 100644
index 0000000..c1b4eea
--- /dev/null
+++ b/src/managers/LightManager.ts
@@ -0,0 +1,33 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { Light, LightEditOptions } from '../structures/Light';
+import {
+ transformColor,
+ transformColorTemperature,
+ transformDimming,
+ transformDynamics,
+ transformEffects,
+ transformGradient,
+ transformMetadataWithArcheType,
+ transformOn,
+ transformTimedEffects,
+} from '../util/Transformers';
+
+export class LightManager extends Manager {
+ type = ResourceType.Light;
+ holds = Light;
+
+ public async edit(id: string, options: LightEditOptions): Promise {
+ await this._put(id, {
+ metadata: transformMetadataWithArcheType(options),
+ on: transformOn(options.on),
+ dynamics: transformDynamics(options.dynamics),
+ effects: transformEffects(options.effect),
+ timed_effects: transformTimedEffects(options.timedEffects),
+ dimming: transformDimming(options.brightness ?? options.color?.z),
+ color_temperature: transformColorTemperature(options.colorTemperature),
+ color: transformColor(options.color),
+ gradient: transformGradient(options.gradient),
+ });
+ }
+}
diff --git a/src/managers/Manager.ts b/src/managers/Manager.ts
new file mode 100644
index 0000000..6de4aa7
--- /dev/null
+++ b/src/managers/Manager.ts
@@ -0,0 +1,48 @@
+import { Collection } from '@discordjs/collection';
+import { ResourceType, ResourceTypeGet, ResourceTypePost, ResourceTypePut } from '../api/ResourceType';
+import { NarrowResource } from '../structures/Resource';
+import { ResourceIdentifier } from '../api/ResourceIdentifier';
+import { Hue } from '../hue/Hue';
+
+export type By = string | ResourceIdentifier;
+
+export type Force = B extends true
+ ? NarrowResource
+ : NarrowResource | undefined;
+
+export type ResourceConstructorSignature = new (bridge: Hue, data: any) => NarrowResource;
+
+export abstract class Manager {
+ public readonly hue: Hue;
+ public readonly cache = new Collection>();
+ public abstract type: ResourceType;
+ public abstract holds: ResourceConstructorSignature;
+
+ public constructor(hue: Hue) {
+ this.hue = hue;
+ }
+
+ public _add(data: any): NarrowResource {
+ const resource = new this.holds(this.hue, data);
+
+ this.cache.set(data.id, resource);
+
+ return resource;
+ }
+
+ public async _get(id: string): Promise> {
+ return await this.hue._rest.get(`/resource/${this.type}/${id}`);
+ }
+
+ public async _put(id: string, data: ResourceTypePut): Promise {
+ return await this.hue._rest.put(`/resource/${this.type}/${id}`, data);
+ }
+
+ public async _post(data: ResourceTypePost): Promise {
+ return await this.hue._rest.post(`/resource/${this.type}`, data);
+ }
+
+ public async _delete(id: string): Promise {
+ return await this.hue._rest.delete(`/resource/${this.type}/${id}`);
+ }
+}
diff --git a/src/managers/MotionManager.ts b/src/managers/MotionManager.ts
new file mode 100644
index 0000000..65fe9d3
--- /dev/null
+++ b/src/managers/MotionManager.ts
@@ -0,0 +1,14 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { Motion, MotionEditOptions } from '../structures/Motion';
+
+export class MotionManager extends Manager {
+ type = ResourceType.Motion;
+ holds = Motion;
+
+ public async edit(id: string, options: MotionEditOptions): Promise {
+ await this._put(id, {
+ enabled: options.enabled,
+ });
+ }
+}
diff --git a/src/managers/ResourceManager.ts b/src/managers/ResourceManager.ts
deleted file mode 100644
index 5e7b3cd..0000000
--- a/src/managers/ResourceManager.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-import { Bridge } from '../bridge/Bridge';
-import { Collection } from '@discordjs/collection';
-import { NarrowResource } from '../structures/Resource';
-import { ApiResourceType, ApiResourceTypeGet } from '../api/ApiResourceType';
-import { Light, LightCapabilities, NarrowLight } from '../structures/Light';
-import { XyLight } from '../structures/XyLight';
-import { MirekLight } from '../structures/MirekLight';
-import { DimmableLight } from '../structures/DimmableLight';
-import { Scene } from '../structures/Scene';
-import { XysLight } from '../structures/XysLight';
-import { ResourceIdentifier } from '../api/ResourceIdentifier';
-import { Room } from '../structures/Room';
-import { Zone } from '../structures/Zone';
-import { Device } from '../structures/Device';
-import { GroupedLight } from '../structures/GroupedLight';
-
-export const RESOURCES = {
- [ApiResourceType.Light]: {
- [LightCapabilities.None]: Light,
- [LightCapabilities.Dimming]: DimmableLight,
- [LightCapabilities.Mirek]: MirekLight,
- [LightCapabilities.Xy]: XyLight,
- [LightCapabilities.Xys]: XysLight,
- },
- [ApiResourceType.Device]: Device,
- [ApiResourceType.BridgeHome]: undefined,
- [ApiResourceType.Room]: Room,
- [ApiResourceType.Zone]: Zone,
- [ApiResourceType.Button]: undefined,
- [ApiResourceType.Temperature]: undefined,
- [ApiResourceType.LightLevel]: undefined,
- [ApiResourceType.Motion]: undefined,
- [ApiResourceType.Entertainment]: undefined,
- [ApiResourceType.GroupedLight]: GroupedLight,
- [ApiResourceType.DevicePower]: undefined,
- [ApiResourceType.ZigbeeBridgeConnectivity]: undefined,
- [ApiResourceType.ZgpConnectivity]: undefined,
- [ApiResourceType.Bridge]: undefined,
- [ApiResourceType.Homekit]: undefined,
- [ApiResourceType.Scene]: Scene,
- [ApiResourceType.EntertainmentConfiguration]: undefined,
- [ApiResourceType.PublicImage]: undefined,
- [ApiResourceType.BehaviourScript]: undefined,
- [ApiResourceType.BehaviourInstance]: undefined,
- [ApiResourceType.Geofence]: undefined,
- [ApiResourceType.GeofenceClient]: undefined,
- [ApiResourceType.Geolocation]: undefined,
-};
-
-export type Resolvable =
- | NarrowResource
- | string
- | ResourceIdentifier;
-
-export interface ResolveOptions<
- T extends ApiResourceType = ApiResourceType,
- L extends LightCapabilities | undefined = undefined,
-> {
- type?: T;
- light?: {
- capableOf?: L;
- };
- force?: boolean;
-}
-
-export type Resolved = T extends ApiResourceType.Light
- ? NarrowLight
- : NarrowResource;
-
-export class ResourceManager {
- public readonly bridge: Bridge;
- public readonly cache = new Collection();
-
- constructor(bridge: Bridge) {
- this.bridge = bridge;
- }
-
- public getById(id?: string, options?: { force: true; type?: T }): NarrowResource;
- public getById(
- id?: string,
- options?: { force?: boolean; type?: T },
- ): NarrowResource | undefined;
- public getById(id?: string, options: { force?: boolean; type?: T } = {}) {
- const resource = this.cache.get(id ?? '');
-
- if (!resource && options.force) throw new Error(`Nonexistent ${options.type ?? 'unknown type'}: ${id}`);
- if (resource && resource.type !== options.type)
- throw new Error(`${resource.type}: ${id} rtype mismatch (requested: ${options.type}, actual: ${options.type})`);
-
- return resource;
- }
-
- public getByIdentifier(
- identifier?: ResourceIdentifier,
- options?: { force: true; type?: U },
- ): NarrowResource;
- public getByIdentifier(
- identifier?: ResourceIdentifier,
- options?: { force?: boolean; type?: U },
- ): NarrowResource | undefined;
- public getByIdentifier(
- identifier?: ResourceIdentifier,
- options: { force?: boolean; type?: U } = {},
- ) {
- return this.getById(identifier?.rid, { type: options.type ?? identifier?.rtype, force: options.force });
- }
-
- public getByIdentifiers(identifiers: ResourceIdentifier[]): NarrowResource[] {
- return identifiers
- .map((identifier) => this.getByIdentifier(identifier))
- .filter((resource) => resource !== null && resource !== undefined);
- }
-
- public getByName(name?: string, options?: { force: true; type?: T }): NarrowResource;
- public getByName(
- name?: string,
- options?: { force?: boolean; type?: T },
- ): NarrowResource | undefined;
- public getByName(name?: string, options: { force?: boolean; type?: T } = {}) {
- const resource = this.cache.find(
- (resource) => 'name' in resource && resource.name === name && resource.type === options.type,
- );
-
- if (!resource && options.force) throw new Error(`Nonexistent ${name}`);
-
- return resource;
- }
-
- public getIdentifierByName(
- name?: string,
- options?: { force: true; type?: T },
- ): ResourceIdentifier;
- public getIdentifierByName(
- name?: string,
- options?: { force?: boolean; type?: T },
- ): ResourceIdentifier | undefined;
- public getIdentifierByName(name?: string, options: { force?: boolean; type?: T } = {}) {
- const resource = this.getByName(name, options);
-
- return resource?.identifier;
- }
-
- public _create(data: ApiResourceTypeGet): NarrowResource | undefined {
- const resourceClass = this._resolveResource(data);
- if (!resourceClass) return;
-
- const resource = new resourceClass(this.bridge, data);
- this.cache.set(resource.id, resource);
-
- return resource;
- }
-
- private _resolveResource(
- data: ApiResourceTypeGet,
- ): (new (bridge: Bridge, data: any) => NarrowResource) | undefined {
- if (data.type && data.type === ApiResourceType.Light)
- return RESOURCES[ApiResourceType.Light][this._resolveLightCapabilities(data)];
- else if (data.type) return RESOURCES[data.type];
- }
-
- private _resolveLightCapabilities(data: ApiResourceTypeGet): LightCapabilities {
- if (data.gradient) return LightCapabilities.Xys;
- if (data.color) return LightCapabilities.Xy;
- if (data.color_temperature) return LightCapabilities.Mirek;
- if (data.dimming) return LightCapabilities.Dimming;
-
- return LightCapabilities.None;
- }
-}
diff --git a/src/managers/RoomManager.ts b/src/managers/RoomManager.ts
new file mode 100644
index 0000000..df001ec
--- /dev/null
+++ b/src/managers/RoomManager.ts
@@ -0,0 +1,29 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { Room, RoomCreateOptions, RoomEditOptions } from '../structures/Room';
+import { transformChildren, transformMetadataWithArcheType } from '../util/Transformers';
+
+export class RoomManager extends Manager {
+ type = ResourceType.Room;
+ holds = Room;
+
+ public async create(options: RoomCreateOptions): Promise {
+ const identifiers = await this._post({
+ metadata: transformMetadataWithArcheType(options)!,
+ children: transformChildren(options.children)!,
+ });
+
+ return identifiers?.[0]?.rid;
+ }
+
+ public async edit(id: string, options: RoomEditOptions): Promise {
+ await this._put(id, {
+ metadata: transformMetadataWithArcheType(options),
+ children: transformChildren(options.children),
+ });
+ }
+
+ public async delete(id: string): Promise {
+ await this._delete(id);
+ }
+}
diff --git a/src/managers/SceneManager.ts b/src/managers/SceneManager.ts
new file mode 100644
index 0000000..6d23d55
--- /dev/null
+++ b/src/managers/SceneManager.ts
@@ -0,0 +1,36 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { Scene, SceneCreateOptions, SceneEditOptions } from '../structures/Scene';
+import { transformMetadata, transformRecall, transformSceneActions } from '../util/Transformers';
+import { createResourceIdentifier } from '../util/resourceIdentifier';
+import { ifNotNull } from '../util/ifNotNull';
+
+export class SceneManager extends Manager {
+ type = ResourceType.Scene;
+ holds = Scene;
+
+ public async create(groupId: string, options: SceneCreateOptions): Promise {
+ const group = this.hue.zones.cache.get(groupId) ?? this.hue.rooms.cache.get(groupId);
+ if (!group) return;
+
+ const identifiers = await this._post({
+ group: createResourceIdentifier(groupId, group.type),
+ metadata: transformMetadata(options)!,
+ actions: transformSceneActions(options.actions)!,
+ });
+
+ return identifiers?.[0]?.rid;
+ }
+ public async edit(id: string, options: SceneEditOptions): Promise {
+ await this._put(id, {
+ metadata: transformMetadata(options),
+ speed: ifNotNull(options.speed, () => options.speed),
+ recall: transformRecall(options.recall),
+ actions: transformSceneActions(options.actions),
+ });
+ }
+
+ public async delete(id: string): Promise {
+ await this._delete(id);
+ }
+}
diff --git a/src/managers/ZigbeeConnectivityManager.ts b/src/managers/ZigbeeConnectivityManager.ts
new file mode 100644
index 0000000..37d7d54
--- /dev/null
+++ b/src/managers/ZigbeeConnectivityManager.ts
@@ -0,0 +1,8 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { ZigbeeConnectivity } from '../structures/ZigbeeConnectivity';
+
+export class ZigbeeConnectivityManager extends Manager {
+ type = ResourceType.ZigbeeConnectivity;
+ holds = ZigbeeConnectivity;
+}
diff --git a/src/managers/ZigbeeDeviceDiscoveryManager.ts b/src/managers/ZigbeeDeviceDiscoveryManager.ts
new file mode 100644
index 0000000..712ad81
--- /dev/null
+++ b/src/managers/ZigbeeDeviceDiscoveryManager.ts
@@ -0,0 +1,19 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import {
+ ZigbeeDeviceDiscovery,
+ ZigbeeDeviceDiscoveryActionType,
+ ZigbeeDeviceDiscoveryEdit,
+} from '../structures/ZigbeeDeviceDiscovery';
+import { transformAction } from '../util/Transformers';
+
+export class ZigbeeDeviceDiscoveryManager extends Manager {
+ type = ResourceType.ZigbeeDeviceDiscovery;
+ holds = ZigbeeDeviceDiscovery;
+
+ public async edit(id: string, options: ZigbeeDeviceDiscoveryEdit) {
+ await this._put(id, {
+ action: transformAction({ ...options, actionType: ZigbeeDeviceDiscoveryActionType.Search }),
+ });
+ }
+}
diff --git a/src/managers/ZoneManager.ts b/src/managers/ZoneManager.ts
new file mode 100644
index 0000000..eaf0733
--- /dev/null
+++ b/src/managers/ZoneManager.ts
@@ -0,0 +1,29 @@
+import { Manager } from './Manager';
+import { ResourceType } from '../api/ResourceType';
+import { Zone, ZoneCreateOptions, ZoneEditOptions } from '../structures/Zone';
+import { transformChildren, transformMetadataWithArcheType } from '../util/Transformers';
+
+export class ZoneManager extends Manager {
+ type = ResourceType.Zone;
+ holds = Zone;
+
+ public async create(options: ZoneCreateOptions): Promise {
+ const identifiers = await this._post({
+ metadata: transformMetadataWithArcheType(options)!,
+ children: transformChildren(options.children)!,
+ });
+
+ return identifiers?.[0]?.rid;
+ }
+
+ public async edit(id: string, options: ZoneEditOptions): Promise {
+ await this._put(id, {
+ metadata: transformMetadataWithArcheType(options),
+ children: transformChildren(options.children),
+ });
+ }
+
+ public async delete(id: string): Promise {
+ await this._delete(id);
+ }
+}
diff --git a/src/structures/ArcheTypeResource.ts b/src/structures/ArcheTypeResource.ts
new file mode 100644
index 0000000..cdacae9
--- /dev/null
+++ b/src/structures/ArcheTypeResource.ts
@@ -0,0 +1,29 @@
+import { NamedResource, NamedResourceEditOptions } from './NamedResource';
+import { ResourceType, ResourceTypeGet } from '../api/ResourceType';
+import { ArcheType } from '../api/ArcheType';
+import { Hue } from '../hue/Hue';
+
+export interface ArcheTypeResourceEditOptions extends NamedResourceEditOptions {
+ archeType?: ArcheType;
+}
+
+export type ArcheTypeResourceCreateOptions = Required;
+
+export abstract class ArcheTypeResource extends NamedResource {
+ public data: ResourceTypeGet & { metadata: { name: string; archetype: ArcheType } };
+
+ constructor(bridge: Hue, data: ResourceTypeGet & { metadata: { name: string; archetype: ArcheType } }) {
+ super(bridge, data);
+ this.data = data;
+ }
+
+ get archeType(): ArcheType {
+ return this.data.metadata.archetype;
+ }
+
+ public async setArcheType(archeType: ArcheType): Promise {
+ await this.edit({ archeType });
+ }
+
+ public abstract edit(options: { name?: string; archeType?: ArcheType }): Promise;
+}
diff --git a/src/structures/Bridge.ts b/src/structures/Bridge.ts
new file mode 100644
index 0000000..60fee47
--- /dev/null
+++ b/src/structures/Bridge.ts
@@ -0,0 +1,23 @@
+import { Resource } from './Resource';
+import { ResourceType } from '../api/ResourceType';
+import { BridgeManager } from '../managers/BridgeManager';
+
+export class Bridge extends Resource {
+ type = ResourceType.Bridge;
+
+ get manager(): BridgeManager {
+ return this.hue.bridges;
+ }
+
+ get ownerId(): string {
+ return this.data.owner.rid;
+ }
+
+ get bridgeId(): string {
+ return this.data.bridge_id;
+ }
+
+ get timeZone(): string {
+ return this.data.time_zone.time_zone;
+ }
+}
diff --git a/src/structures/Device.ts b/src/structures/Device.ts
index ee2596d..3e407f9 100644
--- a/src/structures/Device.ts
+++ b/src/structures/Device.ts
@@ -1,14 +1,15 @@
-import { NamedResource } from './NamedResource';
-import { ApiResourceType } from '../api/ApiResourceType';
-import { ResourceIdentifier } from '../api/ResourceIdentifier';
-import { NarrowResource } from './Resource';
+import { ResourceType } from '../api/ResourceType';
+import { ArcheTypeResource, ArcheTypeResourceEditOptions } from './ArcheTypeResource';
+import { DeviceManager } from '../managers/DeviceManager';
-export interface DeviceEditOptions {
- name: string;
-}
+export type DeviceEditOptions = ArcheTypeResourceEditOptions;
+
+export class Device extends ArcheTypeResource {
+ type = ResourceType.Device;
-export class Device extends NamedResource {
- type = ApiResourceType.Device;
+ get manager(): DeviceManager {
+ return this.hue.devices;
+ }
get modelId(): string {
return this.data.product_data.model_id;
@@ -34,19 +35,19 @@ export class Device extends NamedResource {
return this.data.product_data.hardware_platform_type;
}
- get services(): NarrowResource[] {
- return this.bridge.resources.getByIdentifiers(this.serviceIdentifiers);
- }
-
- get serviceIdentifiers(): ResourceIdentifier[] {
- return this.data.services;
+ get serviceIds(): string[] {
+ return this.data.services.map((service) => service.rid);
}
public async identify(): Promise {
- await this._put({ identify: { action: 'identify' } });
+ await this.manager.identify(this.id);
}
public async edit(options: DeviceEditOptions): Promise {
- await this._put({ metadata: options.name ? { name: options.name } : undefined });
+ await this.manager.edit(this.id, options);
+ }
+
+ public async delete(): Promise {
+ await this.manager.delete(this.id);
}
}
diff --git a/src/structures/DevicePower.ts b/src/structures/DevicePower.ts
new file mode 100644
index 0000000..f2d08e6
--- /dev/null
+++ b/src/structures/DevicePower.ts
@@ -0,0 +1,28 @@
+import { Resource } from './Resource';
+import { ResourceType } from '../api/ResourceType';
+import { DevicePowerManager } from '../managers/DevicePowerManager';
+
+export enum DevicePowerBatteryState {
+ Normal = 'normal',
+ Low = 'low',
+ Critical = 'critical',
+}
+
+export class DevicePower extends Resource {
+ type = ResourceType.DevicePower;
+
+ get manager(): DevicePowerManager {
+ return this.hue.devicePowers;
+ }
+ get ownerId(): string {
+ return this.data.owner.rid;
+ }
+
+ get batteryState(): DevicePowerBatteryState {
+ return this.data.power_state.battery_state as DevicePowerBatteryState;
+ }
+
+ get batteryLevel(): number {
+ return this.data.power_state.battery_level;
+ }
+}
diff --git a/src/structures/DimmableLight.ts b/src/structures/DimmableLight.ts
deleted file mode 100644
index e939488..0000000
--- a/src/structures/DimmableLight.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Light, LightCapabilities, LightEditOptions } from './Light';
-import { ApiResourceType, ApiResourceTypePut } from '../api/ApiResourceType';
-
-export interface DimmableLightEditOptions extends LightEditOptions {
- brightness?: number;
-}
-
-export class DimmableLight extends Light {
- public capabilities = LightCapabilities.Dimming;
-
- get brightness(): number {
- return this.data.dimming!.brightness;
- }
-
- get minBrightnessLevel(): number | undefined {
- return this.data.dimming!.min_dim_level;
- }
-
- public async setBrightness(brightness: DimmableLightEditOptions['brightness'], duration?: number): Promise {
- await this.edit({ brightness, dynamics: { duration } });
- }
-
- public async edit(
- options: DimmableLightEditOptions,
- _inject?: ApiResourceTypePut,
- ): Promise {
- await super.edit(options, {
- dimming: { brightness: options.brightness },
- ..._inject,
- });
- }
-}
diff --git a/src/structures/GroupedLight.ts b/src/structures/GroupedLight.ts
index 64a4231..eaceda0 100644
--- a/src/structures/GroupedLight.ts
+++ b/src/structures/GroupedLight.ts
@@ -1,24 +1,24 @@
-import { NarrowResource, Resource } from './Resource';
-import { ApiResourceType } from '../api/ApiResourceType';
-import { ResourceIdentifier } from '../api/ResourceIdentifier';
-import { XyPoint } from '../util/color/xy';
+import { Resource } from './Resource';
+import { ResourceType } from '../api/ResourceType';
+import { XyPoint } from '../color/xy';
+import { GroupedLightManager } from '../managers/GroupedLightManager';
export interface GroupedLightEditOptions {
on?: boolean;
brightness?: number;
mirek?: number;
- xy?: XyPoint;
+ color?: XyPoint;
}
-export class GroupedLight extends Resource {
- type = ApiResourceType.GroupedLight;
+export class GroupedLight extends Resource {
+ type = ResourceType.GroupedLight;
- get owner(): NarrowResource {
- return this.bridge.resources.getByIdentifier(this.ownerIdentifier);
+ get manager(): GroupedLightManager {
+ return this.hue.groupedLights;
}
- get ownerIdentifier(): ResourceIdentifier {
- return this.data.owner;
+ get ownerId(): string {
+ return this.data.owner.rid;
}
public isOn(): boolean | undefined {
@@ -49,18 +49,11 @@ export class GroupedLight extends Resource {
await this.edit({ mirek });
}
- public async setXy(xy: GroupedLightEditOptions['xy']): Promise {
- await this.edit({ xy });
+ public async setColor(color: GroupedLightEditOptions['color']): Promise {
+ await this.edit({ color });
}
public async edit(options: GroupedLightEditOptions): Promise {
- await this._put({
- on: { on: options.on ?? true },
- dimming: options.brightness ? { brightness: options.brightness } : undefined,
- color_temperature: {
- mirek: options.mirek,
- },
- color: { xy: options.xy ? { x: options.xy.x, y: options.xy.y } : undefined },
- });
+ await this.manager.edit(this.id, options);
}
}
diff --git a/src/structures/Light.ts b/src/structures/Light.ts
index 3c21424..819b63e 100644
--- a/src/structures/Light.ts
+++ b/src/structures/Light.ts
@@ -1,9 +1,8 @@
-import { NamedResource } from './NamedResource';
-import { ApiResourceType, ApiResourceTypePut } from '../api/ApiResourceType';
-import { DimmableLight } from './DimmableLight';
-import { MirekLight } from './MirekLight';
-import { XyLight } from './XyLight';
-import { XysLight } from './XysLight';
+import { ResourceType } from '../api/ResourceType';
+import { ArcheTypeResource, ArcheTypeResourceEditOptions } from './ArcheTypeResource';
+import { LightManager } from '../managers/LightManager';
+import { checkXyInReach, createXy, getClosestXy, XyPoint } from '../color/xy';
+import { createGamut, Gamut, resolveGamutByType } from '../color/gamut';
export enum LightCapabilities {
None = 'none',
@@ -13,78 +12,133 @@ export enum LightCapabilities {
Xys = 'xys',
}
-export interface Lights {
- [LightCapabilities.None]: Light | DimmableLight | MirekLight | XyLight | XysLight;
- [LightCapabilities.Dimming]: DimmableLight | MirekLight | XyLight | XysLight;
- [LightCapabilities.Mirek]: MirekLight | XyLight | XysLight;
- [LightCapabilities.Xy]: XyLight | XysLight;
- [LightCapabilities.Xys]: XysLight;
+export interface LightIsCapableOfDimming {
+ brightness: number;
+ minBrightnessLevel: number;
}
-export type NarrowLight = Lights[T];
+export interface LightIsCapableOfColorTemperature extends LightIsCapableOfDimming {
+ colorTemperature: number;
+ minColorTemperature: number;
+ maxColorTemperature: number;
+}
-export interface LightEditOptions {
- name?: string;
- on?: boolean;
- dynamics?: {
- duration?: number;
- speed?: number;
- };
- effect?: 'fire' | 'candle' | 'no_effect';
- timedEffects?: {
- effect?: 'sunrise' | 'no_effect';
- duration?: number;
- };
+export interface LightIsCapableOfColor extends LightIsCapableOfColorTemperature {
+ color: XyPoint;
+ maxGamutRed: number;
+ maxGamutGreen: number;
+ maxGamutBlue: number;
+ gamut: Gamut;
+ colorInRange: (xy: XyPoint) => boolean;
+ colorToRange: (xy: XyPoint) => XyPoint;
+}
+
+export interface LightIsCapableOfGradient extends LightIsCapableOfColor {
+ gradient: XyPoint[];
}
-export interface LightStateOptions {
+export interface LightEditOptions extends ArcheTypeResourceEditOptions {
on?: boolean;
dynamics?: {
duration?: number;
speed?: number;
};
- effect?: 'fire' | 'candle' | 'no_effect';
+ effect?: LightEffect;
timedEffects?: {
- effect?: 'sunrise' | 'no_effect';
+ effect?: LightTimedEffect;
duration?: number;
};
+ brightness?: number;
+ colorTemperature?: number;
+ color?: XyPoint;
+ gradient?: XyPoint[];
}
-export class Light extends NamedResource {
- public capabilities: LightCapabilities = LightCapabilities.None;
- type = ApiResourceType.Light;
+export enum LightEffect {
+ Fire = 'fire',
+ Candle = 'candle',
+ NoEffect = 'no_effect',
+}
+
+export enum LightTimedEffect {
+ Sunrise = 'sunrise',
+ NoEffect = 'no_effect',
+}
+
+export enum LightMode {
+ Normal = 'normal',
+ Streaming = 'streaming',
+}
+
+// TODO add effects and timed_effects getters
+// TODO dimming_delta & color_temperature_delta
+export class Light extends ArcheTypeResource {
+ type = ResourceType.Light;
+
+ get manager(): LightManager {
+ return this.hue.lights;
+ }
public isOn(): boolean {
return this.data.on.on;
}
- get mode(): 'normal' | 'streaming' {
- return this.data.mode;
+ get brightness(): number | undefined {
+ return this.data.dimming?.brightness;
}
- public isCapableOf(capability: T): this is NarrowLight {
- const order = [LightCapabilities.Dimming, LightCapabilities.Mirek, LightCapabilities.Xy, LightCapabilities.Xys];
+ get minBrightnessLevel(): number | undefined {
+ return this.data.dimming?.min_dim_level;
+ }
+
+ get colorTemperature(): number | undefined {
+ return this.data.color_temperature?.mirek;
+ }
- const index = order.indexOf(capability);
- const left = order.slice(index, order.length);
+ get minColorTemperature(): number | undefined {
+ return this.data.color_temperature?.mirek_schema?.mirek_minimum;
+ }
+
+ get maxColorTemperature(): number | undefined {
+ return this.data.color_temperature?.mirek_schema?.mirek_maximum;
+ }
- return left.includes(this.capabilities);
+ get color(): XyPoint | undefined {
+ return this.data.color
+ ? createXy(this.data.color.xy.x, this.data.color!.xy.y, this.data.dimming!.brightness)
+ : undefined;
}
- public isCapableOfDimming(): this is NarrowLight {
- return this.isCapableOf(LightCapabilities.Dimming);
+ get maxGamutRed(): XyPoint | undefined {
+ return this.data.color
+ ? this.data.color.gamut?.red ?? resolveGamutByType(this.data.color.gamut_type).red
+ : undefined;
}
- public isCapableOfMirek(): this is NarrowLight {
- return this.isCapableOf(LightCapabilities.Mirek);
+ get maxGamutGreen(): XyPoint | undefined {
+ return this.data.color
+ ? this.data.color.gamut?.green ?? resolveGamutByType(this.data.color.gamut_type).green
+ : undefined;
}
- public isCapableOfXy(): this is NarrowLight {
- return this.isCapableOf(LightCapabilities.Xy);
+ get maxGamutBlue(): XyPoint | undefined {
+ return this.data.color
+ ? this.data.color.gamut?.blue ?? resolveGamutByType(this.data.color.gamut_type).blue
+ : undefined;
}
- public isCapableOfXys(): this is NarrowLight {
- return this.isCapableOf(LightCapabilities.Xys);
+ get gamut(): Gamut | undefined {
+ return this.maxGamutRed && this.maxGamutGreen && this.maxGamutBlue
+ ? createGamut(this.maxGamutRed, this.maxGamutGreen, this.maxGamutBlue)
+ : undefined;
+ }
+
+ get gradient(): XyPoint[] | undefined {
+ return this.data.gradient?.points?.map((point) => point.color.xy);
+ }
+
+ get mode(): LightMode {
+ return this.data.mode as LightMode;
}
public async on(duration?: number): Promise {
@@ -99,25 +153,55 @@ export class Light extends NamedResource {
await this.edit({ on: !this.isOn(), dynamics: { duration } });
}
- public async effect(effect: LightStateOptions['effect']): Promise {
+ public async effect(effect: LightEditOptions['effect']): Promise {
await this.edit({ effect });
}
- public async timedEffect(timedEffects: LightStateOptions['timedEffects']): Promise {
+ public async timedEffect(timedEffects: LightEditOptions['timedEffects']): Promise {
await this.edit({ timedEffects });
}
- public async edit(options: LightEditOptions, _inject?: ApiResourceTypePut): Promise {
- await this._put({
- metadata: options.name ? { name: options.name } : undefined,
- on: { on: options.on ?? true },
- dynamics: { duration: options.dynamics?.duration, speed: options.dynamics?.speed },
- effects: { effect: options.effect },
- timed_effects: {
- effect: options.timedEffects?.effect,
- duration: options.timedEffects?.duration,
- },
- ..._inject,
- });
+ public async setBrightness(brightness: LightEditOptions['brightness'], duration?: number): Promise {
+ await this.edit({ brightness, on: true, dynamics: { duration } });
+ }
+
+ public async setMirek(mirek: LightEditOptions['colorTemperature'], duration?: number): Promise {
+ await this.edit({ colorTemperature: mirek, on: true, dynamics: { duration } });
+ }
+
+ public colorInRange(color: XyPoint): boolean | undefined {
+ return this.gamut ? checkXyInReach(color, this.gamut) : undefined;
+ }
+
+ public colorToRange(color: XyPoint): XyPoint | undefined {
+ return this.gamut ? getClosestXy(color, this.gamut) : undefined;
+ }
+
+ public async setColor(color: LightEditOptions['color'], duration?: number): Promise {
+ await this.edit({ color, on: true, dynamics: { duration } });
+ }
+
+ public async setGradient(gradient: LightEditOptions['gradient'], duration?: number): Promise {
+ await this.edit({ gradient, on: true, dynamics: { duration } });
+ }
+
+ public async edit(options: LightEditOptions): Promise {
+ await this.manager.edit(this.id, options);
+ }
+
+ public isCapableOfDimming(): this is this & LightIsCapableOfDimming {
+ return typeof this.data.dimming != 'undefined';
+ }
+
+ public isCapableOfColorTemperature(): this is this & LightIsCapableOfColorTemperature {
+ return typeof this.data.color_temperature != 'undefined';
+ }
+
+ public isCapableOfColor(): this is this & LightIsCapableOfColor {
+ return typeof this.data.color != 'undefined';
+ }
+
+ public isCapableOfGradient(): this is this & LightIsCapableOfGradient {
+ return typeof this.data.gradient != 'undefined';
}
}
diff --git a/src/structures/MirekLight.ts b/src/structures/MirekLight.ts
deleted file mode 100644
index dee7f86..0000000
--- a/src/structures/MirekLight.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { DimmableLight, DimmableLightEditOptions } from './DimmableLight';
-import { LightCapabilities } from './Light';
-import { ApiResourceType, ApiResourceTypePut } from '../api/ApiResourceType';
-
-export interface MirekLightEditOptions extends DimmableLightEditOptions {
- mirek?: number;
-}
-
-export class MirekLight extends DimmableLight {
- public capabilities = LightCapabilities.Mirek;
-
- get mirek(): number {
- return this.data.color_temperature!.mirek;
- }
-
- get minMirek(): number {
- return this.data.color_temperature!.mirek_schema.mirek_minimum;
- }
-
- get maxMirek(): number {
- return this.data.color_temperature!.mirek_schema.mirek_maximum;
- }
-
- public async setMirek(mirek: MirekLightEditOptions['mirek'], duration?: number): Promise {
- await this.edit({ mirek, dynamics: { duration } });
- }
-
- public async edit(
- options: MirekLightEditOptions,
- _inject?: ApiResourceTypePut,
- ): Promise {
- await super.edit(options, {
- color_temperature: {
- mirek: options.mirek,
- },
- ..._inject,
- });
- }
-}
diff --git a/src/structures/Motion.ts b/src/structures/Motion.ts
new file mode 100644
index 0000000..208b29c
--- /dev/null
+++ b/src/structures/Motion.ts
@@ -0,0 +1,39 @@
+import { Resource } from './Resource';
+import { ResourceType } from '../api/ResourceType';
+import { MotionManager } from '../managers/MotionManager';
+
+export interface MotionEditOptions {
+ enabled?: boolean;
+}
+
+export class Motion extends Resource {
+ type = ResourceType.Motion;
+
+ get manager(): MotionManager {
+ return this.hue.motions;
+ }
+
+ get enabled(): boolean {
+ return this.data.enabled;
+ }
+
+ get motionDetected(): boolean {
+ return this.data.motion.motion;
+ }
+
+ get motionValid(): boolean {
+ return this.data.motion.motion_valid;
+ }
+
+ public async disable(): Promise {
+ return await this.edit({ enabled: false });
+ }
+
+ public async enable(): Promise {
+ return await this.edit({ enabled: true });
+ }
+
+ public async edit(options: MotionEditOptions): Promise {
+ await this.manager.edit(this.id, options);
+ }
+}
diff --git a/src/structures/NamedResource.ts b/src/structures/NamedResource.ts
index c3f9a79..9250dc1 100644
--- a/src/structures/NamedResource.ts
+++ b/src/structures/NamedResource.ts
@@ -1,11 +1,18 @@
+import { ResourceType, ResourceTypeGet } from '../api/ResourceType';
+import { Hue } from '../hue/Hue';
+import { ArcheType } from '../api/ArcheType';
import { Resource } from './Resource';
-import { ApiResourceType, ApiResourceTypeGet } from '../api/ApiResourceType';
-import { Bridge } from '../bridge/Bridge';
-export abstract class NamedResource extends Resource {
- public data: ApiResourceTypeGet & { metadata: { name: string } };
+export interface NamedResourceEditOptions {
+ name?: string;
+}
+
+export type NamedResourceCreateOptions = Required;
- constructor(bridge: Bridge, data: ApiResourceTypeGet & { metadata: { name: string } }) {
+export abstract class NamedResource extends Resource {
+ public data: ResourceTypeGet & { metadata: { name: string; archetype: ArcheType } };
+
+ constructor(bridge: Hue, data: ResourceTypeGet & { metadata: { name: string; archetype: ArcheType } }) {
super(bridge, data);
this.data = data;
}
@@ -14,9 +21,17 @@ export abstract class NamedResource extends Resource<
return this.data.metadata.name;
}
+ get archeType(): ArcheType {
+ return this.data.metadata.archetype;
+ }
+
public async setName(name: string): Promise {
await this.edit({ name });
}
- public abstract edit(options: { name?: string }): Promise;
+ public async setArcheType(archeType: ArcheType): Promise {
+ await this.edit({ archeType });
+ }
+
+ public abstract edit(options: { name?: string; archeType?: ArcheType }): Promise;
}
diff --git a/src/structures/Resource.ts b/src/structures/Resource.ts
index a359429..d7b6906 100644
--- a/src/structures/Resource.ts
+++ b/src/structures/Resource.ts
@@ -1,5 +1,5 @@
-import { Bridge } from '../bridge/Bridge';
-import { ApiResourceType, ApiResourceTypeGet, ApiResourceTypePut } from '../api/ApiResourceType';
+import { Hue } from '../hue/Hue';
+import { ResourceType, ResourceTypeGet } from '../api/ResourceType';
import { Light } from './Light';
import { clone } from '../util/clone';
import { merge } from '../util/merge';
@@ -10,44 +10,48 @@ import { Room } from './Room';
import { Zone } from './Zone';
import { Device } from './Device';
import { GroupedLight } from './GroupedLight';
+import { DevicePower } from './DevicePower';
+import { Motion } from './Motion';
+import { Manager } from '../managers/Manager';
+import { ZigbeeConnectivity } from './ZigbeeConnectivity';
+import { ZigbeeDeviceDiscovery } from './ZigbeeDeviceDiscovery';
+import { Bridge } from './Bridge';
export interface Resources {
- [ApiResourceType.Device]: Device;
- [ApiResourceType.BridgeHome]: Resource;
- [ApiResourceType.Room]: Room;
- [ApiResourceType.Zone]: Zone;
- [ApiResourceType.Light]: Light;
- [ApiResourceType.Button]: Resource;
- [ApiResourceType.Temperature]: Resource;
- [ApiResourceType.LightLevel]: Resource;
- [ApiResourceType.Motion]: Resource;
- [ApiResourceType.Entertainment]: Resource;
- [ApiResourceType.GroupedLight]: GroupedLight;
- [ApiResourceType.DevicePower]: Resource;
- [ApiResourceType.ZigbeeBridgeConnectivity]: Resource;
- [ApiResourceType.ZgpConnectivity]: Resource;
- [ApiResourceType.Bridge]: Resource;
- [ApiResourceType.Homekit]: Resource;
- [ApiResourceType.Scene]: Scene;
- [ApiResourceType.EntertainmentConfiguration]: Resource;
- [ApiResourceType.PublicImage]: Resource;
- [ApiResourceType.BehaviourScript]: Resource;
- [ApiResourceType.BehaviourInstance]: Resource;
- [ApiResourceType.Geofence]: Resource;
- [ApiResourceType.GeofenceClient]: Resource;
- [ApiResourceType.Geolocation]: Resource;
+ [ResourceType.Device]: Device;
+ [ResourceType.BridgeHome]: Resource;
+ [ResourceType.Room]: Room;
+ [ResourceType.Zone]: Zone;
+ [ResourceType.Light]: Light;
+ [ResourceType.Button]: Resource;
+ [ResourceType.Temperature]: Resource;
+ [ResourceType.LightLevel]: Resource;
+ [ResourceType.Motion]: Motion;
+ [ResourceType.Entertainment]: Resource;
+ [ResourceType.GroupedLight]: GroupedLight;
+ [ResourceType.DevicePower]: DevicePower;
+ [ResourceType.ZigbeeConnectivity]: ZigbeeConnectivity;
+ [ResourceType.ZgpConnectivity]: Resource;
+ [ResourceType.ZigbeeDeviceDiscovery]: ZigbeeDeviceDiscovery;
+ [ResourceType.Bridge]: Bridge;
+ [ResourceType.Homekit]: Resource;
+ [ResourceType.Scene]: Scene;
+ [ResourceType.EntertainmentConfiguration]: Resource;
+ [ResourceType.PublicImage]: Resource;
+ [ResourceType.BehaviourScript]: Resource;
+ [ResourceType.BehaviourInstance]: Resource;
+ [ResourceType.Geofence]: Resource;
+ [ResourceType.GeofenceClient]: Resource;
+ [ResourceType.Geolocation]: Resource;
}
-/**
-export type NarrowResource = T extends ApiResourceType
- ? Resources[T]
- : Resource | NamedResource;
- */
-export type NarrowResource = Resources[T];
-export abstract class Resource {
- public readonly bridge: Bridge;
- public abstract readonly type: ApiResourceType;
- public data: ApiResourceTypeGet;
+export type NarrowResource = Resources[T];
+
+export abstract class Resource {
+ public readonly hue: Hue;
+ public abstract readonly type: ResourceType;
+ public abstract readonly manager: Manager;
+ public data: ResourceTypeGet;
get id(): string {
return this.data.id;
@@ -57,32 +61,26 @@ export abstract class Resource {
return createResourceIdentifier(this.id, this.type);
}
- constructor(bridge: Bridge, data: ApiResourceTypeGet) {
- this.bridge = bridge;
+ constructor(hue: Hue, data: ResourceTypeGet) {
+ this.hue = hue;
this.data = data;
}
- public isType(type: T): this is NarrowResource {
+ public isType(type: T): this is NarrowResource {
return this.type === type;
}
- public _patch(data: Partial>) {
- this.data = merge>(clone(this.data), data);
+ public _patch(data: Partial>) {
+ this.data = merge>(clone(this.data), data);
}
public _clone(): this {
return clone(this);
}
- public _update(data: Partial>): this {
+ public _update(data: Partial>): this {
const clone = this._clone();
this._patch(data);
return clone;
}
-
- protected async _put(data: ApiResourceTypePut): Promise {
- await this.bridge.rest.put(`/resource/${this.type}/${this.id}`, data);
- }
-
- public abstract edit(options: Record): Promise;
}
diff --git a/src/structures/Room.ts b/src/structures/Room.ts
index 049bc7e..c106933 100644
--- a/src/structures/Room.ts
+++ b/src/structures/Room.ts
@@ -1,39 +1,54 @@
-import { NamedResource } from './NamedResource';
-import { ApiResourceType } from '../api/ApiResourceType';
-import { ResourceIdentifier } from '../api/ResourceIdentifier';
-import { NarrowResource } from './Resource';
-
-export interface RoomEditOptions {
- name?: string;
- children?: ResourceIdentifier[];
+import { ResourceType } from '../api/ResourceType';
+import { ArcheTypeResource, ArcheTypeResourceEditOptions } from './ArcheTypeResource';
+import { RoomManager } from '../managers/RoomManager';
+import { ZoneEditOptions } from './Zone';
+import { SceneCreateOptions } from './Scene';
+
+export interface RoomEditOptions extends ArcheTypeResourceEditOptions {
+ children?: string[];
}
-export class Room extends NamedResource {
- type = ApiResourceType.Room;
+export type RoomCreateOptions = Required;
- get children(): NarrowResource[] {
- return this.bridge.resources.getByIdentifiers(this.childIdentifiers);
+export class Room extends ArcheTypeResource {
+ type = ResourceType.Room;
+
+ get manager(): RoomManager {
+ return this.hue.rooms;
+ }
+
+ get childIds(): string[] {
+ return this.data.children.map((child) => child.rid);
}
- get childIdentifiers(): ResourceIdentifier[] {
- return this.data.children;
+ get serviceIds(): string[] {
+ return this.data.services.map((service) => service.rid);
}
- get services(): NarrowResource[] {
- return this.bridge.resources.getByIdentifiers(this.serviceIdentifiers);
+ public async createScene(options: SceneCreateOptions): Promise {
+ return await this.hue.scenes.create(this.id, options);
}
- get serviceIdentifiers(): ResourceIdentifier[] {
- return this.data.services;
+ public async addChildren(children: Required['children']): Promise {
+ const newChildren = [...this.childIds, ...children];
+
+ await this.setChildren(newChildren);
}
- public async addChildren(...children: ResourceIdentifier[]) {
- const newChildren = [...this.childIdentifiers, ...children];
+ public async removeChildren(children: Required['children']): Promise