homebridge-volcano-hybrid 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Rosh Beed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # homebridge-volcano-hybrid
2
+
3
+ [![npm](https://img.shields.io/npm/v/homebridge-volcano-hybrid)](https://www.npmjs.com/package/homebridge-volcano-hybrid)
4
+ [![Build and Lint](https://github.com/RoshBeed/homebridge-volcano-hybrid/actions/workflows/build.yml/badge.svg)](https://github.com/RoshBeed/homebridge-volcano-hybrid/actions/workflows/build.yml)
5
+
6
+ Homebridge plugin for the [Storz & Bickel Volcano Hybrid](https://www.storz-bickel.com/en-us/volcano-hybrid) desktop vaporizer. Brings the device into Apple HomeKit so you can set temperature, turn the heater on/off, and control the air pump from the Home app, Siri, or automations.
7
+
8
+ Exposes three controls in HomeKit:
9
+
10
+ - **Thermostat** — live temperature display on the arc; set target temperature (40–230°C / 104–446°F)
11
+ - **Heat switch** — turn the heating element on/off
12
+ - **Pump switch** — turn the air pump on/off
13
+
14
+ > **macOS only.** The plugin connects directly to the device via CoreBluetooth and must run on the Mac that has Bluetooth access to the vaporizer.
15
+
16
+ ---
17
+
18
+ ## Requirements
19
+
20
+ - macOS
21
+ - Node.js `^22.10.0 || ^24.0.0`
22
+ - Homebridge `^1.8.0 || ^2.0.0`
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ Install via **Homebridge Config UI X**: search for **Volcano Hybrid** and click Install.
29
+
30
+ Or manually:
31
+ ```bash
32
+ npm install -g homebridge-volcano-hybrid
33
+ sudo hb-service restart
34
+ ```
35
+
36
+ ### Bluetooth permission (one-time setup)
37
+
38
+ macOS requires explicit permission for Homebridge to use Bluetooth. After installing:
39
+
40
+ 1. Open **System Settings → Privacy & Security → Bluetooth**
41
+ 2. Click **+** at the bottom of the list
42
+ 3. Press `Cmd+Shift+G` in the file picker and paste your Node.js binary path
43
+ 4. Click Open, then enable the toggle
44
+ 5. Restart Homebridge: `sudo hb-service restart`
45
+
46
+ To find your Node.js binary path, run `which node` in Terminal. If you use nvm, the path includes the Node.js version number (e.g. `/Users/yourname/.nvm/versions/node/v22.10.0/bin/node`) and **you will need to repeat this step each time you upgrade Node.js through nvm**.
47
+
48
+ ---
49
+
50
+ ## Finding your device details
51
+
52
+ ### BLE advertisement name
53
+
54
+ The Volcano Hybrid advertises over Bluetooth with a name containing `VOLCANO H`. You can confirm the exact name your unit uses with a BLE scanner:
55
+
56
+ - **LightBlue** (free, App Store)
57
+ - **nRF Connect** (free, App Store)
58
+ - **macOS System Information**: hold ⌥ Option → Apple menu → System Information → Bluetooth
59
+
60
+ The default `"VOLCANO H"` works for most units. Only set `bleAdvertisementName` in the config if your device advertises differently.
61
+
62
+ ### MAC address
63
+
64
+ The `address` field is used as a stable unique identifier for Homebridge to cache your accessory across restarts — it does not affect Bluetooth discovery on macOS. To find it:
65
+
66
+ 1. Hold ⌥ Option → Apple menu → **System Information**
67
+ 2. Navigate to **Bluetooth**
68
+ 3. Power on your Volcano Hybrid so it appears in the device list
69
+ 4. Note the address (format: `AA:BB:CC:DD:EE:FF`)
70
+
71
+ ---
72
+
73
+ ## Configuration
74
+
75
+ ```json
76
+ {
77
+ "platforms": [
78
+ {
79
+ "platform": "VolcanoHybrid",
80
+ "name": "Volcano Hybrid",
81
+ "devices": [
82
+ {
83
+ "name": "Volcano",
84
+ "address": "AA:BB:CC:DD:EE:FF",
85
+ "bleAdvertisementName": "VOLCANO H"
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ }
91
+ ```
92
+
93
+ | Field | Required | Default | Description |
94
+ |---|---|---|---|
95
+ | `name` | yes | — | Platform name shown in the Homebridge UI sidebar (not the HomeKit accessory name) |
96
+ | `devices[].name` | yes | — | Accessory name shown in the Home app |
97
+ | `devices[].address` | yes | — | Bluetooth MAC address, used as the HomeKit accessory unique ID |
98
+ | `devices[].bleAdvertisementName` | no | `"VOLCANO H"` | BLE advertisement name substring used to discover the device |
99
+
100
+ **Temperature units:** the device operates in Celsius. The Home app will display temperatures in your region's preferred unit (°C or °F) automatically — no configuration needed.
101
+
102
+ ---
103
+
104
+ ## Troubleshooting
105
+
106
+ **Scan timeout — device not found**
107
+ - Make sure the Volcano Hybrid is powered on and not exclusively connected to the Storz & Bickel app
108
+ - Confirm the advertisement name with a BLE scanner (LightBlue or nRF Connect) and set `bleAdvertisementName` in the config if it differs from `"VOLCANO H"`
109
+ - Check that Bluetooth is enabled on your Mac
110
+
111
+ **Stays in "BLE disconnected" / keeps reconnecting**
112
+ - The device may be connected to another app or device — disconnect it from the Storz & Bickel iOS app first
113
+ - Try toggling Bluetooth off and on in macOS System Settings
114
+
115
+ **`BT state: unauthorized` in Homebridge logs**
116
+ - Follow the [Bluetooth permission setup](#bluetooth-permission-one-time-setup) above
117
+ - If you upgraded Node.js via nvm since granting permission, you need to add the new binary path to the Bluetooth list again
118
+
119
+ **Controls show an error in the Home app**
120
+ - This happens when HomeKit tries to control the device before the BLE connection is established
121
+ - Wait a few seconds for the `BLE ready` log message, then try again
122
+
123
+ ---
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ npm install # install dependencies
129
+ npm run build # compile TypeScript
130
+ npm run watch # watch mode
131
+ npm run lint # run ESLint
132
+ npm link # link into local Homebridge for testing
133
+ sudo hb-service restart
134
+ hb-service logs
135
+ ```
136
+
137
+ ---
138
+
139
+ ## License
140
+
141
+ MIT © Rosh Beed
@@ -0,0 +1,63 @@
1
+ {
2
+ "pluginAlias": "VolcanoHybrid",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "strictValidation": false,
6
+ "schema": {
7
+ "type": "object",
8
+ "required": ["name", "devices"],
9
+ "properties": {
10
+ "name": {
11
+ "title": "Platform Name",
12
+ "type": "string",
13
+ "default": "Volcano Hybrid",
14
+ "description": "Name shown in the Homebridge UI sidebar. This does not affect the accessory name in the Home app — configure that under Devices.",
15
+ "required": true
16
+ },
17
+ "devices": {
18
+ "title": "Devices",
19
+ "type": "array",
20
+ "minItems": 1,
21
+ "items": {
22
+ "title": "Device",
23
+ "type": "object",
24
+ "required": ["name", "address"],
25
+ "properties": {
26
+ "name": {
27
+ "title": "Name",
28
+ "type": "string",
29
+ "default": "Volcano",
30
+ "required": true
31
+ },
32
+ "address": {
33
+ "title": "BLE Address",
34
+ "type": "string",
35
+ "placeholder": "AA:BB:CC:DD:EE:FF",
36
+ "description": "Bluetooth MAC address — find it in System Information → Bluetooth.",
37
+ "required": true
38
+ },
39
+ "bleAdvertisementName": {
40
+ "title": "BLE Advertisement Name",
41
+ "type": "string",
42
+ "default": "VOLCANO H",
43
+ "description": "Substring of the name your device advertises over Bluetooth. Use a BLE scanner app to confirm. Defaults to \"VOLCANO H\"."
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ },
50
+ "layout": [
51
+ "name",
52
+ {
53
+ "key": "devices",
54
+ "type": "array",
55
+ "title": "Devices",
56
+ "items": [
57
+ "devices[].name",
58
+ "devices[].address",
59
+ "devices[].bleAdvertisementName"
60
+ ]
61
+ }
62
+ ]
63
+ }
@@ -0,0 +1,30 @@
1
+ import { EventEmitter } from 'events';
2
+ import type { Logging } from 'homebridge';
3
+ import type { VolcanoState } from './types.js';
4
+ export declare class BleManager extends EventEmitter {
5
+ private readonly log;
6
+ private peripheral;
7
+ private chars;
8
+ private state;
9
+ private stopped;
10
+ private tempPollTimer;
11
+ private reconnectDelay;
12
+ private readonly match;
13
+ constructor(identifier: string, log: Logging);
14
+ getState(): VolcanoState;
15
+ start(): void;
16
+ stop(): void;
17
+ setTargetTemp(celsius: number): Promise<void>;
18
+ setHeater(on: boolean): Promise<void>;
19
+ setPump(on: boolean): Promise<void>;
20
+ private _setState;
21
+ private _scheduleReconnect;
22
+ private _connect;
23
+ private _scan;
24
+ private _readStatics;
25
+ private _decodeActivity;
26
+ private _startTempPoll;
27
+ private _stopTempPoll;
28
+ private _read;
29
+ private _write;
30
+ }
@@ -0,0 +1,237 @@
1
+ import { EventEmitter } from 'events';
2
+ import noble from '@abandonware/noble';
3
+ import { UUID_TEMP, UUID_HEATER_SETPOINT, UUID_AUTO_SHUT_OFF, UUID_AUTO_SHUT_OFF_SETTING, UUID_HEAT_ON, UUID_HEAT_OFF, UUID_PUMP_ON, UUID_PUMP_OFF, UUID_HOURS, UUID_MINUTES, UUID_PUMP_NOTIFICATIONS, UUID_FIRMWARE, UUID_BLE_FIRMWARE, UUID_SERIAL, TEMP_POLL_INTERVAL_MS, RECONNECT_DELAY_MS, } from './constants.js';
4
+ const MAX_RECONNECT_DELAY_MS = 30000;
5
+ const DEFAULT_STATE = {
6
+ status: 'DISCONNECTED',
7
+ currentTemp: 0, targetTemp: 0,
8
+ heaterOn: false, pumpOn: false,
9
+ autoShutoff: false, autoShutoffMinutes: 0,
10
+ firmwareVersion: '', bleVersion: '', serialNumber: '',
11
+ hoursOfOperation: 0, minutesOfOperation: 0,
12
+ };
13
+ export class BleManager extends EventEmitter {
14
+ log;
15
+ peripheral = null;
16
+ chars = new Map();
17
+ state = { ...DEFAULT_STATE };
18
+ stopped = false;
19
+ tempPollTimer = null;
20
+ reconnectDelay = RECONNECT_DELAY_MS;
21
+ match;
22
+ constructor(identifier, log) {
23
+ super();
24
+ this.log = log;
25
+ // macOS CoreBluetooth does not expose MAC addresses — noble address is always "".
26
+ // Match by advertised name; identifier is treated as a substring.
27
+ this.match = (p) => (p.address && p.address.toLowerCase() === identifier.toLowerCase()) ||
28
+ (p.advertisement.localName ?? '').includes(identifier);
29
+ }
30
+ getState() {
31
+ return { ...this.state };
32
+ }
33
+ start() {
34
+ this.stopped = false;
35
+ this._connect();
36
+ }
37
+ stop() {
38
+ this.stopped = true;
39
+ this._stopTempPoll();
40
+ this.chars = new Map();
41
+ if (this.peripheral) {
42
+ this.peripheral.disconnect();
43
+ this.peripheral = null;
44
+ }
45
+ // Reset state without broadcasting — Homebridge may be tearing down HAP
46
+ this.state = { ...DEFAULT_STATE };
47
+ }
48
+ async setTargetTemp(celsius) {
49
+ const buf = Buffer.alloc(2);
50
+ buf.writeUInt16LE(Math.round(celsius * 10));
51
+ await this._write(UUID_HEATER_SETPOINT, buf);
52
+ this._setState({ targetTemp: celsius });
53
+ }
54
+ async setHeater(on) {
55
+ await this._write(on ? UUID_HEAT_ON : UUID_HEAT_OFF, Buffer.alloc(0));
56
+ this._setState({ heaterOn: on });
57
+ }
58
+ async setPump(on) {
59
+ await this._write(on ? UUID_PUMP_ON : UUID_PUMP_OFF, Buffer.alloc(0));
60
+ this._setState({ pumpOn: on });
61
+ }
62
+ _setState(partial) {
63
+ this.state = { ...this.state, ...partial };
64
+ this.emit('stateChange', this.getState());
65
+ }
66
+ _scheduleReconnect() {
67
+ if (!this.stopped) {
68
+ this.log.debug(`Next reconnect attempt in ${this.reconnectDelay / 1000}s`);
69
+ setTimeout(() => this._connect(), this.reconnectDelay);
70
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
71
+ }
72
+ }
73
+ async _connect() {
74
+ this._setState({ status: 'CONNECTING' });
75
+ try {
76
+ const peripheral = await this._scan();
77
+ await new Promise((resolve, reject) => {
78
+ peripheral.connect((err) => err ? reject(err) : resolve());
79
+ });
80
+ this.peripheral = peripheral;
81
+ this.reconnectDelay = RECONNECT_DELAY_MS; // reset backoff on success
82
+ this.log.info(`BLE connected (${peripheral.advertisement.localName ?? peripheral.id})`);
83
+ peripheral.once('disconnect', () => {
84
+ this._stopTempPoll();
85
+ this.log.warn('BLE disconnected — reconnecting...');
86
+ this._setState({ status: 'DISCONNECTED' });
87
+ this._scheduleReconnect();
88
+ });
89
+ const chars = await new Promise((resolve, reject) => peripheral.discoverAllServicesAndCharacteristics((err, _svcs, chars) => err ? reject(err) : resolve(chars)));
90
+ this.chars = new Map(chars.map(c => [c.uuid, c]));
91
+ await this._readStatics();
92
+ this._startTempPoll();
93
+ this._setState({ status: 'CONNECTED' });
94
+ this.log.info('BLE ready — all characteristics discovered');
95
+ }
96
+ catch (err) {
97
+ this.log.error(`BLE connect error: ${err.message ?? err}`);
98
+ this._setState({ status: 'ERROR' });
99
+ this._scheduleReconnect();
100
+ }
101
+ }
102
+ async _scan() {
103
+ // noble exposes a public `state` getter via Object.defineProperty (non-enumerable)
104
+ await new Promise((resolve, reject) => {
105
+ if (noble.state === 'poweredOn') {
106
+ resolve();
107
+ return;
108
+ }
109
+ const timeout = setTimeout(() => reject(new Error('BT adapter not ready')), 10000);
110
+ noble.once('stateChange', (state) => {
111
+ clearTimeout(timeout);
112
+ if (state === 'poweredOn') {
113
+ resolve();
114
+ }
115
+ else if (state === 'unauthorized') {
116
+ reject(new Error('Bluetooth permission denied — grant access in System Settings → Privacy & Security → Bluetooth'));
117
+ }
118
+ else {
119
+ reject(new Error(`BT adapter not available (state: ${state})`));
120
+ }
121
+ });
122
+ });
123
+ return new Promise((resolve, reject) => {
124
+ const onDiscover = (p) => {
125
+ if (this.match(p)) {
126
+ clearTimeout(timeout);
127
+ noble.stopScanning();
128
+ noble.removeListener('discover', onDiscover);
129
+ resolve(p);
130
+ }
131
+ };
132
+ const timeout = setTimeout(() => {
133
+ noble.stopScanning();
134
+ noble.removeListener('discover', onDiscover);
135
+ reject(new Error('scan timeout — device not found'));
136
+ }, 15000);
137
+ noble.startScanning([], false, (err) => {
138
+ if (err) {
139
+ clearTimeout(timeout);
140
+ noble.removeListener('discover', onDiscover);
141
+ reject(err);
142
+ }
143
+ });
144
+ noble.on('discover', onDiscover);
145
+ });
146
+ }
147
+ async _readStatics() {
148
+ const readStr = async (uuid) => (await this._read(uuid))?.toString('utf8') ?? '';
149
+ const readU16 = async (uuid) => (await this._read(uuid))?.readUInt16LE(0) ?? 0;
150
+ const [fw, bleFw, serial, hours, minutes, autoOff, autoOffSecs, setpoint, temp, activity] = await Promise.all([
151
+ readStr(UUID_FIRMWARE),
152
+ readStr(UUID_BLE_FIRMWARE),
153
+ readStr(UUID_SERIAL),
154
+ readU16(UUID_HOURS),
155
+ readU16(UUID_MINUTES),
156
+ this._read(UUID_AUTO_SHUT_OFF),
157
+ this._read(UUID_AUTO_SHUT_OFF_SETTING),
158
+ this._read(UUID_HEATER_SETPOINT),
159
+ this._read(UUID_TEMP),
160
+ this._read(UUID_PUMP_NOTIFICATIONS),
161
+ ]);
162
+ this._setState({
163
+ firmwareVersion: fw,
164
+ bleVersion: bleFw,
165
+ serialNumber: serial,
166
+ hoursOfOperation: hours,
167
+ minutesOfOperation: minutes,
168
+ autoShutoff: (autoOff?.readUInt8(0) ?? 0) === 0x01,
169
+ autoShutoffMinutes: Math.round((autoOffSecs?.readUInt16LE(0) ?? 0) / 60),
170
+ targetTemp: (setpoint?.readUInt16LE(0) ?? 0) / 10,
171
+ currentTemp: (temp?.readUInt16LE(0) ?? 0) / 10,
172
+ ...(activity ? this._decodeActivity(activity) : {}),
173
+ });
174
+ }
175
+ _decodeActivity(data) {
176
+ const value = data.length >= 2 ? data.readUInt16LE(0) : 0;
177
+ return {
178
+ heaterOn: (value & 0x0020) !== 0,
179
+ pumpOn: (value & 0x2000) !== 0,
180
+ };
181
+ }
182
+ _startTempPoll() {
183
+ this._stopTempPoll();
184
+ const poll = async () => {
185
+ if (this.stopped || this.state.status !== 'CONNECTED') {
186
+ return;
187
+ }
188
+ try {
189
+ const temp = await this._read(UUID_TEMP);
190
+ if (temp) {
191
+ this._setState({ currentTemp: temp.readUInt16LE(0) / 10 });
192
+ }
193
+ const activity = await this._read(UUID_PUMP_NOTIFICATIONS);
194
+ if (activity) {
195
+ this._setState(this._decodeActivity(activity));
196
+ }
197
+ }
198
+ catch (err) {
199
+ this.log.warn(`BLE poll error: ${err.message} — disconnecting`);
200
+ this.peripheral?.disconnect();
201
+ this.peripheral = null;
202
+ return;
203
+ }
204
+ this.tempPollTimer = setTimeout(poll, TEMP_POLL_INTERVAL_MS);
205
+ };
206
+ this.tempPollTimer = setTimeout(poll, TEMP_POLL_INTERVAL_MS);
207
+ }
208
+ _stopTempPoll() {
209
+ if (this.tempPollTimer) {
210
+ clearTimeout(this.tempPollTimer);
211
+ this.tempPollTimer = null;
212
+ }
213
+ }
214
+ _read(uuid) {
215
+ const char = this.chars.get(uuid.replace(/-/g, ''));
216
+ if (!char) {
217
+ return Promise.resolve(null);
218
+ }
219
+ return new Promise((resolve, reject) => {
220
+ char.read((err, data) => err ? reject(err) : resolve(data));
221
+ });
222
+ }
223
+ _write(uuid, data) {
224
+ if (this.state.status !== 'CONNECTED') {
225
+ return Promise.reject(new Error('device not connected'));
226
+ }
227
+ const char = this.chars.get(uuid.replace(/-/g, ''));
228
+ if (!char) {
229
+ return Promise.reject(new Error(`characteristic ${uuid} not found`));
230
+ }
231
+ // write(data, withoutResponse=false) = Write With Response, required by device
232
+ return new Promise((resolve, reject) => {
233
+ char.write(data, false, (err) => err ? reject(err) : resolve());
234
+ });
235
+ }
236
+ }
237
+ //# sourceMappingURL=bleManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bleManager.js","sourceRoot":"","sources":["../src/bleManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export declare const UUID_TEMP = "10110001-5354-4f52-5a26-4249434b454c";
2
+ export declare const UUID_HEATER_SETPOINT = "10110003-5354-4f52-5a26-4249434b454c";
3
+ export declare const UUID_LED_BRIGHTNESS = "10110005-5354-4f52-5a26-4249434b454c";
4
+ export declare const UUID_AUTO_SHUT_OFF = "1011000c-5354-4f52-5a26-4249434b454c";
5
+ export declare const UUID_AUTO_SHUT_OFF_SETTING = "1011000d-5354-4f52-5a26-4249434b454c";
6
+ export declare const UUID_HEAT_ON = "1011000f-5354-4f52-5a26-4249434b454c";
7
+ export declare const UUID_HEAT_OFF = "10110010-5354-4f52-5a26-4249434b454c";
8
+ export declare const UUID_PUMP_ON = "10110013-5354-4f52-5a26-4249434b454c";
9
+ export declare const UUID_PUMP_OFF = "10110014-5354-4f52-5a26-4249434b454c";
10
+ export declare const UUID_HOURS = "10110015-5354-4f52-5a26-4249434b454c";
11
+ export declare const UUID_MINUTES = "10110016-5354-4f52-5a26-4249434b454c";
12
+ export declare const UUID_PUMP_NOTIFICATIONS = "1010000c-5354-4f52-5a26-4249434b454c";
13
+ export declare const UUID_FIRMWARE = "10100003-5354-4f52-5a26-4249434b454c";
14
+ export declare const UUID_BLE_FIRMWARE = "10100004-5354-4f52-5a26-4249434b454c";
15
+ export declare const UUID_SERIAL = "10100008-5354-4f52-5a26-4249434b454c";
16
+ export declare const TEMP_POLL_INTERVAL_MS = 1000;
17
+ export declare const RECONNECT_DELAY_MS = 3000;
@@ -0,0 +1,19 @@
1
+ const SUFFIX = '5354-4f52-5a26-4249434b454c';
2
+ export const UUID_TEMP = `10110001-${SUFFIX}`;
3
+ export const UUID_HEATER_SETPOINT = `10110003-${SUFFIX}`;
4
+ export const UUID_LED_BRIGHTNESS = `10110005-${SUFFIX}`;
5
+ export const UUID_AUTO_SHUT_OFF = `1011000c-${SUFFIX}`;
6
+ export const UUID_AUTO_SHUT_OFF_SETTING = `1011000d-${SUFFIX}`;
7
+ export const UUID_HEAT_ON = `1011000f-${SUFFIX}`;
8
+ export const UUID_HEAT_OFF = `10110010-${SUFFIX}`;
9
+ export const UUID_PUMP_ON = `10110013-${SUFFIX}`;
10
+ export const UUID_PUMP_OFF = `10110014-${SUFFIX}`;
11
+ export const UUID_HOURS = `10110015-${SUFFIX}`;
12
+ export const UUID_MINUTES = `10110016-${SUFFIX}`;
13
+ export const UUID_PUMP_NOTIFICATIONS = `1010000c-${SUFFIX}`;
14
+ export const UUID_FIRMWARE = `10100003-${SUFFIX}`;
15
+ export const UUID_BLE_FIRMWARE = `10100004-${SUFFIX}`;
16
+ export const UUID_SERIAL = `10100008-${SUFFIX}`;
17
+ export const TEMP_POLL_INTERVAL_MS = 1000;
18
+ export const RECONNECT_DELAY_MS = 3000;
19
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,6BAA6B,CAAC;AAE7C,MAAM,CAAC,MAAM,SAAS,GAAoB,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAS,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,mBAAmB,GAAU,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,kBAAkB,GAAW,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,0BAA0B,GAAG,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,YAAY,GAAiB,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,aAAa,GAAgB,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,YAAY,GAAiB,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,aAAa,GAAgB,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,UAAU,GAAmB,YAAY,MAAM,EAAE,CAAC;AAC/D,MAAM,CAAC,MAAM,YAAY,GAAiB,YAAY,MAAM,EAAE,CAAC;AAE/D,MAAM,CAAC,MAAM,uBAAuB,GAAG,YAAY,MAAM,EAAE,CAAC;AAC5D,MAAM,CAAC,MAAM,aAAa,GAAa,YAAY,MAAM,EAAE,CAAC;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAS,YAAY,MAAM,EAAE,CAAC;AAC5D,MAAM,CAAC,MAAM,WAAW,GAAe,YAAY,MAAM,EAAE,CAAC;AAE5D,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC1C,MAAM,CAAC,MAAM,kBAAkB,GAAM,IAAI,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { API } from 'homebridge';
2
+ declare const _default: (api: API) => void;
3
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { VolcanoPlatform } from './platform.js';
2
+ import { PLATFORM_NAME } from './settings.js';
3
+ export default (api) => {
4
+ api.registerPlatform(PLATFORM_NAME, VolcanoPlatform);
5
+ };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,eAAe,CAAC,GAAQ,EAAE,EAAE;IAC1B,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AACvD,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { API, DynamicPlatformPlugin, HAP, Logging, PlatformAccessory, PlatformConfig } from 'homebridge';
2
+ export declare class VolcanoPlatform implements DynamicPlatformPlugin {
3
+ readonly log: Logging;
4
+ readonly config: PlatformConfig;
5
+ readonly api: API;
6
+ readonly hap: HAP;
7
+ private readonly accessories;
8
+ private readonly managers;
9
+ constructor(log: Logging, config: PlatformConfig, api: API);
10
+ configureAccessory(accessory: PlatformAccessory): void;
11
+ private _init;
12
+ }
@@ -0,0 +1,59 @@
1
+ import { BleManager } from './bleManager.js';
2
+ import { VolcanoAccessory } from './volcanoAccessory.js';
3
+ import { PLUGIN_NAME, PLATFORM_NAME } from './settings.js';
4
+ export class VolcanoPlatform {
5
+ log;
6
+ config;
7
+ api;
8
+ hap;
9
+ accessories = [];
10
+ managers = [];
11
+ constructor(log, config, api) {
12
+ this.log = log;
13
+ this.config = config;
14
+ this.api = api;
15
+ this.hap = api.hap;
16
+ this.api.on('didFinishLaunching', () => this._init());
17
+ this.api.on('shutdown', () => {
18
+ for (const manager of this.managers) {
19
+ manager.stop();
20
+ }
21
+ });
22
+ }
23
+ configureAccessory(accessory) {
24
+ this.accessories.push(accessory);
25
+ }
26
+ _init() {
27
+ try {
28
+ const devices = this.config['devices'] ?? [];
29
+ if (devices.length === 0) {
30
+ this.log.warn('No devices configured. Add at least one device in the plugin settings.');
31
+ return;
32
+ }
33
+ if (devices.length > 1) {
34
+ this.log.warn('Multiple devices configured, but only one BLE device is supported at a time. Only the first device will be used.');
35
+ }
36
+ const device = devices[0];
37
+ if (!device.name || !device.address) {
38
+ this.log.error(`Device is missing required name or address: ${JSON.stringify(device)}`);
39
+ return;
40
+ }
41
+ const uuid = this.api.hap.uuid.generate(device.address);
42
+ let accessory = this.accessories.find((a) => a.UUID === uuid);
43
+ if (!accessory) {
44
+ accessory = new this.api.platformAccessory(device.name, uuid);
45
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
46
+ }
47
+ const identifier = device.bleAdvertisementName ?? 'VOLCANO H';
48
+ const manager = new BleManager(identifier, this.log);
49
+ this.managers.push(manager);
50
+ new VolcanoAccessory(this, accessory, manager);
51
+ manager.start();
52
+ this.log.info(`Registered ${device.name} — scanning for "${identifier}"`);
53
+ }
54
+ catch (err) {
55
+ this.log.error(`Failed to initialise platform: ${err.message ?? String(err)}`);
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const PLATFORM_NAME = "VolcanoHybrid";
2
+ export declare const PLUGIN_NAME = "homebridge-volcano-hybrid";
@@ -0,0 +1,3 @@
1
+ export const PLATFORM_NAME = 'VolcanoHybrid';
2
+ export const PLUGIN_NAME = 'homebridge-volcano-hybrid';
3
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAC7C,MAAM,CAAC,MAAM,WAAW,GAAK,2BAA2B,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface VolcanoState {
2
+ status: 'DISCONNECTED' | 'CONNECTING' | 'CONNECTED' | 'ERROR';
3
+ currentTemp: number;
4
+ targetTemp: number;
5
+ heaterOn: boolean;
6
+ pumpOn: boolean;
7
+ autoShutoff: boolean;
8
+ autoShutoffMinutes: number;
9
+ firmwareVersion: string;
10
+ bleVersion: string;
11
+ serialNumber: string;
12
+ hoursOfOperation: number;
13
+ minutesOfOperation: number;
14
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { VolcanoPlatform } from './platform.js';
3
+ import { BleManager } from './bleManager.js';
4
+ export declare class VolcanoAccessory {
5
+ private readonly platform;
6
+ private readonly accessory;
7
+ private readonly manager;
8
+ private readonly infoService;
9
+ private targetTempControl;
10
+ private heatSwitch;
11
+ private pumpSwitch;
12
+ private state;
13
+ constructor(platform: VolcanoPlatform, accessory: PlatformAccessory, manager: BleManager);
14
+ private _onState;
15
+ }
@@ -0,0 +1,115 @@
1
+ export class VolcanoAccessory {
2
+ platform;
3
+ accessory;
4
+ manager;
5
+ infoService;
6
+ targetTempControl;
7
+ heatSwitch;
8
+ pumpSwitch;
9
+ state = {
10
+ status: 'DISCONNECTED',
11
+ currentTemp: 0, targetTemp: 0,
12
+ heaterOn: false, pumpOn: false,
13
+ autoShutoff: false, autoShutoffMinutes: 0,
14
+ firmwareVersion: '', bleVersion: '', serialNumber: '',
15
+ hoursOfOperation: 0, minutesOfOperation: 0,
16
+ };
17
+ constructor(platform, accessory, manager) {
18
+ this.platform = platform;
19
+ this.accessory = accessory;
20
+ this.manager = manager;
21
+ const { Service: Svc, Characteristic: C, HapStatusError, HAPStatus } = platform.hap;
22
+ this.infoService = accessory.getService(Svc.AccessoryInformation)
23
+ .setCharacteristic(C.Manufacturer, 'Storz & Bickel')
24
+ .setCharacteristic(C.Model, 'Volcano Hybrid');
25
+ // Thermostat tile — shows current temp on arc, target temp as setpoint
26
+ this.targetTempControl = accessory.getServiceById(Svc.Thermostat, 'target-temp')
27
+ ?? accessory.addService(Svc.Thermostat, 'Target Temp', 'target-temp');
28
+ this.targetTempControl.getCharacteristic(C.CurrentTemperature)
29
+ .setProps({ minValue: 0, maxValue: 300 })
30
+ .onGet(() => this.state.currentTemp);
31
+ this.targetTempControl.getCharacteristic(C.TargetTemperature)
32
+ .setProps({ minValue: 40, maxValue: 230, minStep: 1 })
33
+ .onGet(() => Math.max(40, this.state.targetTemp))
34
+ .onSet(async (v) => {
35
+ try {
36
+ await this.manager.setTargetTemp(v);
37
+ }
38
+ catch (err) {
39
+ this.platform.log.error(`setTargetTemp failed: ${err.message}`);
40
+ throw new HapStatusError(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
41
+ }
42
+ });
43
+ this.targetTempControl.getCharacteristic(C.CurrentHeatingCoolingState)
44
+ .onGet(() => this.state.heaterOn ? 1 : 0);
45
+ // TargetHeatingCoolingState doubles as a heater on/off control on the thermostat tile.
46
+ // It stays in sync with the Heat switch — both control the same thing.
47
+ this.targetTempControl.getCharacteristic(C.TargetHeatingCoolingState)
48
+ .setProps({ validValues: [0, 1] })
49
+ .onGet(() => this.state.heaterOn ? 1 : 0)
50
+ .onSet(async (v) => {
51
+ try {
52
+ await this.manager.setHeater(v === 1);
53
+ }
54
+ catch (err) {
55
+ this.platform.log.error(`setHeater failed: ${err.message}`);
56
+ throw new HapStatusError(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
57
+ }
58
+ });
59
+ // Heat switch
60
+ this.heatSwitch = accessory.getServiceById(Svc.Switch, 'heat')
61
+ ?? accessory.addService(Svc.Switch, 'Heat', 'heat');
62
+ this.heatSwitch.getCharacteristic(C.On)
63
+ .onGet(() => this.state.heaterOn)
64
+ .onSet(async (v) => {
65
+ try {
66
+ await this.manager.setHeater(v);
67
+ }
68
+ catch (err) {
69
+ this.platform.log.error(`setHeater failed: ${err.message}`);
70
+ throw new HapStatusError(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
71
+ }
72
+ });
73
+ // Pump switch
74
+ this.pumpSwitch = accessory.getServiceById(Svc.Switch, 'pump')
75
+ ?? accessory.addService(Svc.Switch, 'Pump', 'pump');
76
+ this.pumpSwitch.getCharacteristic(C.On)
77
+ .onGet(() => this.state.pumpOn)
78
+ .onSet(async (v) => {
79
+ try {
80
+ await this.manager.setPump(v);
81
+ }
82
+ catch (err) {
83
+ this.platform.log.error(`setPump failed: ${err.message}`);
84
+ throw new HapStatusError(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
85
+ }
86
+ });
87
+ manager.on('stateChange', (state) => this._onState(state));
88
+ }
89
+ _onState(state) {
90
+ const prev = this.state;
91
+ this.state = state;
92
+ const { Characteristic: C } = this.platform.hap;
93
+ // Populate AccessoryInformation once the device reports its identity
94
+ if (state.serialNumber && state.serialNumber !== prev.serialNumber) {
95
+ this.infoService
96
+ .setCharacteristic(C.SerialNumber, state.serialNumber)
97
+ .setCharacteristic(C.FirmwareRevision, state.firmwareVersion);
98
+ }
99
+ if (state.currentTemp !== prev.currentTemp) {
100
+ this.targetTempControl.updateCharacteristic(C.CurrentTemperature, state.currentTemp);
101
+ }
102
+ if (state.targetTemp !== prev.targetTemp) {
103
+ this.targetTempControl.updateCharacteristic(C.TargetTemperature, Math.min(230, Math.max(40, state.targetTemp)));
104
+ }
105
+ if (state.heaterOn !== prev.heaterOn) {
106
+ this.targetTempControl.updateCharacteristic(C.CurrentHeatingCoolingState, state.heaterOn ? 1 : 0);
107
+ this.targetTempControl.updateCharacteristic(C.TargetHeatingCoolingState, state.heaterOn ? 1 : 0);
108
+ this.heatSwitch.updateCharacteristic(C.On, state.heaterOn);
109
+ }
110
+ if (state.pumpOn !== prev.pumpOn) {
111
+ this.pumpSwitch.updateCharacteristic(C.On, state.pumpOn);
112
+ }
113
+ }
114
+ }
115
+ //# sourceMappingURL=volcanoAccessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"volcanoAccessory.js","sourceRoot":"","sources":["../src/volcanoAccessory.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "homebridge-volcano-hybrid",
3
+ "version": "0.1.0",
4
+ "displayName": "Volcano Hybrid",
5
+ "description": "Homebridge plugin for the Storz & Bickel Volcano Hybrid vaporizer",
6
+ "license": "MIT",
7
+ "author": "Rosh Beed",
8
+ "type": "module",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/RoshBeed/homebridge-volcano-hybrid.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/RoshBeed/homebridge-volcano-hybrid/issues"
15
+ },
16
+ "homepage": "https://github.com/RoshBeed/homebridge-volcano-hybrid#readme",
17
+ "main": "dist/index.js",
18
+ "types": "dist/index.d.ts",
19
+ "files": [
20
+ "dist",
21
+ "config.schema.json"
22
+ ],
23
+ "keywords": [
24
+ "homebridge-plugin",
25
+ "homebridge",
26
+ "volcano",
27
+ "volcano-hybrid",
28
+ "storz-bickel"
29
+ ],
30
+ "scripts": {
31
+ "build": "rimraf ./dist && tsc",
32
+ "watch": "tsc --watch",
33
+ "lint": "eslint src/",
34
+ "prepublishOnly": "npm run lint && npm run build"
35
+ },
36
+ "dependencies": {
37
+ "@abandonware/noble": "^1.9.2-22"
38
+ },
39
+ "devDependencies": {
40
+ "@eslint/js": "^9.0.0",
41
+ "@types/node": "^22.10.0",
42
+ "eslint": "^9.0.0",
43
+ "homebridge": "^2.0.0",
44
+ "rimraf": "^6.0.0",
45
+ "typescript": "^5.4.0",
46
+ "typescript-eslint": "^8.0.0"
47
+ },
48
+ "peerDependencies": {
49
+ "homebridge": "^1.8.0 || ^2.0.0"
50
+ },
51
+ "engines": {
52
+ "node": "^22.10.0 || ^24.0.0",
53
+ "homebridge": "^1.8.0 || ^2.0.0"
54
+ }
55
+ }