homebridge-openrgb-multi 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/.gitlab-ci.yml +24 -0
  2. package/README.md +1 -0
  3. package/dist/Device.d.ts +21 -0
  4. package/dist/Device.js +139 -0
  5. package/dist/Device.js.map +1 -0
  6. package/dist/Zone.d.ts +10 -0
  7. package/dist/Zone.js +64 -0
  8. package/dist/Zone.js.map +1 -0
  9. package/dist/accessories/ZoneBrightnessAccessory.d.ts +9 -0
  10. package/dist/accessories/ZoneBrightnessAccessory.js +35 -0
  11. package/dist/accessories/ZoneBrightnessAccessory.js.map +1 -0
  12. package/dist/accessories/ZoneEffectAccessory.d.ts +11 -0
  13. package/dist/accessories/ZoneEffectAccessory.js +54 -0
  14. package/dist/accessories/ZoneEffectAccessory.js.map +1 -0
  15. package/dist/accessories/ZoneSceneTransitionAccessory.d.ts +9 -0
  16. package/dist/accessories/ZoneSceneTransitionAccessory.js +37 -0
  17. package/dist/accessories/ZoneSceneTransitionAccessory.js.map +1 -0
  18. package/dist/accessories/index.d.ts +3 -0
  19. package/dist/accessories/index.js +20 -0
  20. package/dist/accessories/index.js.map +1 -0
  21. package/dist/constants.d.ts +9 -0
  22. package/dist/constants.js +11 -0
  23. package/dist/constants.js.map +1 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +26 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/platformFactory.d.ts +11 -0
  28. package/dist/platformFactory.js +87 -0
  29. package/dist/platformFactory.js.map +1 -0
  30. package/dist/services/PersistenceService.d.ts +13 -0
  31. package/dist/services/PersistenceService.js +83 -0
  32. package/dist/services/PersistenceService.js.map +1 -0
  33. package/dist/services/index.d.ts +1 -0
  34. package/dist/services/index.js +18 -0
  35. package/dist/services/index.js.map +1 -0
  36. package/dist/test.d.ts +1 -0
  37. package/dist/test.js +38 -0
  38. package/dist/test.js.map +1 -0
  39. package/dist/types/Context.d.ts +14 -0
  40. package/dist/types/Context.js +3 -0
  41. package/dist/types/Context.js.map +1 -0
  42. package/dist/types/configuration/ConfigurationVector2.d.ts +4 -0
  43. package/dist/types/configuration/ConfigurationVector2.js +3 -0
  44. package/dist/types/configuration/ConfigurationVector2.js.map +1 -0
  45. package/dist/types/configuration/DeviceConfiguration.d.ts +8 -0
  46. package/dist/types/configuration/DeviceConfiguration.js +3 -0
  47. package/dist/types/configuration/DeviceConfiguration.js.map +1 -0
  48. package/dist/types/configuration/PlatformConfiguration.d.ts +10 -0
  49. package/dist/types/configuration/PlatformConfiguration.js +3 -0
  50. package/dist/types/configuration/PlatformConfiguration.js.map +1 -0
  51. package/dist/types/configuration/SegmentConfiguration.d.ts +7 -0
  52. package/dist/types/configuration/SegmentConfiguration.js +3 -0
  53. package/dist/types/configuration/SegmentConfiguration.js.map +1 -0
  54. package/dist/types/configuration/ZoneConfiguration.d.ts +6 -0
  55. package/dist/types/configuration/ZoneConfiguration.js +3 -0
  56. package/dist/types/configuration/ZoneConfiguration.js.map +1 -0
  57. package/dist/types/configuration/index.d.ts +5 -0
  58. package/dist/types/configuration/index.js +22 -0
  59. package/dist/types/configuration/index.js.map +1 -0
  60. package/dist/types/index.d.ts +3 -0
  61. package/dist/types/index.js +20 -0
  62. package/dist/types/index.js.map +1 -0
  63. package/dist/types/persistence/PlatformPersistence.d.ts +6 -0
  64. package/dist/types/persistence/PlatformPersistence.js +3 -0
  65. package/dist/types/persistence/PlatformPersistence.js.map +1 -0
  66. package/dist/types/persistence/ZonePersistence.d.ts +5 -0
  67. package/dist/types/persistence/ZonePersistence.js +3 -0
  68. package/dist/types/persistence/ZonePersistence.js.map +1 -0
  69. package/dist/types/persistence/index.d.ts +2 -0
  70. package/dist/types/persistence/index.js +19 -0
  71. package/dist/types/persistence/index.js.map +1 -0
  72. package/package.json +53 -0
  73. package/src/Device.ts +175 -0
  74. package/src/Zone.ts +82 -0
  75. package/src/accessories/ZoneBrightnessAccessory.ts +27 -0
  76. package/src/accessories/ZoneEffectAccessory.ts +44 -0
  77. package/src/accessories/ZoneSceneTransitionAccessory.ts +29 -0
  78. package/src/accessories/index.ts +3 -0
  79. package/src/constants.ts +11 -0
  80. package/src/index.ts +16 -0
  81. package/src/platformFactory.ts +116 -0
  82. package/src/services/PersistenceService.ts +74 -0
  83. package/src/services/index.ts +1 -0
  84. package/src/test.ts +44 -0
  85. package/src/types/Context.ts +15 -0
  86. package/src/types/configuration/ConfigurationVector2.ts +4 -0
  87. package/src/types/configuration/DeviceConfiguration.ts +9 -0
  88. package/src/types/configuration/PlatformConfiguration.ts +11 -0
  89. package/src/types/configuration/SegmentConfiguration.ts +8 -0
  90. package/src/types/configuration/ZoneConfiguration.ts +7 -0
  91. package/src/types/configuration/index.ts +5 -0
  92. package/src/types/index.ts +3 -0
  93. package/src/types/persistence/PlatformPersistence.ts +7 -0
  94. package/src/types/persistence/ZonePersistence.ts +5 -0
  95. package/src/types/persistence/index.ts +2 -0
  96. package/tsconfig.json +20 -0
