@ecobridge.xyz/devicemanager 3.0.2

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 (137) hide show
  1. package/.gitea/workflows/default_nottags.yaml +66 -0
  2. package/.gitea/workflows/default_tags.yaml +124 -0
  3. package/.vscode/launch.json +11 -0
  4. package/.vscode/settings.json +26 -0
  5. package/changelog.md +91 -0
  6. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  7. package/dist_ts/00_commitinfo_data.js +9 -0
  8. package/dist_ts/device/device.classes.device.d.ts +205 -0
  9. package/dist_ts/device/device.classes.device.js +344 -0
  10. package/dist_ts/devicemanager.classes.devicemanager.d.ts +210 -0
  11. package/dist_ts/devicemanager.classes.devicemanager.js +835 -0
  12. package/dist_ts/discovery/discovery.classes.homeassistant.d.ts +101 -0
  13. package/dist_ts/discovery/discovery.classes.homeassistant.js +313 -0
  14. package/dist_ts/discovery/discovery.classes.mdns.d.ts +90 -0
  15. package/dist_ts/discovery/discovery.classes.mdns.js +291 -0
  16. package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +64 -0
  17. package/dist_ts/discovery/discovery.classes.networkscanner.js +535 -0
  18. package/dist_ts/discovery/discovery.classes.ssdp.d.ts +112 -0
  19. package/dist_ts/discovery/discovery.classes.ssdp.js +255 -0
  20. package/dist_ts/factories/index.d.ts +190 -0
  21. package/dist_ts/factories/index.js +350 -0
  22. package/dist_ts/features/feature.abstract.d.ts +118 -0
  23. package/dist_ts/features/feature.abstract.js +172 -0
  24. package/dist_ts/features/feature.camera.d.ts +95 -0
  25. package/dist_ts/features/feature.camera.js +162 -0
  26. package/dist_ts/features/feature.climate.d.ts +174 -0
  27. package/dist_ts/features/feature.climate.js +328 -0
  28. package/dist_ts/features/feature.cover.d.ts +121 -0
  29. package/dist_ts/features/feature.cover.js +217 -0
  30. package/dist_ts/features/feature.fan.d.ts +127 -0
  31. package/dist_ts/features/feature.fan.js +235 -0
  32. package/dist_ts/features/feature.light.d.ts +156 -0
  33. package/dist_ts/features/feature.light.js +296 -0
  34. package/dist_ts/features/feature.lock.d.ts +103 -0
  35. package/dist_ts/features/feature.lock.js +170 -0
  36. package/dist_ts/features/feature.playback.d.ts +98 -0
  37. package/dist_ts/features/feature.playback.js +191 -0
  38. package/dist_ts/features/feature.power.d.ts +79 -0
  39. package/dist_ts/features/feature.power.js +173 -0
  40. package/dist_ts/features/feature.print.d.ts +78 -0
  41. package/dist_ts/features/feature.print.js +226 -0
  42. package/dist_ts/features/feature.scan.d.ts +79 -0
  43. package/dist_ts/features/feature.scan.js +315 -0
  44. package/dist_ts/features/feature.sensor.d.ts +95 -0
  45. package/dist_ts/features/feature.sensor.js +147 -0
  46. package/dist_ts/features/feature.snmp.d.ts +88 -0
  47. package/dist_ts/features/feature.snmp.js +175 -0
  48. package/dist_ts/features/feature.switch.d.ts +75 -0
  49. package/dist_ts/features/feature.switch.js +126 -0
  50. package/dist_ts/features/feature.volume.d.ts +104 -0
  51. package/dist_ts/features/feature.volume.js +186 -0
  52. package/dist_ts/features/index.d.ts +19 -0
  53. package/dist_ts/features/index.js +23 -0
  54. package/dist_ts/helpers/helpers.iprange.d.ts +35 -0
  55. package/dist_ts/helpers/helpers.iprange.js +151 -0
  56. package/dist_ts/helpers/helpers.retry.d.ts +25 -0
  57. package/dist_ts/helpers/helpers.retry.js +69 -0
  58. package/dist_ts/index.d.ts +15 -0
  59. package/dist_ts/index.js +40 -0
  60. package/dist_ts/interfaces/feature.interfaces.d.ts +255 -0
  61. package/dist_ts/interfaces/feature.interfaces.js +6 -0
  62. package/dist_ts/interfaces/homeassistant.interfaces.d.ts +550 -0
  63. package/dist_ts/interfaces/homeassistant.interfaces.js +111 -0
  64. package/dist_ts/interfaces/index.d.ts +330 -0
  65. package/dist_ts/interfaces/index.js +16 -0
  66. package/dist_ts/interfaces/smarthome.interfaces.d.ts +282 -0
  67. package/dist_ts/interfaces/smarthome.interfaces.js +7 -0
  68. package/dist_ts/paths.d.ts +1 -0
  69. package/dist_ts/paths.js +3 -0
  70. package/dist_ts/plugins.d.ts +23 -0
  71. package/dist_ts/plugins.js +30 -0
  72. package/dist_ts/protocols/index.d.ts +12 -0
  73. package/dist_ts/protocols/index.js +21 -0
  74. package/dist_ts/protocols/protocol.escl.d.ts +58 -0
  75. package/dist_ts/protocols/protocol.escl.js +364 -0
  76. package/dist_ts/protocols/protocol.homeassistant.d.ts +248 -0
  77. package/dist_ts/protocols/protocol.homeassistant.js +611 -0
  78. package/dist_ts/protocols/protocol.ipp.d.ts +45 -0
  79. package/dist_ts/protocols/protocol.ipp.js +284 -0
  80. package/dist_ts/protocols/protocol.nut.d.ts +160 -0
  81. package/dist_ts/protocols/protocol.nut.js +364 -0
  82. package/dist_ts/protocols/protocol.sane.d.ts +104 -0
  83. package/dist_ts/protocols/protocol.sane.js +597 -0
  84. package/dist_ts/protocols/protocol.snmp.d.ts +133 -0
  85. package/dist_ts/protocols/protocol.snmp.js +333 -0
  86. package/dist_ts/protocols/protocol.upnp.d.ts +207 -0
  87. package/dist_ts/protocols/protocol.upnp.js +442 -0
  88. package/dist_ts/protocols/protocol.upssnmp.d.ts +160 -0
  89. package/dist_ts/protocols/protocol.upssnmp.js +263 -0
  90. package/npmextra.json +24 -0
  91. package/package.json +39 -0
  92. package/readme.hints.md +115 -0
  93. package/readme.md +624 -0
  94. package/test/test.ts +261 -0
  95. package/ts/00_commitinfo_data.ts +8 -0
  96. package/ts/device/device.classes.device.ts +454 -0
  97. package/ts/devicemanager.classes.devicemanager.ts +1219 -0
  98. package/ts/discovery/discovery.classes.homeassistant.ts +376 -0
  99. package/ts/discovery/discovery.classes.mdns.ts +347 -0
  100. package/ts/discovery/discovery.classes.networkscanner.ts +648 -0
  101. package/ts/discovery/discovery.classes.ssdp.ts +357 -0
  102. package/ts/factories/index.ts +696 -0
  103. package/ts/features/feature.abstract.ts +251 -0
  104. package/ts/features/feature.camera.ts +214 -0
  105. package/ts/features/feature.climate.ts +407 -0
  106. package/ts/features/feature.cover.ts +278 -0
  107. package/ts/features/feature.fan.ts +296 -0
  108. package/ts/features/feature.light.ts +369 -0
  109. package/ts/features/feature.lock.ts +223 -0
  110. package/ts/features/feature.playback.ts +246 -0
  111. package/ts/features/feature.power.ts +231 -0
  112. package/ts/features/feature.print.ts +297 -0
  113. package/ts/features/feature.scan.ts +370 -0
  114. package/ts/features/feature.sensor.ts +202 -0
  115. package/ts/features/feature.snmp.ts +235 -0
  116. package/ts/features/feature.switch.ts +170 -0
  117. package/ts/features/feature.volume.ts +256 -0
  118. package/ts/features/index.ts +25 -0
  119. package/ts/helpers/helpers.iprange.ts +172 -0
  120. package/ts/helpers/helpers.retry.ts +100 -0
  121. package/ts/index.ts +165 -0
  122. package/ts/interfaces/feature.interfaces.ts +359 -0
  123. package/ts/interfaces/homeassistant.interfaces.ts +666 -0
  124. package/ts/interfaces/index.ts +421 -0
  125. package/ts/interfaces/smarthome.interfaces.ts +421 -0
  126. package/ts/paths.ts +5 -0
  127. package/ts/plugins.ts +41 -0
  128. package/ts/protocols/index.ts +59 -0
  129. package/ts/protocols/protocol.escl.ts +439 -0
  130. package/ts/protocols/protocol.homeassistant.ts +737 -0
  131. package/ts/protocols/protocol.ipp.ts +329 -0
  132. package/ts/protocols/protocol.nut.ts +471 -0
  133. package/ts/protocols/protocol.sane.ts +694 -0
  134. package/ts/protocols/protocol.snmp.ts +439 -0
  135. package/ts/protocols/protocol.upnp.ts +627 -0
  136. package/ts/protocols/protocol.upssnmp.ts +377 -0
  137. package/tsconfig.json +12 -0
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Switch Feature
3
+ * Provides binary on/off control for smart switches, outlets, etc.
4
+ */
5
+ import { Feature } from './feature.abstract.js';
6
+ /**
7
+ * Switch Feature - binary on/off control
8
+ *
9
+ * Protocol-agnostic: works with Home Assistant, MQTT, Tasmota, Tuya, etc.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const sw = device.getFeature<SwitchFeature>('switch');
14
+ * if (sw) {
15
+ * await sw.turnOn();
16
+ * await sw.toggle();
17
+ * console.log(`Switch is ${sw.isOn ? 'on' : 'off'}`);
18
+ * }
19
+ * ```
20
+ */
21
+ export class SwitchFeature extends Feature {
22
+ type = 'switch';
23
+ protocol;
24
+ /** Entity ID (e.g., "switch.living_room") */
25
+ entityId;
26
+ /** Capabilities */
27
+ capabilities;
28
+ /** Current state */
29
+ _isOn = false;
30
+ /** Protocol client for controlling the switch */
31
+ protocolClient;
32
+ constructor(device, port, options) {
33
+ super(device, port, options);
34
+ this.protocol = options.protocol;
35
+ this.entityId = options.entityId;
36
+ this.protocolClient = options.protocolClient;
37
+ this.capabilities = {
38
+ deviceClass: options.deviceClass,
39
+ };
40
+ }
41
+ // ============================================================================
42
+ // Properties
43
+ // ============================================================================
44
+ /**
45
+ * Get current on/off state (cached)
46
+ */
47
+ get isOn() {
48
+ return this._isOn;
49
+ }
50
+ // ============================================================================
51
+ // Connection
52
+ // ============================================================================
53
+ async doConnect() {
54
+ // Fetch initial state
55
+ try {
56
+ const state = await this.protocolClient.getState(this.entityId);
57
+ this._isOn = state.isOn;
58
+ }
59
+ catch {
60
+ // Ignore errors fetching initial state
61
+ }
62
+ }
63
+ async doDisconnect() {
64
+ // Nothing to disconnect
65
+ }
66
+ // ============================================================================
67
+ // Switch Control
68
+ // ============================================================================
69
+ /**
70
+ * Turn on the switch
71
+ */
72
+ async turnOn() {
73
+ await this.protocolClient.turnOn(this.entityId);
74
+ this._isOn = true;
75
+ this.emit('state:changed', { isOn: true });
76
+ }
77
+ /**
78
+ * Turn off the switch
79
+ */
80
+ async turnOff() {
81
+ await this.protocolClient.turnOff(this.entityId);
82
+ this._isOn = false;
83
+ this.emit('state:changed', { isOn: false });
84
+ }
85
+ /**
86
+ * Toggle the switch
87
+ */
88
+ async toggle() {
89
+ await this.protocolClient.toggle(this.entityId);
90
+ this._isOn = !this._isOn;
91
+ this.emit('state:changed', { isOn: this._isOn });
92
+ }
93
+ /**
94
+ * Refresh state from the device
95
+ */
96
+ async refreshState() {
97
+ const state = await this.protocolClient.getState(this.entityId);
98
+ this._isOn = state.isOn;
99
+ return state;
100
+ }
101
+ /**
102
+ * Update state from external source (e.g., state change event)
103
+ */
104
+ updateState(state) {
105
+ const changed = this._isOn !== state.isOn;
106
+ this._isOn = state.isOn;
107
+ if (changed) {
108
+ this.emit('state:changed', state);
109
+ }
110
+ }
111
+ // ============================================================================
112
+ // Feature Info
113
+ // ============================================================================
114
+ getFeatureInfo() {
115
+ return {
116
+ ...this.getBaseFeatureInfo(),
117
+ type: 'switch',
118
+ protocol: this.protocol,
119
+ capabilities: this.capabilities,
120
+ currentState: {
121
+ isOn: this._isOn,
122
+ },
123
+ };
124
+ }
125
+ }
126
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVhdHVyZS5zd2l0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9mZWF0dXJlcy9mZWF0dXJlLnN3aXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUF5QixNQUFNLHVCQUF1QixDQUFDO0FBd0J2RTs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sT0FBTyxhQUFjLFNBQVEsT0FBTztJQUN4QixJQUFJLEdBQUcsUUFBaUIsQ0FBQztJQUN6QixRQUFRLENBQWtCO0lBRTFDLDZDQUE2QztJQUM3QixRQUFRLENBQVM7SUFFakMsbUJBQW1CO0lBQ0gsWUFBWSxDQUFzQjtJQUVsRCxvQkFBb0I7SUFDVixLQUFLLEdBQVksS0FBSyxDQUFDO0lBRWpDLGlEQUFpRDtJQUN6QyxjQUFjLENBQXdCO0lBRTlDLFlBQ0UsTUFBd0IsRUFDeEIsSUFBWSxFQUNaLE9BQThCO1FBRTlCLEtBQUssQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzdCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUNqQyxJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDakMsSUFBSSxDQUFDLGNBQWMsR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDO1FBQzdDLElBQUksQ0FBQyxZQUFZLEdBQUc7WUFDbEIsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXO1NBQ2pDLENBQUM7SUFDSixDQUFDO0lBRUQsK0VBQStFO0lBQy9FLGFBQWE7SUFDYiwrRUFBK0U7SUFFL0U7O09BRUc7SUFDSCxJQUFXLElBQUk7UUFDYixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUM7SUFDcEIsQ0FBQztJQUVELCtFQUErRTtJQUMvRSxhQUFhO0lBQ2IsK0VBQStFO0lBRXJFLEtBQUssQ0FBQyxTQUFTO1FBQ3ZCLHNCQUFzQjtRQUN0QixJQUFJLENB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
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Volume Feature
3
+ * Provides volume control capability
4
+ */
5
+ import { Feature, type TDeviceReference } from './feature.abstract.js';
6
+ import type { IVolumeFeatureInfo, IFeatureOptions } from '../interfaces/feature.interfaces.js';
7
+ /**
8
+ * Options for creating a VolumeFeature
9
+ */
10
+ export interface IVolumeFeatureOptions extends IFeatureOptions {
11
+ /** Volume control protocol (usually same as device protocol) */
12
+ volumeProtocol?: string;
13
+ /** Minimum volume level */
14
+ minVolume?: number;
15
+ /** Maximum volume level */
16
+ maxVolume?: number;
17
+ /** Volume step increment */
18
+ volumeStep?: number;
19
+ /** Whether mute is supported */
20
+ supportsMute?: boolean;
21
+ /** Protocol-specific volume controller */
22
+ volumeController?: IVolumeController;
23
+ }
24
+ /**
25
+ * Interface for protocol-specific volume control
26
+ */
27
+ export interface IVolumeController {
28
+ getVolume(): Promise<number>;
29
+ setVolume(level: number): Promise<void>;
30
+ getMute(): Promise<boolean>;
31
+ setMute(muted: boolean): Promise<void>;
32
+ }
33
+ /**
34
+ * Volume Feature - provides volume control capability
35
+ *
36
+ * Separated from PlaybackFeature because some devices have volume control
37
+ * without playback capability (e.g., amplifiers, HDMI matrix switches).
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const volume = device.getFeature<VolumeFeature>('volume');
42
+ * if (volume) {
43
+ * const current = await volume.getVolume();
44
+ * await volume.setVolume(current + 10);
45
+ * await volume.toggleMute();
46
+ * }
47
+ * ```
48
+ */
49
+ export declare class VolumeFeature extends Feature {
50
+ readonly type: "volume";
51
+ readonly protocol: string;
52
+ readonly minVolume: number;
53
+ readonly maxVolume: number;
54
+ readonly volumeStep: number;
55
+ readonly supportsMute: boolean;
56
+ protected _volume: number;
57
+ protected _muted: boolean;
58
+ protected volumeController: IVolumeController | null;
59
+ constructor(device: TDeviceReference, port: number, options?: IVolumeFeatureOptions);
60
+ /**
61
+ * Get current volume level (cached)
62
+ */
63
+ get volume(): number;
64
+ /**
65
+ * Get mute state (cached)
66
+ */
67
+ get muted(): boolean;
68
+ protected doConnect(): Promise<void>;
69
+ protected doDisconnect(): Promise<void>;
70
+ /**
71
+ * Get current volume level
72
+ */
73
+ getVolume(): Promise<number>;
74
+ /**
75
+ * Set volume level
76
+ * @param level Volume level (clamped to min/max)
77
+ */
78
+ setVolume(level: number): Promise<void>;
79
+ /**
80
+ * Increase volume by step
81
+ */
82
+ volumeUp(step?: number): Promise<number>;
83
+ /**
84
+ * Decrease volume by step
85
+ */
86
+ volumeDown(step?: number): Promise<number>;
87
+ /**
88
+ * Get mute state
89
+ */
90
+ getMute(): Promise<boolean>;
91
+ /**
92
+ * Set mute state
93
+ */
94
+ setMute(muted: boolean): Promise<void>;
95
+ /**
96
+ * Toggle mute state
97
+ */
98
+ toggleMute(): Promise<boolean>;
99
+ getFeatureInfo(): IVolumeFeatureInfo;
100
+ /**
101
+ * Create from discovery metadata
102
+ */
103
+ static fromDiscovery(device: TDeviceReference, port: number, protocol: string, metadata: Record<string, unknown>): VolumeFeature;
104
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Volume Feature
3
+ * Provides volume control capability
4
+ */
5
+ import { Feature } from './feature.abstract.js';
6
+ /**
7
+ * Volume Feature - provides volume control capability
8
+ *
9
+ * Separated from PlaybackFeature because some devices have volume control
10
+ * without playback capability (e.g., amplifiers, HDMI matrix switches).
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const volume = device.getFeature<VolumeFeature>('volume');
15
+ * if (volume) {
16
+ * const current = await volume.getVolume();
17
+ * await volume.setVolume(current + 10);
18
+ * await volume.toggleMute();
19
+ * }
20
+ * ```
21
+ */
22
+ export class VolumeFeature extends Feature {
23
+ type = 'volume';
24
+ protocol;
25
+ // Capabilities
26
+ minVolume;
27
+ maxVolume;
28
+ volumeStep;
29
+ supportsMute;
30
+ // Current state
31
+ _volume = 0;
32
+ _muted = false;
33
+ // Volume controller (protocol-specific)
34
+ volumeController = null;
35
+ constructor(device, port, options) {
36
+ super(device, port, options);
37
+ this.protocol = options?.volumeProtocol ?? 'generic';
38
+ this.minVolume = options?.minVolume ?? 0;
39
+ this.maxVolume = options?.maxVolume ?? 100;
40
+ this.volumeStep = options?.volumeStep ?? 5;
41
+ this.supportsMute = options?.supportsMute ?? true;
42
+ if (options?.volumeController) {
43
+ this.volumeController = options.volumeController;
44
+ }
45
+ }
46
+ // ============================================================================
47
+ // Properties
48
+ // ============================================================================
49
+ /**
50
+ * Get current volume level (cached)
51
+ */
52
+ get volume() {
53
+ return this._volume;
54
+ }
55
+ /**
56
+ * Get mute state (cached)
57
+ */
58
+ get muted() {
59
+ return this._muted;
60
+ }
61
+ // ============================================================================
62
+ // Connection
63
+ // ============================================================================
64
+ async doConnect() {
65
+ // Fetch initial state if we have a controller
66
+ if (this.volumeController) {
67
+ try {
68
+ this._volume = await this.volumeController.getVolume();
69
+ this._muted = await this.volumeController.getMute();
70
+ }
71
+ catch {
72
+ // Ignore errors fetching initial state
73
+ }
74
+ }
75
+ }
76
+ async doDisconnect() {
77
+ // Nothing to disconnect for volume control
78
+ }
79
+ // ============================================================================
80
+ // Volume Control
81
+ // ============================================================================
82
+ /**
83
+ * Get current volume level
84
+ */
85
+ async getVolume() {
86
+ if (this.volumeController) {
87
+ this._volume = await this.volumeController.getVolume();
88
+ }
89
+ return this._volume;
90
+ }
91
+ /**
92
+ * Set volume level
93
+ * @param level Volume level (clamped to min/max)
94
+ */
95
+ async setVolume(level) {
96
+ const clampedLevel = Math.max(this.minVolume, Math.min(this.maxVolume, level));
97
+ if (this.volumeController) {
98
+ await this.volumeController.setVolume(clampedLevel);
99
+ }
100
+ const oldVolume = this._volume;
101
+ this._volume = clampedLevel;
102
+ if (oldVolume !== clampedLevel) {
103
+ this.emit('volume:changed', { oldVolume, newVolume: clampedLevel });
104
+ }
105
+ }
106
+ /**
107
+ * Increase volume by step
108
+ */
109
+ async volumeUp(step) {
110
+ const increment = step ?? this.volumeStep;
111
+ const newVolume = Math.min(this.maxVolume, this._volume + increment);
112
+ await this.setVolume(newVolume);
113
+ return this._volume;
114
+ }
115
+ /**
116
+ * Decrease volume by step
117
+ */
118
+ async volumeDown(step) {
119
+ const decrement = step ?? this.volumeStep;
120
+ const newVolume = Math.max(this.minVolume, this._volume - decrement);
121
+ await this.setVolume(newVolume);
122
+ return this._volume;
123
+ }
124
+ /**
125
+ * Get mute state
126
+ */
127
+ async getMute() {
128
+ if (this.volumeController && this.supportsMute) {
129
+ this._muted = await this.volumeController.getMute();
130
+ }
131
+ return this._muted;
132
+ }
133
+ /**
134
+ * Set mute state
135
+ */
136
+ async setMute(muted) {
137
+ if (!this.supportsMute) {
138
+ throw new Error('Mute not supported');
139
+ }
140
+ if (this.volumeController) {
141
+ await this.volumeController.setMute(muted);
142
+ }
143
+ const oldMuted = this._muted;
144
+ this._muted = muted;
145
+ if (oldMuted !== muted) {
146
+ this.emit('mute:changed', { oldMuted, newMuted: muted });
147
+ }
148
+ }
149
+ /**
150
+ * Toggle mute state
151
+ */
152
+ async toggleMute() {
153
+ const currentMuted = await this.getMute();
154
+ await this.setMute(!currentMuted);
155
+ return this._muted;
156
+ }
157
+ // ============================================================================
158
+ // Serialization
159
+ // ============================================================================
160
+ getFeatureInfo() {
161
+ return {
162
+ ...this.getBaseFeatureInfo(),
163
+ type: 'volume',
164
+ minVolume: this.minVolume,
165
+ maxVolume: this.maxVolume,
166
+ volumeStep: this.volumeStep,
167
+ supportsMute: this.supportsMute,
168
+ };
169
+ }
170
+ // ============================================================================
171
+ // Static Factory
172
+ // ============================================================================
173
+ /**
174
+ * Create from discovery metadata
175
+ */
176
+ static fromDiscovery(device, port, protocol, metadata) {
177
+ return new VolumeFeature(device, port, {
178
+ volumeProtocol: protocol,
179
+ minVolume: metadata.minVolume ?? 0,
180
+ maxVolume: metadata.maxVolume ?? 100,
181
+ volumeStep: metadata.volumeStep ?? 5,
182
+ supportsMute: metadata.supportsMute ?? true,
183
+ });
184
+ }
185
+ }
186
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVhdHVyZS52b2x1bWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9mZWF0dXJlcy9mZWF0dXJlLnZvbHVtZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUF5QixNQUFNLHVCQUF1QixDQUFDO0FBa0N2RTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCxNQUFNLE9BQU8sYUFBYyxTQUFRLE9BQU87SUFDeEIsSUFBSSxHQUFHLFFBQWlCLENBQUM7SUFDekIsUUFBUSxDQUFTO0lBRWpDLGVBQWU7SUFDQyxTQUFTLENBQVM7SUFDbEIsU0FBUyxDQUFTO0lBQ2xCLFVBQVUsQ0FBUztJQUNuQixZQUFZLENBQVU7SUFFdEMsZ0JBQWdCO0lBQ04sT0FBTyxHQUFXLENBQUMsQ0FBQztJQUNwQixNQUFNLEdBQVksS0FBSyxDQUFDO0lBRWxDLHdDQUF3QztJQUM5QixnQkFBZ0IsR0FBNkIsSUFBSSxDQUFDO0lBRTVELFlBQ0UsTUFBd0IsRUFDeEIsSUFBWSxFQUNaLE9BQStCO1FBRS9CLEtBQUssQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzdCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxFQUFFLGNBQWMsSUFBSSxTQUFTLENBQUM7UUFDckQsSUFBSSxDQUFDLFNBQVMsR0FBRyxPQUFPLEVBQUUsU0FBUyxJQUFJLENBQUMsQ0FBQztRQUN6QyxJQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sRUFBRSxTQUFTLElBQUksR0FBRyxDQUFDO1FBQzNDLElBQUksQ0FBQyxVQUFVLEdBQUcsT0FBTyxFQUFFLFVBQVUsSUFBSSxDQUFDLENBQUM7UUFDM0MsSUFBSSxDQUFDLFlBQVksR0FBRyxPQUFPLEVBQUUsWUFBWSxJQUFJLElBQUksQ0FBQztRQUVsRCxJQUFJLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxDQUFDO1lBQzlCLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxP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
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Features Module
3
+ * Exports all feature classes and types
4
+ */
5
+ export { Feature, type TDeviceReference } from './feature.abstract.js';
6
+ export { ScanFeature, type IScanFeatureOptions } from './feature.scan.js';
7
+ export { PrintFeature, type IPrintFeatureOptions } from './feature.print.js';
8
+ export { PlaybackFeature, type IPlaybackFeatureOptions } from './feature.playback.js';
9
+ export { VolumeFeature, type IVolumeFeatureOptions, type IVolumeController } from './feature.volume.js';
10
+ export { PowerFeature, type IPowerFeatureOptions } from './feature.power.js';
11
+ export { SnmpFeature, type ISnmpFeatureOptions } from './feature.snmp.js';
12
+ export { SwitchFeature, type ISwitchFeatureOptions } from './feature.switch.js';
13
+ export { SensorFeature, type ISensorFeatureOptions } from './feature.sensor.js';
14
+ export { LightFeature, type ILightFeatureOptions } from './feature.light.js';
15
+ export { CoverFeature, type ICoverFeatureOptions } from './feature.cover.js';
16
+ export { LockFeature, type ILockFeatureOptions } from './feature.lock.js';
17
+ export { FanFeature, type IFanFeatureOptions } from './feature.fan.js';
18
+ export { ClimateFeature, type IClimateFeatureOptions } from './feature.climate.js';
19
+ export { CameraFeature, type ICameraFeatureOptions } from './feature.camera.js';
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Features Module
3
+ * Exports all feature classes and types
4
+ */
5
+ // Abstract base
6
+ export { Feature } from './feature.abstract.js';
7
+ // Concrete features - Document/Infrastructure
8
+ export { ScanFeature } from './feature.scan.js';
9
+ export { PrintFeature } from './feature.print.js';
10
+ export { PlaybackFeature } from './feature.playback.js';
11
+ export { VolumeFeature } from './feature.volume.js';
12
+ export { PowerFeature } from './feature.power.js';
13
+ export { SnmpFeature } from './feature.snmp.js';
14
+ // Smart Home Features (protocol-agnostic: home-assistant, hue, mqtt, etc.)
15
+ export { SwitchFeature } from './feature.switch.js';
16
+ export { SensorFeature } from './feature.sensor.js';
17
+ export { LightFeature } from './feature.light.js';
18
+ export { CoverFeature } from './feature.cover.js';
19
+ export { LockFeature } from './feature.lock.js';
20
+ export { FanFeature } from './feature.fan.js';
21
+ export { ClimateFeature } from './feature.climate.js';
22
+ export { CameraFeature } from './feature.camera.js';
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9mZWF0dXJlcy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxnQkFBZ0I7QUFDaEIsT0FBTyxFQUFFLE9BQU8sRUFBeUIsTUFBTSx1QkFBdUIsQ0FBQztBQUV2RSw4Q0FBOEM7QUFDOUMsT0FBTyxFQUFFLFdBQVcsRUFBNEIsTUFBTSxtQkFBbUIsQ0FBQztBQUMxRSxPQUFPLEVBQUUsWUFBWSxFQUE2QixNQUFNLG9CQUFvQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxlQUFlLEVBQWdDLE1BQU0sdUJBQXVCLENBQUM7QUFDdEYsT0FBTyxFQUFFLGFBQWEsRUFBc0QsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RyxPQUFPLEVBQUUsWUFBWSxFQUE2QixNQUFNLG9CQUFvQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxXQUFXLEVBQTRCLE1BQU0sbUJBQW1CLENBQUM7QUFFMUUsMkVBQTJFO0FBQzNFLE9BQU8sRUFBRSxhQUFhLEVBQThCLE1BQU0scUJBQXFCLENBQUM7QUFDaEYsT0FBTyxFQUFFLGFBQWEsRUFBOEIsTUFBTSxxQkFBcUIsQ0FBQztBQUNoRixPQUFPLEVBQUUsWUFBWSxFQUE2QixNQUFNLG9CQUFvQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxZQUFZLEVBQTZCLE1BQU0sb0JBQW9CLENBQUM7QUFDN0UsT0FBTyxFQUFFLFdBQVcsRUFBNEIsTUFBTSxtQkFBbUIsQ0FBQztBQUMxRSxPQUFPLEVBQUUsVUFBVSxFQUEyQixNQUFNLGtCQUFrQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxjQUFjLEVBQStCLE1BQU0sc0JBQXNCLENBQUM7QUFDbkYsT0FBTyxFQUFFLGFBQWEsRUFBOEIsTUFBTSxxQkFBcUIsQ0FBQyJ9
@@ -0,0 +1,35 @@
1
+ /**
2
+ * IP Range utility functions for network scanning
3
+ */
4
+ /**
5
+ * Validates an IPv4 address
6
+ */
7
+ export declare function isValidIp(ip: string): boolean;
8
+ /**
9
+ * Converts an IPv4 address to a 32-bit number
10
+ */
11
+ export declare function ipToNumber(ip: string): number;
12
+ /**
13
+ * Converts a 32-bit number to an IPv4 address
14
+ */
15
+ export declare function numberToIp(num: number): string;
16
+ /**
17
+ * Generates an array of IP addresses from a start to end range
18
+ * Excludes network address (.0) and broadcast address (.255) for /24 ranges
19
+ */
20
+ export declare function ipRangeToIps(startIp: string, endIp: string): string[];
21
+ /**
22
+ * Parses CIDR notation and returns an array of usable host IPs
23
+ * Excludes network address and broadcast address
24
+ * Example: "192.168.1.0/24" returns 192.168.1.1 through 192.168.1.254
25
+ */
26
+ export declare function cidrToIps(cidr: string): string[];
27
+ /**
28
+ * Gets the local network interfaces and returns the first non-loopback IPv4 subnet
29
+ * Returns CIDR notation (e.g., "192.168.1.0/24")
30
+ */
31
+ export declare function getLocalSubnet(): string | null;
32
+ /**
33
+ * Counts the number of IPs in a CIDR range (excluding network and broadcast)
34
+ */
35
+ export declare function countIpsInCidr(cidr: string): number;
@@ -0,0 +1,151 @@
1
+ /**
2
+ * IP Range utility functions for network scanning
3
+ */
4
+ import * as os from 'os';
5
+ /**
6
+ * Validates an IPv4 address
7
+ */
8
+ export function isValidIp(ip) {
9
+ const parts = ip.split('.');
10
+ if (parts.length !== 4)
11
+ return false;
12
+ return parts.every((part) => {
13
+ const num = parseInt(part, 10);
14
+ return !isNaN(num) && num >= 0 && num <= 255 && part === num.toString();
15
+ });
16
+ }
17
+ /**
18
+ * Converts an IPv4 address to a 32-bit number
19
+ */
20
+ export function ipToNumber(ip) {
21
+ const parts = ip.split('.').map((p) => parseInt(p, 10));
22
+ return ((parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]) >>> 0;
23
+ }
24
+ /**
25
+ * Converts a 32-bit number to an IPv4 address
26
+ */
27
+ export function numberToIp(num) {
28
+ return [
29
+ (num >>> 24) & 255,
30
+ (num >>> 16) & 255,
31
+ (num >>> 8) & 255,
32
+ num & 255,
33
+ ].join('.');
34
+ }
35
+ /**
36
+ * Generates an array of IP addresses from a start to end range
37
+ * Excludes network address (.0) and broadcast address (.255) for /24 ranges
38
+ */
39
+ export function ipRangeToIps(startIp, endIp) {
40
+ if (!isValidIp(startIp) || !isValidIp(endIp)) {
41
+ throw new Error(`Invalid IP address: ${!isValidIp(startIp) ? startIp : endIp}`);
42
+ }
43
+ const start = ipToNumber(startIp);
44
+ const end = ipToNumber(endIp);
45
+ if (start > end) {
46
+ throw new Error(`Start IP (${startIp}) must be less than or equal to end IP (${endIp})`);
47
+ }
48
+ const ips = [];
49
+ for (let i = start; i <= end; i++) {
50
+ ips.push(numberToIp(i));
51
+ }
52
+ return ips;
53
+ }
54
+ /**
55
+ * Parses CIDR notation and returns an array of usable host IPs
56
+ * Excludes network address and broadcast address
57
+ * Example: "192.168.1.0/24" returns 192.168.1.1 through 192.168.1.254
58
+ */
59
+ export function cidrToIps(cidr) {
60
+ const match = cidr.match(/^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/);
61
+ if (!match) {
62
+ throw new Error(`Invalid CIDR notation: ${cidr}`);
63
+ }
64
+ const [, networkIp, prefixStr] = match;
65
+ const prefix = parseInt(prefixStr, 10);
66
+ if (!isValidIp(networkIp)) {
67
+ throw new Error(`Invalid network address: ${networkIp}`);
68
+ }
69
+ if (prefix < 0 || prefix > 32) {
70
+ throw new Error(`Invalid prefix length: ${prefix}`);
71
+ }
72
+ // For /32, just return the single IP
73
+ if (prefix === 32) {
74
+ return [networkIp];
75
+ }
76
+ // For /31 (point-to-point), return both IPs
77
+ if (prefix === 31) {
78
+ const networkNum = ipToNumber(networkIp);
79
+ const mask = (0xffffffff << (32 - prefix)) >>> 0;
80
+ const network = (networkNum & mask) >>> 0;
81
+ return [numberToIp(network), numberToIp(network + 1)];
82
+ }
83
+ // Calculate network and broadcast addresses
84
+ const networkNum = ipToNumber(networkIp);
85
+ const mask = (0xffffffff << (32 - prefix)) >>> 0;
86
+ const network = (networkNum & mask) >>> 0;
87
+ const broadcast = (network | (~mask >>> 0)) >>> 0;
88
+ // Generate usable host IPs (exclude network and broadcast)
89
+ const ips = [];
90
+ for (let i = network + 1; i < broadcast; i++) {
91
+ ips.push(numberToIp(i));
92
+ }
93
+ return ips;
94
+ }
95
+ /**
96
+ * Gets the local network interfaces and returns the first non-loopback IPv4 subnet
97
+ * Returns CIDR notation (e.g., "192.168.1.0/24")
98
+ */
99
+ export function getLocalSubnet() {
100
+ try {
101
+ const interfaces = os.networkInterfaces();
102
+ for (const name of Object.keys(interfaces)) {
103
+ const iface = interfaces[name];
104
+ if (!iface)
105
+ continue;
106
+ for (const info of iface) {
107
+ // Skip loopback and non-IPv4
108
+ if (info.family !== 'IPv4' || info.internal)
109
+ continue;
110
+ // Calculate the network address from IP and netmask
111
+ const ip = ipToNumber(info.address);
112
+ const mask = ipToNumber(info.netmask);
113
+ const network = (ip & mask) >>> 0;
114
+ // Calculate prefix length from netmask
115
+ const maskBits = info.netmask.split('.').reduce((acc, octet) => {
116
+ const byte = parseInt(octet, 10);
117
+ let bits = 0;
118
+ for (let i = 7; i >= 0; i--) {
119
+ if ((byte >> i) & 1)
120
+ bits++;
121
+ else
122
+ break;
123
+ }
124
+ return acc + bits;
125
+ }, 0);
126
+ return `${numberToIp(network)}/${maskBits}`;
127
+ }
128
+ }
129
+ }
130
+ catch {
131
+ // Failed to get network interfaces
132
+ }
133
+ return null;
134
+ }
135
+ /**
136
+ * Counts the number of IPs in a CIDR range (excluding network and broadcast)
137
+ */
138
+ export function countIpsInCidr(cidr) {
139
+ const match = cidr.match(/^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/);
140
+ if (!match) {
141
+ throw new Error(`Invalid CIDR notation: ${cidr}`);
142
+ }
143
+ const prefix = parseInt(match[2], 10);
144
+ if (prefix === 32)
145
+ return 1;
146
+ if (prefix === 31)
147
+ return 2;
148
+ // 2^(32-prefix) - 2 (minus network and broadcast)
149
+ return Math.pow(2, 32 - prefix) - 2;
150
+ }
151
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5pcHJhbmdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvaGVscGVycy9oZWxwZXJzLmlwcmFuZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztBQUV6Qjs7R0FFRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQUMsRUFBVTtJQUNsQyxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzVCLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFFckMsT0FBTyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUU7UUFDMUIsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMvQixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLEdBQUcsSUFBSSxJQUFJLEtBQUssR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzFFLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLFVBQVUsQ0FBQyxFQUFVO0lBQ25DLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDeEQsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FB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