homebridge-smartika 1.0.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.
@@ -0,0 +1,162 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Smartika Fan Accessory
5
+ *
6
+ * Exposes Smartika ceiling fan devices to HomeKit with support for:
7
+ * - On/Off control
8
+ * - Rotation Speed
9
+ */
10
+ class SmartikaFanAccessory {
11
+ /**
12
+ * @param {import('../SmartikaPlatform')} platform
13
+ * @param {import('homebridge').PlatformAccessory} accessory
14
+ * @param {Object} device - Device info from hub
15
+ */
16
+ constructor(platform, accessory, device) {
17
+ this.platform = platform;
18
+ this.accessory = accessory;
19
+ this.device = device;
20
+ this.log = platform.log;
21
+
22
+ // HAP references
23
+ this.Service = platform.api.hap.Service;
24
+ this.Characteristic = platform.api.hap.Characteristic;
25
+
26
+ // Current state
27
+ this.state = {
28
+ active: false,
29
+ rotationSpeed: 0,
30
+ };
31
+
32
+ // Configure the fan service
33
+ this.configureService();
34
+ }
35
+
36
+ /**
37
+ * Configure the Fanv2 service
38
+ */
39
+ configureService() {
40
+ // Use Fanv2 for better HomeKit support
41
+ this.service = this.accessory.getService(this.Service.Fanv2) ||
42
+ this.accessory.addService(this.Service.Fanv2, this.device.typeName);
43
+
44
+ // Set the service name
45
+ this.service.setCharacteristic(this.Characteristic.Name, this.device.typeName);
46
+
47
+ // Configure Active characteristic (on/off)
48
+ this.service.getCharacteristic(this.Characteristic.Active)
49
+ .onGet(this.getActive.bind(this))
50
+ .onSet(this.setActive.bind(this));
51
+
52
+ // Configure RotationSpeed characteristic
53
+ this.service.getCharacteristic(this.Characteristic.RotationSpeed)
54
+ .onGet(this.getRotationSpeed.bind(this))
55
+ .onSet(this.setRotationSpeed.bind(this))
56
+ .setProps({
57
+ minValue: 0,
58
+ maxValue: 100,
59
+ minStep: 1,
60
+ });
61
+
62
+ this.log.debug(`Configured fan accessory: ${this.device.typeName} (0x${this.device.shortAddress.toString(16)})`);
63
+ }
64
+
65
+ /**
66
+ * Handle GET Active
67
+ * @returns {number} - 0 (INACTIVE) or 1 (ACTIVE)
68
+ */
69
+ getActive() {
70
+ this.log.debug(`GET Active for ${this.device.typeName}: ${this.state.active}`);
71
+ return this.state.active ? 1 : 0;
72
+ }
73
+
74
+ /**
75
+ * Handle SET Active
76
+ * @param {number} value - 0 (INACTIVE) or 1 (ACTIVE)
77
+ */
78
+ async setActive(value) {
79
+ const active = value === 1;
80
+ this.log.info(`SET Active for ${this.device.typeName}: ${active}`);
81
+
82
+ try {
83
+ await this.platform.hub.setDevicePower(active, [this.device.shortAddress]);
84
+ this.state.active = active;
85
+
86
+ // If turning on and speed is 0, set a default speed
87
+ if (active && this.state.rotationSpeed === 0) {
88
+ this.state.rotationSpeed = 50;
89
+ this.service.updateCharacteristic(this.Characteristic.RotationSpeed, 50);
90
+ }
91
+ } catch (error) {
92
+ this.log.error(`Failed to set power for ${this.device.typeName}:`, error.message);
93
+ throw new this.platform.api.hap.HapStatusError(
94
+ this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
95
+ );
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Handle GET RotationSpeed
101
+ * @returns {number} - Speed percentage (0-100)
102
+ */
103
+ getRotationSpeed() {
104
+ this.log.debug(`GET RotationSpeed for ${this.device.typeName}: ${this.state.rotationSpeed}%`);
105
+ return this.state.rotationSpeed;
106
+ }
107
+
108
+ /**
109
+ * Handle SET RotationSpeed
110
+ * @param {number} value - Speed percentage (0-100)
111
+ */
112
+ async setRotationSpeed(value) {
113
+ this.log.info(`SET RotationSpeed for ${this.device.typeName}: ${value}%`);
114
+
115
+ try {
116
+ // Convert 0-100% to 0-255
117
+ const speed255 = Math.round(value / 100 * 255);
118
+ await this.platform.hub.setFanSpeed(speed255, [this.device.shortAddress]);
119
+ this.state.rotationSpeed = value;
120
+
121
+ // Update active state based on speed
122
+ if (value > 0 && !this.state.active) {
123
+ this.state.active = true;
124
+ this.service.updateCharacteristic(this.Characteristic.Active, 1);
125
+ } else if (value === 0 && this.state.active) {
126
+ this.state.active = false;
127
+ this.service.updateCharacteristic(this.Characteristic.Active, 0);
128
+ }
129
+ } catch (error) {
130
+ this.log.error(`Failed to set fan speed for ${this.device.typeName}:`, error.message);
131
+ throw new this.platform.api.hap.HapStatusError(
132
+ this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
133
+ );
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Update state from hub status
139
+ * @param {Object} status - Status from hub
140
+ */
141
+ updateStatus(status) {
142
+ // Update Active state
143
+ if (status.on !== undefined && status.on !== this.state.active) {
144
+ this.state.active = status.on;
145
+ this.service.updateCharacteristic(this.Characteristic.Active, status.on ? 1 : 0);
146
+ this.log.debug(`Updated ${this.device.typeName} Active: ${status.on}`);
147
+ }
148
+
149
+ // Update RotationSpeed
150
+ if (status.speed !== undefined) {
151
+ // Convert 0-255 to 0-100%
152
+ const speed = Math.round(status.speed / 255 * 100);
153
+ if (speed !== this.state.rotationSpeed) {
154
+ this.state.rotationSpeed = speed;
155
+ this.service.updateCharacteristic(this.Characteristic.RotationSpeed, speed);
156
+ this.log.debug(`Updated ${this.device.typeName} RotationSpeed: ${speed}%`);
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ module.exports = SmartikaFanAccessory;
@@ -0,0 +1,203 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Smartika Light Accessory
5
+ *
6
+ * Exposes Smartika light devices to HomeKit with support for:
7
+ * - On/Off control
8
+ * - Brightness (dimming)
9
+ * - Color Temperature
10
+ */
11
+ class SmartikaLightAccessory {
12
+ /**
13
+ * @param {import('../SmartikaPlatform')} platform
14
+ * @param {import('homebridge').PlatformAccessory} accessory
15
+ * @param {Object} device - Device info from hub
16
+ */
17
+ constructor(platform, accessory, device) {
18
+ this.platform = platform;
19
+ this.accessory = accessory;
20
+ this.device = device;
21
+ this.log = platform.log;
22
+
23
+ // HAP references
24
+ this.Service = platform.api.hap.Service;
25
+ this.Characteristic = platform.api.hap.Characteristic;
26
+
27
+ // Current state
28
+ this.state = {
29
+ on: false,
30
+ brightness: 100,
31
+ colorTemperature: 200, // HomeKit: 140-500 mireds
32
+ };
33
+
34
+ // Configure the lightbulb service
35
+ this.configureService();
36
+ }
37
+
38
+ /**
39
+ * Configure the Lightbulb service
40
+ */
41
+ configureService() {
42
+ // Get or create the Lightbulb service
43
+ this.service = this.accessory.getService(this.Service.Lightbulb) ||
44
+ this.accessory.addService(this.Service.Lightbulb, this.device.typeName);
45
+
46
+ // Set the service name
47
+ this.service.setCharacteristic(this.Characteristic.Name, this.device.typeName);
48
+
49
+ // Configure On characteristic
50
+ this.service.getCharacteristic(this.Characteristic.On)
51
+ .onGet(this.getOn.bind(this))
52
+ .onSet(this.setOn.bind(this));
53
+
54
+ // Configure Brightness characteristic
55
+ this.service.getCharacteristic(this.Characteristic.Brightness)
56
+ .onGet(this.getBrightness.bind(this))
57
+ .onSet(this.setBrightness.bind(this));
58
+
59
+ // Configure Color Temperature characteristic (if device supports it)
60
+ // Most Smartika lights support color temperature
61
+ this.service.getCharacteristic(this.Characteristic.ColorTemperature)
62
+ .onGet(this.getColorTemperature.bind(this))
63
+ .onSet(this.setColorTemperature.bind(this))
64
+ .setProps({
65
+ minValue: 140, // ~7142K (cool white)
66
+ maxValue: 500, // ~2000K (warm white)
67
+ });
68
+
69
+ this.log.debug(`Configured light accessory: ${this.device.typeName} (0x${this.device.shortAddress.toString(16)})`);
70
+ }
71
+
72
+ /**
73
+ * Handle GET On
74
+ * @returns {boolean}
75
+ */
76
+ getOn() {
77
+ this.log.debug(`GET On for ${this.device.typeName}: ${this.state.on}`);
78
+ return this.state.on;
79
+ }
80
+
81
+ /**
82
+ * Handle SET On
83
+ * @param {boolean} value
84
+ */
85
+ async setOn(value) {
86
+ this.log.info(`SET On for ${this.device.typeName}: ${value}`);
87
+
88
+ try {
89
+ await this.platform.hub.setDevicePower(value, [this.device.shortAddress]);
90
+ this.state.on = value;
91
+ } catch (error) {
92
+ this.log.error(`Failed to set power for ${this.device.typeName}:`, error.message);
93
+ throw new this.platform.api.hap.HapStatusError(
94
+ this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
95
+ );
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Handle GET Brightness
101
+ * @returns {number} - Brightness percentage (0-100)
102
+ */
103
+ getBrightness() {
104
+ this.log.debug(`GET Brightness for ${this.device.typeName}: ${this.state.brightness}%`);
105
+ return this.state.brightness;
106
+ }
107
+
108
+ /**
109
+ * Handle SET Brightness
110
+ * @param {number} value - Brightness percentage (0-100)
111
+ */
112
+ async setBrightness(value) {
113
+ this.log.info(`SET Brightness for ${this.device.typeName}: ${value}%`);
114
+
115
+ try {
116
+ // Convert 0-100% to 0-255
117
+ const brightness255 = Math.round(value / 100 * 255);
118
+ await this.platform.hub.setLightBrightness(brightness255, [this.device.shortAddress]);
119
+ this.state.brightness = value;
120
+
121
+ // If brightness is set to > 0, ensure light is on
122
+ if (value > 0 && !this.state.on) {
123
+ this.state.on = true;
124
+ this.service.updateCharacteristic(this.Characteristic.On, true);
125
+ }
126
+ } catch (error) {
127
+ this.log.error(`Failed to set brightness for ${this.device.typeName}:`, error.message);
128
+ throw new this.platform.api.hap.HapStatusError(
129
+ this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
130
+ );
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Handle GET ColorTemperature
136
+ * @returns {number} - Color temperature in mireds (140-500)
137
+ */
138
+ getColorTemperature() {
139
+ this.log.debug(`GET ColorTemperature for ${this.device.typeName}: ${this.state.colorTemperature} mireds`);
140
+ return this.state.colorTemperature;
141
+ }
142
+
143
+ /**
144
+ * Handle SET ColorTemperature
145
+ * @param {number} value - Color temperature in mireds (140-500)
146
+ */
147
+ async setColorTemperature(value) {
148
+ this.log.info(`SET ColorTemperature for ${this.device.typeName}: ${value} mireds`);
149
+
150
+ try {
151
+ // Convert mireds to Smartika temperature (0=warm, 255=cool)
152
+ // HomeKit: 140 (cool/7142K) to 500 (warm/2000K)
153
+ // Smartika: 0 (warm) to 255 (cool)
154
+ // So we need to invert: higher mireds = warmer = lower Smartika value
155
+ const temp255 = Math.round((500 - value) / (500 - 140) * 255);
156
+ await this.platform.hub.setLightTemperature(temp255, [this.device.shortAddress]);
157
+ this.state.colorTemperature = value;
158
+ } catch (error) {
159
+ this.log.error(`Failed to set color temperature for ${this.device.typeName}:`, error.message);
160
+ throw new this.platform.api.hap.HapStatusError(
161
+ this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
162
+ );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Update state from hub status
168
+ * @param {Object} status - Status from hub
169
+ */
170
+ updateStatus(status) {
171
+ // Update On state
172
+ if (status.on !== undefined && status.on !== this.state.on) {
173
+ this.state.on = status.on;
174
+ this.service.updateCharacteristic(this.Characteristic.On, status.on);
175
+ this.log.debug(`Updated ${this.device.typeName} On: ${status.on}`);
176
+ }
177
+
178
+ // Update Brightness
179
+ if (status.brightness !== undefined) {
180
+ // Convert 0-255 to 0-100%
181
+ const brightness = Math.round(status.brightness / 255 * 100);
182
+ if (brightness !== this.state.brightness) {
183
+ this.state.brightness = brightness;
184
+ this.service.updateCharacteristic(this.Characteristic.Brightness, brightness);
185
+ this.log.debug(`Updated ${this.device.typeName} Brightness: ${brightness}%`);
186
+ }
187
+ }
188
+
189
+ // Update Color Temperature
190
+ if (status.temperature !== undefined) {
191
+ // Convert Smartika temperature (0=warm, 255=cool) to mireds (140-500)
192
+ // Invert: lower Smartika = warmer = higher mireds
193
+ const mireds = Math.round(500 - (status.temperature / 255 * (500 - 140)));
194
+ if (mireds !== this.state.colorTemperature) {
195
+ this.state.colorTemperature = mireds;
196
+ this.service.updateCharacteristic(this.Characteristic.ColorTemperature, mireds);
197
+ this.log.debug(`Updated ${this.device.typeName} ColorTemperature: ${mireds} mireds`);
198
+ }
199
+ }
200
+ }
201
+ }
202
+
203
+ module.exports = SmartikaLightAccessory;
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Smartika Plug Accessory
5
+ *
6
+ * Exposes Smartika smart plug devices to HomeKit with support for:
7
+ * - On/Off control
8
+ */
9
+ class SmartikaPlugAccessory {
10
+ /**
11
+ * @param {import('../SmartikaPlatform')} platform
12
+ * @param {import('homebridge').PlatformAccessory} accessory
13
+ * @param {Object} device - Device info from hub
14
+ */
15
+ constructor(platform, accessory, device) {
16
+ this.platform = platform;
17
+ this.accessory = accessory;
18
+ this.device = device;
19
+ this.log = platform.log;
20
+
21
+ // HAP references
22
+ this.Service = platform.api.hap.Service;
23
+ this.Characteristic = platform.api.hap.Characteristic;
24
+
25
+ // Current state
26
+ this.state = {
27
+ on: false,
28
+ };
29
+
30
+ // Configure the outlet service
31
+ this.configureService();
32
+ }
33
+
34
+ /**
35
+ * Configure the Outlet service
36
+ */
37
+ configureService() {
38
+ // Get or create the Outlet service
39
+ this.service = this.accessory.getService(this.Service.Outlet) ||
40
+ this.accessory.addService(this.Service.Outlet, this.device.typeName);
41
+
42
+ // Set the service name
43
+ this.service.setCharacteristic(this.Characteristic.Name, this.device.typeName);
44
+
45
+ // Configure On characteristic
46
+ this.service.getCharacteristic(this.Characteristic.On)
47
+ .onGet(this.getOn.bind(this))
48
+ .onSet(this.setOn.bind(this));
49
+
50
+ // Configure OutletInUse characteristic (we'll assume it's in use if it's on)
51
+ this.service.getCharacteristic(this.Characteristic.OutletInUse)
52
+ .onGet(this.getOutletInUse.bind(this));
53
+
54
+ this.log.debug(`Configured plug accessory: ${this.device.typeName} (0x${this.device.shortAddress.toString(16)})`);
55
+ }
56
+
57
+ /**
58
+ * Handle GET On
59
+ * @returns {boolean}
60
+ */
61
+ getOn() {
62
+ this.log.debug(`GET On for ${this.device.typeName}: ${this.state.on}`);
63
+ return this.state.on;
64
+ }
65
+
66
+ /**
67
+ * Handle SET On
68
+ * @param {boolean} value
69
+ */
70
+ async setOn(value) {
71
+ this.log.info(`SET On for ${this.device.typeName}: ${value}`);
72
+
73
+ try {
74
+ await this.platform.hub.setDevicePower(value, [this.device.shortAddress]);
75
+ this.state.on = value;
76
+
77
+ // Update OutletInUse when power state changes
78
+ this.service.updateCharacteristic(this.Characteristic.OutletInUse, value);
79
+ } catch (error) {
80
+ this.log.error(`Failed to set power for ${this.device.typeName}:`, error.message);
81
+ throw new this.platform.api.hap.HapStatusError(
82
+ this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
83
+ );
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Handle GET OutletInUse
89
+ * @returns {boolean}
90
+ */
91
+ getOutletInUse() {
92
+ // We'll assume the outlet is in use if it's on
93
+ // (Smartika plugs don't report power consumption)
94
+ return this.state.on;
95
+ }
96
+
97
+ /**
98
+ * Update state from hub status
99
+ * @param {Object} status - Status from hub
100
+ */
101
+ updateStatus(status) {
102
+ // Update On state
103
+ if (status.on !== undefined && status.on !== this.state.on) {
104
+ this.state.on = status.on;
105
+ this.service.updateCharacteristic(this.Characteristic.On, status.on);
106
+ this.service.updateCharacteristic(this.Characteristic.OutletInUse, status.on);
107
+ this.log.debug(`Updated ${this.device.typeName} On: ${status.on}`);
108
+ }
109
+ }
110
+ }
111
+
112
+ module.exports = SmartikaPlugAccessory;
package/src/index.js ADDED
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const { PLATFORM_NAME, PLUGIN_NAME } = require('./settings');
4
+ const SmartikaPlatform = require('./SmartikaPlatform');
5
+
6
+ /**
7
+ * Homebridge plugin entry point
8
+ * @param {API} api - Homebridge API
9
+ */
10
+ module.exports = (api) => {
11
+ api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, SmartikaPlatform);
12
+ };
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Platform name - must match the "pluginAlias" in config.schema.json
5
+ */
6
+ const PLATFORM_NAME = 'Smartika';
7
+
8
+ /**
9
+ * Plugin name - must match the "name" in package.json
10
+ */
11
+ const PLUGIN_NAME = 'homebridge-smartika';
12
+
13
+ module.exports = {
14
+ PLATFORM_NAME,
15
+ PLUGIN_NAME,
16
+ };