package/src/Device.ts ADDED
@@ -0,0 +1,175 @@
1
+ import ReadWriteLock from 'rwlock';
2
+ import { Vector2 } from 'three';
3
+ import { utils as openRgbUtils } from 'openrgb-sdk';
4
+ import OpenRGBDevice from 'openrgb-sdk/types/device';
5
+ import {
6
+ colorToString,
7
+ ColorHSV,
8
+ ColorRGB,
9
+ DEFAULT_COLOR,
10
+ convertColorSpace,
11
+ ColorSpace,
12
+ } from '@manganese/palette-kit-core';
13
+ import { Controller, ApplyParameters } from '@manganese/effect-kit';
14
+ import { Context, DeviceConfiguration } from './types';
15
+ import { logBoolean } from 'helpers-for-homebridge';
16
+
17
+ export class Device implements Controller {
18
+ private openRgbDeviceId: number = -1;
19
+ private lock = new ReadWriteLock();
20
+ private colors: ColorHSV[];
21
+ private size: number;
22
+
23
+ constructor(
24
+ private readonly configuration: DeviceConfiguration,
25
+ private readonly context: Context
26
+ ) {}
27
+
28
+ public get name() {
29
+ return this.configuration.name;
30
+ }
31
+
32
+ private get position(): Vector2 {
33
+ return new Vector2(
34
+ this.configuration.position.x,
35
+ this.configuration.position.y
36
+ );
37
+ }
38
+
39
+ private get virtual() {
40
+ return this.configuration.virtual ?? false;
41
+ }
42
+
43
+ public async initialize() {
44
+ if (this.virtual) {
45
+ this.context.log.info(
46
+ `Initialized virtual device <${this.configuration.name}>`
47
+ );
48
+ } else {
49
+ const openRgbDevices =
50
+ await (this.context.openRgbClient.getAllControllerData() as unknown as Promise<
51
+ OpenRGBDevice[]
52
+ >);
53
+ const openRgbDevice = openRgbDevices.find(
54
+ (openRgb) =>
55
+ (openRgb as any).serial.replace(/\s+$/g, '') ===
56
+ this.configuration.serial
57
+ );
58
+
59
+ if (!openRgbDevice) {
60
+ this.context.log.warn(
61
+ `Could not resolve device <${this.name}> to OpenRGB controller`
62
+ );
63
+ } else {
64
+ this.openRgbDeviceId = openRgbDevice.deviceId;
65
+ this.size = openRgbDevice.leds.length;
66
+ this.colors = new Array(this.size).fill(DEFAULT_COLOR);
67
+ this.context.log.info(
68
+ `Resolved device <${this.name}> to OpenRGB controller ID [${this.openRgbDeviceId}] with size ${this.size}`
69
+ );
70
+ }
71
+ }
72
+ }
73
+
74
+ private async withWriteLock(callback: () => Promise<void>) {
75
+ if (!this.virtual && this.openRgbDeviceId < 0) {
76
+ this.context.log.warn(
77
+ `Ignoring write lock request because device <${this.name}> was not initialized`
78
+ );
79
+
80
+ return;
81
+ }
82
+
83
+ return this.lock.writeLock(async (release) => {
84
+ try {
85
+ await callback();
86
+ } catch (error) {
87
+ this.context.log.error('Error while holding write lock', error);
88
+ } finally {
89
+ release();
90
+ }
91
+ });
92
+ }
93
+
94
+ public getSize() {
95
+ return this.size;
96
+ }
97
+
98
+ public getIndexPosition(index: number) {
99
+ const { segments } = this.configuration;
100
+
101
+ for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex++) {
102
+ const segment = segments[segmentIndex];
103
+ const segmentLength = segment.toIndex - segment.fromIndex;
104
+
105
+ if (index < segmentLength) {
106
+ const ratio = index / segmentLength;
107
+
108
+ return new Vector2(
109
+ this.position.x +
110
+ segment.fromPosition.x +
111
+ (segment.toPosition.x - segment.fromPosition.x) * ratio,
112
+ this.position.y +
113
+ segment.fromPosition.y +
114
+ (segment.toPosition.y - segment.fromPosition.y) * ratio
115
+ );
116
+ } else {
117
+ index -= segmentLength;
118
+ }
119
+ }
120
+
121
+ return this.position;
122
+ }
123
+
124
+ public async setActive(active: boolean) {
125
+ await this.withWriteLock(async () => {
126
+ if (this.virtual) return;
127
+
128
+ this.context.log.debug(
129
+ `Applied <${this.name}>.active = ${logBoolean(active)}`
130
+ );
131
+ });
132
+ }
133
+
134
+ public async applyRange(
135
+ fromIndex: number,
136
+ toIndex: number,
137
+ colors: ColorHSV[],
138
+ parameters?: ApplyParameters
139
+ ) {
140
+ await this.withWriteLock(async () => {
141
+ if (!this.virtual) {
142
+ this.context.openRgbClient.updateLeds(
143
+ this.openRgbDeviceId,
144
+ new Array(this.size)
145
+ .fill(DEFAULT_COLOR)
146
+ .map((defaultColor, index) => {
147
+ if (index >= fromIndex && index < toIndex) {
148
+ return colors[index - fromIndex];
149
+ } else {
150
+ return this.colors[index];
151
+ }
152
+ })
153
+ .map((colorHsv) => {
154
+ return openRgbUtils.HSVColor(
155
+ colorHsv.hue * 360,
156
+ colorHsv.saturation,
157
+ colorHsv.value
158
+ );
159
+ })
160
+ );
161
+
162
+ for (let index = fromIndex; index < toIndex; index++) {
163
+ this.colors[index] = colors[index - fromIndex];
164
+ }
165
+ }
166
+
167
+ this.context.log.debug(
168
+ `Applied <${this.name}>[${fromIndex}-${toIndex}] = [\n${colors
169
+ .map(colorToString)
170
+ .map((colorString) => ' ' + colorString)
171
+ .join(',\n')}\n]`
172
+ );
173
+ });
174
+ }
175
+ }
package/src/Zone.ts ADDED
@@ -0,0 +1,82 @@
1
+ import { Context, ZoneConfiguration } from './types';
2
+ import { Device } from './Device';
3
+ import {
4
+ logAccessoryName,
5
+ logBoolean,
6
+ logPercent,
7
+ } from 'helpers-for-homebridge';
8
+ import { Effect, Zone as EffectKitZone } from '@manganese/effect-kit';
9
+
10
+ export class Zone extends EffectKitZone {
11
+ constructor(
12
+ protected override configuration: ZoneConfiguration,
13
+ protected override context: Context
14
+ ) {
15
+ const devices = new Set(
16
+ configuration.devices.map((deviceConfiguration) => {
17
+ return new Device(deviceConfiguration, context);
18
+ })
19
+ );
20
+
21
+ super(configuration, devices, context);
22
+
23
+ this.devices = devices;
24
+ }
25
+
26
+ // Initialization
27
+ public async initialize() {
28
+ await super.initialize();
29
+
30
+ if (this.context.persistence) {
31
+ const active = await this.context.persistence.getZoneActive(this.id);
32
+ const brightness = await this.context.persistence.getZoneBrightness(
33
+ this.id
34
+ );
35
+ const effectId = await this.context.persistence.getZoneEffectId(this.id);
36
+
37
+ if (effectId) {
38
+ const effect = this.getEffect<Effect<any>>(effectId);
39
+
40
+ this.context.log.debug(
41
+ `Restoring persisted zone ${logAccessoryName(
42
+ this.name
43
+ )} effect ID = [${effect.name}]`
44
+ );
45
+
46
+ await this.setEffectId(effectId);
47
+ }
48
+
49
+ this.context.log.debug(
50
+ `Restoring persisted zone ${logAccessoryName(
51
+ this.name
52
+ )} active = ${logBoolean(active)}, brightness = ${logPercent(
53
+ brightness
54
+ )}`
55
+ );
56
+
57
+ await this.setBrightness(brightness);
58
+ await this.setActive(active);
59
+ }
60
+
61
+ this.on('updateActive', async (active: boolean) => {
62
+ await this.context.persistence.setZoneActive(this.id, active);
63
+ });
64
+
65
+ this.on('updateBrightness', async (brightness: number) => {
66
+ await this.context.persistence.setZoneBrightness(this.id, brightness);
67
+ });
68
+
69
+ this.on('updateEffect', async (effectId: string | null) => {
70
+ if (effectId) {
71
+ await this.context.persistence.setZoneEffectId(this.id, effectId);
72
+ }
73
+ });
74
+ }
75
+
76
+ // Devices
77
+ private devices: Set<Device>;
78
+
79
+ private get devicesArray() {
80
+ return Array.from(this.devices);
81
+ }
82
+ }
@@ -0,0 +1,27 @@
1
+ import { BrightnessAccessory } from 'helpers-for-homebridge';
2
+ import { Context } from '../types';
3
+ import { Zone } from '../Zone';
4
+
5
+ export class ZoneBrightnessAccessory extends BrightnessAccessory {
6
+ constructor(
7
+ private readonly zone: Zone,
8
+ context: Context
9
+ ) {
10
+ super(
11
+ {
12
+ name: zone.name + ' Brightness',
13
+ serial: zone.id + '_brightness',
14
+ model: 'OpenRGB Multi Zone Brightness',
15
+ },
16
+ context
17
+ );
18
+ }
19
+
20
+ public override async getBrightness(): Promise<number> {
21
+ return this.zone.getBrightness();
22
+ }
23
+
24
+ public override async setBrightness(brightness: number): Promise<void> {
25
+ return this.zone.setBrightness(brightness);
26
+ }
27
+ }
@@ -0,0 +1,44 @@
1
+ import { SelectionAccessory } from 'helpers-for-homebridge';
2
+ import { Context } from '../types';
3
+ import { Zone } from '../Zone';
4
+ import { ZONE_EFFECT_SELECTION_ACCESSORY_MODE } from '../constants';
5
+
6
+ export class ZoneEffectAccessory extends SelectionAccessory {
7
+ constructor(
8
+ private readonly zone: Zone,
9
+ context: Context
10
+ ) {
11
+ super(
12
+ {
13
+ name: zone.name + ' Effect',
14
+ serial: zone.id + '_effect',
15
+ model: 'OpenRGB Multi Zone Effect',
16
+ mode: ZONE_EFFECT_SELECTION_ACCESSORY_MODE,
17
+ options: Array.from(zone.getEffects().values()).map((effect) => {
18
+ return {
19
+ name: effect.name,
20
+ id: effect.id,
21
+ hapIdentifier: effect.hapIdentifier,
22
+ };
23
+ }),
24
+ },
25
+ context
26
+ );
27
+ }
28
+
29
+ protected override async getActiveOptionId() {
30
+ return this.zone.getEffectIdOrDefault();
31
+ }
32
+
33
+ protected override async setActiveOptionId(effectId: string) {
34
+ await this.zone.setActiveEffectId(effectId);
35
+ }
36
+
37
+ protected override async getActive() {
38
+ return this.zone.active;
39
+ }
40
+
41
+ protected override async setActive(active: boolean) {
42
+ await this.zone.setActive(active);
43
+ }
44
+ }
@@ -0,0 +1,29 @@
1
+ import { SwitchAccessory } from 'helpers-for-homebridge';
2
+ import { Context } from '../types';
3
+ import { Zone } from '../Zone';
4
+
5
+ export class ZoneSceneTransitionAccessory extends SwitchAccessory {
6
+ constructor(
7
+ private readonly zone: Zone,
8
+ context: Context
9
+ ) {
10
+ super(
11
+ {
12
+ name: zone.name + ' Scene Transition',
13
+ serial: zone.id + '_scene_transition',
14
+ model: 'OpenRGB Multi Zone Scene Transition',
15
+ },
16
+ context
17
+ );
18
+ }
19
+
20
+ public override async getOn(): Promise<boolean> {
21
+ return this.zone.sceneTransitionActive;
22
+ }
23
+
24
+ public override async setOn(on: boolean): Promise<void> {
25
+ if (!on) return;
26
+
27
+ this.zone.sceneTransition();
28
+ }
29
+ }
@@ -0,0 +1,3 @@
1
+ export * from './ZoneEffectAccessory';
2
+ export * from './ZoneBrightnessAccessory';
3
+ export * from './ZoneSceneTransitionAccessory';
@@ -0,0 +1,11 @@
1
+ import SimpleLogger from 'simple-node-logger';
2
+ import { SelectionAccessoryMode } from 'helpers-for-homebridge';
3
+
4
+ export const DEFAULT_PERSISTENCE_FILENAME = 'openrgb.json';
5
+ export const DEFAULT_OPENRGB_HOST = 'localhost';
6
+ export const DEFAULT_OPENRGB_PORT = 6742;
7
+ export const DEFAULT_LOG_LEVEL: SimpleLogger.STANDARD_LEVELS = 'info';
8
+ export const DEFAULT_DURATION = 1000;
9
+ export const EFFECT_TRANSITION_DURATION = 500;
10
+ export const ZONE_EFFECT_SELECTION_ACCESSORY_MODE: SelectionAccessoryMode =
11
+ 'outlet';
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { createPlatform } from './platformFactory';
2
+
3
+ export * from './types';
4
+ export * from './constants';
5
+ export * from './Zone';
6
+ export * from './Device';
7
+
8
+ export default (homebridge: any) => {
9
+ const Platform = createPlatform(homebridge);
10
+
11
+ homebridge.registerPlatform(
12
+ 'homebridge-openrgb-multi',
13
+ 'OpenRGBMulti',
14
+ Platform
15
+ );
16
+ };
@@ -0,0 +1,116 @@
1
+ import { Logger } from 'simple-node-logger';
2
+ import { Client as OpenRGBClient } from 'openrgb-sdk';
3
+ import { API, Logging } from 'homebridge';
4
+ import { HomebridgeLogAppender } from 'helpers-for-homebridge';
5
+ import { Palette } from '@manganese/palette-kit-core';
6
+ import { PaletteClient } from '@manganese/palette-kit-client';
7
+
8
+ import { Context, PlatformConfiguration } from './types';
9
+ import { PersistenceService } from './services';
10
+ import {
11
+ ZoneBrightnessAccessory,
12
+ ZoneEffectAccessory,
13
+ ZoneSceneTransitionAccessory,
14
+ } from './accessories';
15
+ import { Zone } from './Zone';
16
+ import { DEFAULT_OPENRGB_HOST, DEFAULT_OPENRGB_PORT } from './constants';
17
+
18
+ export const createPlatform = (homebridge: API) => {
19
+ class Platform {
20
+ context: Context;
21
+ zones = new Map<string /* zone ID */, Zone>();
22
+
23
+ constructor(
24
+ homebridgeLog: Logging,
25
+ readonly configuration: PlatformConfiguration
26
+ ) {
27
+ const log = new Logger({
28
+ level: 'debug',
29
+ appenders: [new HomebridgeLogAppender(homebridgeLog)],
30
+ });
31
+ const persistence = new PersistenceService({
32
+ homebridge,
33
+ log,
34
+ });
35
+
36
+ const openRgbClient = new OpenRGBClient(
37
+ 'default',
38
+ configuration.openRgbPort ?? DEFAULT_OPENRGB_PORT,
39
+ configuration.openRgbHost ?? DEFAULT_OPENRGB_HOST
40
+ );
41
+
42
+ const palette = new Palette({}, { log });
43
+ const paletteClient = configuration.paletteClient
44
+ ? new PaletteClient(
45
+ configuration.paletteClient,
46
+ {
47
+ log,
48
+ },
49
+ palette
50
+ )
51
+ : null;
52
+
53
+ this.context = {
54
+ homebridge,
55
+ openRgbClient,
56
+ palette,
57
+ paletteClient,
58
+ log,
59
+ persistence,
60
+ };
61
+
62
+ configuration.zones.forEach((zoneConfiguration) => {
63
+ const zone = new Zone(zoneConfiguration, this.context);
64
+
65
+ this.zones.set(zone.id, zone);
66
+ });
67
+ }
68
+
69
+ public async accessories(callback) {
70
+ await this.context.openRgbClient.connect();
71
+ await this.context.paletteClient.connect();
72
+ await Promise.all(
73
+ Array.from(this.zones.values()).map(async (zone) => {
74
+ return await zone.initialize();
75
+ })
76
+ );
77
+
78
+ const zoneAccessories = Array.from(this.zones.values()).flatMap(
79
+ (zone) => {
80
+ const zoneEffectAccessory = new ZoneEffectAccessory(
81
+ zone,
82
+ this.context
83
+ );
84
+ const zoneBrightnessAccessory = new ZoneBrightnessAccessory(
85
+ zone,
86
+ this.context
87
+ );
88
+ const zoneSceneTransitionAccessory = new ZoneSceneTransitionAccessory(
89
+ zone,
90
+ this.context
91
+ );
92
+
93
+ zone.on('updateBrightness', (brightness) => {
94
+ zoneBrightnessAccessory.updateBrightness(brightness);
95
+ });
96
+
97
+ zone.on('updateSceneTransitionActive', (sceneTransitionActive) => {
98
+ zoneSceneTransitionAccessory.update(sceneTransitionActive);
99
+ });
100
+
101
+ return [
102
+ zoneEffectAccessory,
103
+ zoneBrightnessAccessory,
104
+ zoneSceneTransitionAccessory,
105
+ ];
106
+ }
107
+ );
108
+
109
+ const accessories = [...zoneAccessories];
110
+
111
+ callback(accessories);
112
+ }
113
+ }
114
+
115
+ return Platform;
116
+ };
@@ -0,0 +1,74 @@
1
+ import {
2
+ BasicPersistenceService,
3
+ Context,
4
+ logAccessoryName,
5
+ } from 'helpers-for-homebridge';
6
+ import { PlatformPersistence, ZonePersistence } from '../types';
7
+ import { DEFAULT_PERSISTENCE_FILENAME } from '../constants';
8
+
9
+ export class PersistenceService extends BasicPersistenceService<PlatformPersistence> {
10
+ constructor(context: Context) {
11
+ super(
12
+ {
13
+ fileName: DEFAULT_PERSISTENCE_FILENAME,
14
+ defaultValue: {
15
+ zones: {},
16
+ },
17
+ },
18
+ context
19
+ );
20
+ }
21
+
22
+ async getZonePersistence(zoneId: string): Promise<ZonePersistence | null> {
23
+ const persistence = await this.readPersistence();
24
+
25
+ return persistence.zones?.[zoneId] ?? null;
26
+ }
27
+
28
+ async getZoneActive(zoneId: string): Promise<boolean> {
29
+ const zonePersistence = await this.getZonePersistence(zoneId);
30
+
31
+ return zonePersistence?.active || false;
32
+ }
33
+
34
+ async getZoneBrightness(zoneId: string): Promise<number> {
35
+ const zonePersistence = await this.getZonePersistence(zoneId);
36
+
37
+ return zonePersistence?.brightness ?? 1;
38
+ }
39
+
40
+ async getZoneEffectId(zoneId: string): Promise<string | null /* effect ID*/> {
41
+ const zonePersistence = await this.getZonePersistence(zoneId);
42
+
43
+ return zonePersistence?.effectId ?? null;
44
+ }
45
+
46
+ async mergeZonePersistence(
47
+ zoneId: string,
48
+ zonePersistence: Partial<ZonePersistence>
49
+ ): Promise<void> {
50
+ await this.mergePersistence({
51
+ zones: {
52
+ [zoneId]: zonePersistence,
53
+ },
54
+ });
55
+ }
56
+
57
+ async setZoneActive(zoneId: string, active: boolean): Promise<void> {
58
+ await this.mergeZonePersistence(zoneId, {
59
+ active,
60
+ });
61
+ }
62
+
63
+ async setZoneBrightness(zoneId: string, brightness: number): Promise<void> {
64
+ await this.mergeZonePersistence(zoneId, {
65
+ brightness,
66
+ });
67
+ }
68
+
69
+ async setZoneEffectId(zoneId: string, effectId: string): Promise<void> {
70
+ await this.mergeZonePersistence(zoneId, {
71
+ effectId,
72
+ });
73
+ }
74
+ }
@@ -0,0 +1 @@
1
+ export * from './PersistenceService';
package/src/test.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { Client as OpenRGBClient } from 'openrgb-sdk';
2
+
3
+ (async () => {
4
+ const openRgbClient = new OpenRGBClient(
5
+ 'default',
6
+ 6742,
7
+ 'living-room-node.local.mangane.se'
8
+ );
9
+
10
+ await openRgbClient.connect();
11
+
12
+ const openRgbDevices = await openRgbClient.getAllControllerData();
13
+
14
+ console.log(openRgbDevices);
15
+ const openRgbDevice = (openRgbDevices as any).find(
16
+ (openRgbDevice) =>
17
+ (openRgbDevice as any).serial.replace(/\s+$/g, '') === '282039U09401607'
18
+ );
19
+
20
+ console.log(JSON.stringify((openRgbDevices[0] as any).serial));
21
+ console.log(
22
+ JSON.stringify((openRgbDevices[0] as any).serial.replace(/\s/g, ''))
23
+ );
24
+ console.log(openRgbDevice);
25
+
26
+ // openRgbClient.updateLeds(
27
+ // openRgbDevice.deviceId,
28
+ // openRgbDevice.colors.map((color, index) => {
29
+ // if (index !== 8) return color;
30
+
31
+ // return {
32
+ // red: 255,
33
+ // green: 0,
34
+ // blue: 255,
35
+ // };
36
+ // })
37
+ // );
38
+
39
+ // // const openRgbDevices = await Promise.all(
40
+ // // openRgbClient.getAllControllerData()
41
+ // // );
42
+
43
+ // // console.log(openRgbDevices);
44
+ })();
@@ -0,0 +1,15 @@
1
+ import { API } from 'homebridge';
2
+ import { Logger } from 'simple-node-logger';
3
+ import OpenRGBClient from 'openrgb-sdk/types/client';
4
+ import { PaletteClient } from '@manganese/palette-kit-client';
5
+ import { Palette } from '@manganese/palette-kit-core';
6
+ import { PersistenceService } from '../services';
7
+
8
+ export interface Context {
9
+ homebridge: API;
10
+ openRgbClient: OpenRGBClient;
11
+ palette: Palette;
12
+ paletteClient: PaletteClient | null;
13
+ log: Logger;
14
+ persistence: PersistenceService;
15
+ }
@@ -0,0 +1,4 @@
1
+ export interface ConfigurationVector2 {
2
+ x: number;
3
+ y: number;
4
+ }
@@ -0,0 +1,9 @@
1
+ import { ConfigurationVector2, SegmentConfiguration } from '.';
2
+
3
+ export interface DeviceConfiguration {
4
+ name: string;
5
+ serial: string;
6
+ position: ConfigurationVector2;
7
+ segments: SegmentConfiguration[];
8
+ virtual?: boolean;
9
+ }
@@ -0,0 +1,11 @@
1
+ import { ClientConfiguration } from '@manganese/palette-kit-client';
2
+ import { ZoneConfiguration } from '.';
3
+
4
+ export interface PlatformConfiguration {
5
+ discoveryInterval?: number;
6
+ networkInterfaces?: string[];
7
+ openRgbHost: string;
8
+ openRgbPort: number;
9
+ paletteClient?: ClientConfiguration;
10
+ zones: ZoneConfiguration[];
11
+ }
@@ -0,0 +1,8 @@
1
+ import { ConfigurationVector2 } from '.';
2
+
3
+ export interface SegmentConfiguration {
4
+ fromIndex: number;
5
+ toIndex: number;
6
+ fromPosition: ConfigurationVector2;
7
+ toPosition: ConfigurationVector2;
8
+ }
@@ -0,0 +1,7 @@
1
+ import { ZoneConfiguration as EffectKitZoneConfiguration } from '@manganese/effect-kit';
2
+ import { DeviceConfiguration } from './DeviceConfiguration';
3
+
4
+ export interface ZoneConfiguration extends EffectKitZoneConfiguration {
5
+ trueOff?: boolean;
6
+ devices: DeviceConfiguration[];
7
+ }