homebridge-nest-accfactory 0.3.0 → 0.3.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.
package/dist/config.js CHANGED
@@ -1,23 +1,19 @@
1
1
  // Configuration validation and processing
2
2
  // Part of homebridge-nest-accfactory
3
3
  //
4
- // Code version 2025.06.12
4
+ // Code version 2025.06.30
5
5
  // Mark Hulskamp
6
6
  'use strict';
7
7
 
8
8
  // Define nodejs module requirements
9
- import fs from 'fs';
10
9
  import path from 'node:path';
11
10
  import crypto from 'node:crypto';
12
- import process from 'node:process';
13
- import child_process from 'node:child_process';
11
+
12
+ // Import our modules
13
+ import FFmpeg from './ffmpeg.js';
14
14
 
15
15
  // Define constants
16
- const FFMPEGVERSION = '6.0.0';
17
- const AccountType = {
18
- Nest: 'Nest',
19
- Google: 'Google',
20
- };
16
+ import { FFMPEG_VERSION, ACCOUNT_TYPE } from './consts.js';
21
17
 
22
18
  function processConfig(config, log) {
23
19
  let options = (config.options = typeof config?.options === 'object' ? config.options : {});
@@ -40,93 +36,79 @@ function processConfig(config, log) {
40
36
  options.maxStreams = isNaN(config.options?.maxStreams) === false ? Number(config.options.maxStreams) : 2;
41
37
 
42
38
  // Check if a ffmpeg binary exist via a specific path in configuration OR /usr/local/bin
43
- options.ffmpeg = {};
44
- options.ffmpeg.debug = config.options?.ffmpegDebug === true;
45
- options.ffmpeg.binary = path.resolve(
46
- typeof config.options?.ffmpegPath === 'string' && config.options.ffmpegPath !== '' ? config.options.ffmpegPath : '/usr/local/bin',
47
- );
48
-
49
- // If the path doesn't include 'ffmpeg' on the end, we'll add it here
50
- if (options.ffmpeg.binary.endsWith('/ffmpeg') === false) {
51
- options.ffmpeg.binary += '/ffmpeg';
52
- }
53
-
54
- options.ffmpeg.version = undefined;
55
- options.ffmpeg.libspeex = false;
56
- options.ffmpeg.libopus = false;
57
- options.ffmpeg.libx264 = false;
58
- options.ffmpeg.libfdk_aac = false;
59
-
60
- if (fs.existsSync(options.ffmpeg.binary) === false) {
61
- // If we flag ffmpegPath as undefined, no video streaming/record support enabled for camers/doorbells
62
- log?.warn?.('Specified ffmpeg binary "%s" was not found', options.ffmpeg.binary);
63
- log?.warn?.('Stream video/recording from camera/doorbells will be unavailable');
64
- options.ffmpeg.binary = undefined;
39
+ options.ffmpeg = {
40
+ binary: undefined,
41
+ valid: false,
42
+ debug: config.options?.ffmpegDebug === true,
43
+ hwaccel: false,
44
+ };
45
+
46
+ let ffmpegPath =
47
+ typeof config.options?.ffmpegPath === 'string' && config.options.ffmpegPath !== '' ? config.options.ffmpegPath : '/usr/local/bin';
48
+
49
+ let resolvedPath = path.resolve(ffmpegPath);
50
+ if (resolvedPath.endsWith('/ffmpeg') === false) {
51
+ resolvedPath += '/ffmpeg';
65
52
  }
66
53
 
67
- if (fs.existsSync(options.ffmpeg.binary) === true) {
68
- let ffmpegProcess = child_process.spawnSync(options.ffmpeg.binary, ['-version'], {
69
- env: process.env,
54
+ // Create FFmpeg probe
55
+ let ffmpeg = new FFmpeg(resolvedPath, log);
56
+
57
+ if (typeof ffmpeg.version !== 'string') {
58
+ log?.warn?.('ffmpeg binary "%s" not found or not executable, camera/doorbell streaming will be unavailable', ffmpeg.binary);
59
+ } else {
60
+ // Proceed with compatibility checks
61
+ options.ffmpeg.valid = ffmpeg.hasMinimumSupport({
62
+ version: FFMPEG_VERSION,
63
+ encoders: ['libx264', 'libfdk_aac', 'libopus'],
64
+ decoders: ['libspeex'],
70
65
  });
66
+ if (options.ffmpeg.valid === false) {
67
+ log?.warn?.('ffmpeg binary "%s" does not meet the minimum support requirements', ffmpeg.binary);
71
68
 
72
- if (ffmpegProcess.stdout !== null) {
73
- let stdout = ffmpegProcess.stdout.toString();
74
-
75
- // Determine what libraries ffmpeg is compiled with
76
- options.ffmpeg.version = stdout.match(/(?:ffmpeg version:(\d+)\.)?(?:(\d+)\.)?(?:(\d+)\.\d+)(.*?)/gim)?.[0];
77
- options.ffmpeg.libspeex = stdout.includes('--enable-libspeex') === true;
78
- options.ffmpeg.libopus = stdout.includes('--enable-libopus') === true;
79
- options.ffmpeg.libx264 = stdout.includes('--enable-libx264') === true;
80
- options.ffmpeg.libfdk_aac = stdout.includes('--enable-libfdk-aac') === true;
81
-
82
- let versionTooOld =
83
- options.ffmpeg.version?.localeCompare(FFMPEGVERSION, undefined, {
69
+ if (
70
+ ffmpeg.version?.localeCompare(FFMPEG_VERSION, undefined, {
84
71
  numeric: true,
85
72
  sensitivity: 'case',
86
73
  caseFirst: 'upper',
87
- }) === -1;
74
+ }) === -1
75
+ ) {
76
+ log?.warn?.('Minimum binary version is "%s", however the installed version is "%s"', FFMPEG_VERSION, ffmpeg.version);
77
+ log?.warn?.('Stream video/recording from camera/doorbells will be unavailable');
78
+ }
79
+
80
+ if ((ffmpeg.features?.decoders || []).includes('libspeex') === false) {
81
+ log?.warn?.('Missing speex decoder in ffmpeg, talkback on certain camera/doorbells will be unavailable');
82
+ }
88
83
 
89
84
  if (
90
- versionTooOld ||
91
- options.ffmpeg.libspeex === false ||
92
- options.ffmpeg.libopus === false ||
93
- options.ffmpeg.libx264 === false ||
94
- options.ffmpeg.libfdk_aac === false
85
+ (ffmpeg.features?.encoders || []).includes('libfdk_aac') === false &&
86
+ (ffmpeg.features?.encoders || []).includes('libopus') === false
95
87
  ) {
96
- log?.warn?.('ffmpeg binary "%s" does not meet the minimum support requirements', options.ffmpeg.binary);
97
-
98
- if (versionTooOld) {
99
- log?.warn?.('Minimum binary version is "%s", however the installed version is "%s"', FFMPEGVERSION, options.ffmpeg.version);
100
- log?.warn?.('Stream video/recording from camera/doorbells will be unavailable');
101
- options.ffmpeg.binary = undefined; // No ffmpeg since below min version
102
- }
103
-
104
- if (!options.ffmpeg.libspeex && options.ffmpeg.libx264 && options.ffmpeg.libfdk_aac) {
105
- log?.warn?.('Missing libspeex in ffmpeg binary, talkback on certain camera/doorbells will be unavailable');
106
- }
107
-
108
- if (options.ffmpeg.libx264 && !options.ffmpeg.libfdk_aac && !options.ffmpeg.libopus) {
109
- log?.warn?.('Missing libfdk_aac and libopus in ffmpeg binary, audio from camera/doorbells will be unavailable');
110
- }
111
-
112
- if (options.ffmpeg.libx264 && !options.ffmpeg.libfdk_aac) {
113
- log?.warn?.('Missing libfdk_aac in ffmpeg binary, audio from camera/doorbells will be unavailable');
114
- }
115
-
116
- if (options.ffmpeg.libx264 && options.ffmpeg.libfdk_aac && !options.ffmpeg.libopus) {
117
- log?.warn?.('Missing libopus in ffmpeg binary, audio (including talkback) from certain camera/doorbells will be unavailable');
118
- }
119
-
120
- if (!options.ffmpeg.libx264) {
121
- log?.warn?.('Missing libx264 in ffmpeg binary, stream video/recording from camera/doorbells will be unavailable');
122
- options.ffmpeg.binary = undefined; // No ffmpeg since we do not have all the required libraries
123
- }
88
+ log?.warn?.('Missing fdk_aac and opus encoders in ffmpeg, audio from camera/doorbells will be unavailable');
89
+ }
90
+
91
+ if ((ffmpeg.features?.encoders || []).includes('libfdk_aac') === false) {
92
+ log?.warn?.('Missing fdk_aac encoder in ffmpeg, audio from camera/doorbells will be unavailable');
93
+ }
94
+
95
+ if ((ffmpeg.features?.encoders || []).includes('libopus') === false) {
96
+ log?.warn?.('Missing opus encoder in ffmpeg, talkback on certain camera/doorbells will be unavailable');
97
+ }
98
+
99
+ if ((ffmpeg.features?.encoders || []).includes('libx264') === false) {
100
+ log?.warn?.('Missing libx264 encoder in ffmpeg, stream video/recording from camera/doorbells will be unavailable');
124
101
  }
125
102
  }
126
- }
127
103
 
128
- if (options.ffmpeg.binary !== undefined) {
129
- log?.success?.('Found valid ffmpeg binary in %s', options.ffmpeg.binary);
104
+ if (options.ffmpeg.valid === true) {
105
+ log?.success?.('Found valid ffmpeg binary in %s', ffmpeg.binary);
106
+ options.ffmpeg.binary = ffmpeg.binary;
107
+ options.ffmpeg.hwaccel = ffmpeg.supportsHardwareH264 === true;
108
+ if (ffmpeg.supportsHardwareH264 === true) {
109
+ log?.debug?.('Hardware H264 encoding available via "%s"', ffmpeg.hardwareH264Codec);
110
+ }
111
+ }
130
112
  }
131
113
 
132
114
  // Process per device configuration(s)
@@ -135,7 +117,7 @@ function processConfig(config, log) {
135
117
  }
136
118
 
137
119
  if (config?.devices !== undefined && Array.isArray(config.devices) === false) {
138
- // If the devices section is a JSON oject keyed by the devices serial number, convert to devices array object
120
+ // If the devices section is a JSON object keyed by the devices serial number, convert to devices array object
139
121
  let newDeviceArray = [];
140
122
  for (const [serialNumber, props] of Object.entries(config.devices)) {
141
123
  newDeviceArray.push({
@@ -167,7 +149,7 @@ function buildConnections(config) {
167
149
  let fieldTest = section?.fieldTest === true;
168
150
  connections[crypto.randomUUID()] = {
169
151
  name: key,
170
- type: AccountType.Nest,
152
+ type: ACCOUNT_TYPE.NEST,
171
153
  authorised: false,
172
154
  access_token: section.access_token,
173
155
  fieldTest,
@@ -187,7 +169,7 @@ function buildConnections(config) {
187
169
  let fieldTest = section?.fieldTest === true;
188
170
  connections[crypto.randomUUID()] = {
189
171
  name: key,
190
- type: AccountType.Google,
172
+ type: ACCOUNT_TYPE.GOOGLE,
191
173
  authorised: false,
192
174
  issuetoken: section.issuetoken,
193
175
  cookie: section.cookie,
@@ -204,4 +186,4 @@ function buildConnections(config) {
204
186
  }
205
187
 
206
188
  // Define exports
207
- export { AccountType, processConfig, buildConnections };
189
+ export { processConfig, buildConnections };
package/dist/consts.js ADDED
@@ -0,0 +1,160 @@
1
+ // Common defines
2
+ // Part of homebridge-nest-accfactory
3
+ //
4
+ // Code version 2025.08.07
5
+ // Mark Hulskamp
6
+ 'use strict';
7
+
8
+ // Define nodejs module requirements
9
+ import path from 'node:path';
10
+ import url from 'node:url';
11
+
12
+ // Define constants
13
+ export const TIMERS = {
14
+ ALERTS: 2000, // Camera alert polling interval (ms)
15
+ ZONES: 30000, // Camera zone polling interval (ms)
16
+ WEATHER: 300000, // Weather data refresh interval (ms)
17
+ NEST_API: 10000, // Nest API request timeout (ms)
18
+ TALKBACK_AUDIO: 1000, // Audio talkback timeout (ms)
19
+ SNAPSHOT: 30000, // Timeout for retaining snapshot image timeout (ms)
20
+ };
21
+
22
+ export const USER_AGENT = 'Nest/5.82.2 (iOScom.nestlabs.jasper.release) os=18.5'; // User Agent string
23
+
24
+ export const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); // Make a defined for JS __dirname
25
+
26
+ export const DATA_SOURCE = {
27
+ NEST: 'Nest', // From the Nest API
28
+ GOOGLE: 'Google', // From the Protobuf/Google API
29
+ };
30
+
31
+ export const DAYS_OF_WEEK_FULL = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY'];
32
+
33
+ export const DAYS_OF_WEEK_SHORT = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'];
34
+
35
+ export const PROTOBUF_RESOURCES = {
36
+ THERMOSTAT: [
37
+ 'nest.resource.NestAmber1DisplayResource',
38
+ 'nest.resource.NestAmber2DisplayResource',
39
+ 'nest.resource.NestLearningThermostat1Resource',
40
+ 'nest.resource.NestLearningThermostat2Resource',
41
+ 'nest.resource.NestLearningThermostat3Resource',
42
+ 'nest.resource.NestAgateDisplayResource',
43
+ 'nest.resource.NestOnyxResource',
44
+ 'google.resource.GoogleZirconium1Resource',
45
+ 'google.resource.GoogleBismuth1Resource',
46
+ ],
47
+ HEATLINK: ['nest.resource.NestAgateHeatlinkResource'],
48
+ KRYPTONITE: ['nest.resource.NestKryptoniteResource'],
49
+ LOCK: ['yale.resource.LinusLockResource'],
50
+ PROTECT: [
51
+ 'nest.resource.NestProtect1LinePoweredResource',
52
+ 'nest.resource.NestProtect1BatteryPoweredResource',
53
+ 'nest.resource.NestProtect2LinePoweredResource',
54
+ 'nest.resource.NestProtect2BatteryPoweredResource',
55
+ 'nest.resource.NestProtect2Resource',
56
+ ],
57
+ CAMERA: [
58
+ 'google.resource.GreenQuartzResource',
59
+ 'google.resource.SpencerResource',
60
+ 'google.resource.VenusResource',
61
+ 'nest.resource.NestCamIndoorResource',
62
+ 'nest.resource.NestCamIQResource',
63
+ 'nest.resource.NestCamIQOutdoorResource',
64
+ 'nest.resource.NestCamOutdoorResource',
65
+ 'nest.resource.NestHelloResource',
66
+ 'google.resource.GoogleNewmanResource',
67
+ ],
68
+ DOORBELL: ['nest.resource.NestHelloResource', 'google.resource.GreenQuartzResource', 'google.resource.VenusResource'],
69
+ FLOODLIGHT: ['google.resource.NeonQuartzResource', 'google.resource.AzizResource'],
70
+ CONNECT: ['nest.resource.NestConnectResource'],
71
+ DETECT: ['nest.resource.NestDetectResource'],
72
+ GUARD: ['nest.resource.NestHelloResource'],
73
+ };
74
+
75
+ export const NEST_API_BUCKETS = [
76
+ 'buckets',
77
+ 'delayed_topaz',
78
+ 'demand_response',
79
+ 'device',
80
+ 'device_alert_dialog',
81
+ 'geofence_info',
82
+ 'kryptonite',
83
+ 'link',
84
+ 'message',
85
+ 'message_center',
86
+ 'metadata',
87
+ 'occupancy',
88
+ 'quartz',
89
+ 'safety',
90
+ 'rcs_settings',
91
+ 'safety_summary',
92
+ 'schedule',
93
+ 'shared',
94
+ 'structure',
95
+ 'structure_metadata',
96
+ 'topaz',
97
+ 'topaz_resource',
98
+ 'track',
99
+ 'trip',
100
+ 'tuneups',
101
+ 'user',
102
+ 'user_settings',
103
+ 'where',
104
+ 'widget_track',
105
+ ];
106
+
107
+ export const DEVICE_TYPE = {
108
+ THERMOSTAT: 'Thermostat',
109
+ TEMPSENSOR: 'TemperatureSensor',
110
+ PROTECT: 'Protect',
111
+ CAMERA: 'Camera',
112
+ DOORBELL: 'Doorbell',
113
+ FLOODLIGHT: 'FloodlightCamera',
114
+ WEATHER: 'Weather',
115
+ HEATLINK: 'Heatlink',
116
+ LOCK: 'Lock',
117
+ ALARM: 'Alarm',
118
+ };
119
+
120
+ export const FFMPEG_VERSION = '6.0.0';
121
+
122
+ export const ACCOUNT_TYPE = {
123
+ NEST: 'Nest',
124
+ GOOGLE: 'Google',
125
+ };
126
+
127
+ export const LOW_BATTERY_LEVEL = 10; // Low battery level percentage
128
+
129
+ export const THERMOSTAT_MIN_TEMPERATURE = 9; // Minimum temperature for Nest Thermostat
130
+
131
+ export const THERMOSTAT_MAX_TEMPERATURE = 32; // Maximum temperature for Nest Thermostat
132
+
133
+ export const HOTWATER_MIN_TEMPERATURE = 30; // Minimum temperature for hotwater heating
134
+
135
+ export const HOTWATER_MAX_TEMPERATURE = 70; // Maximum temperature for hotwater heating
136
+
137
+ export const HOTWATER_BOOST_TIMES = [1800, 3600, 7200]; // Valid hotwater boost times
138
+
139
+ export const RESOURCE_PATH = './res';
140
+ export const RESOURCE_IMAGES = {
141
+ CAMERA_OFFLINE: 'Nest_camera_offline.jpg',
142
+ CAMERA_OFF: 'Nest_camera_off.jpg',
143
+ CAMERA_TRANSFER: 'Nest_camera_transfer.jpg',
144
+ };
145
+
146
+ export const RESOURCE_FRAMES = {
147
+ CAMERA_OFFLINE: 'Nest_camera_offline.h264',
148
+ CAMERA_OFF: 'Nest_camera_off.h264',
149
+ CAMERA_TRANSFER: 'Nest_camera_transfer.h264',
150
+ };
151
+
152
+ export const LOG_LEVELS = {
153
+ INFO: 'info',
154
+ SUCCESS: 'success',
155
+ WARN: 'warn',
156
+ ERROR: 'error',
157
+ DEBUG: 'debug',
158
+ };
159
+
160
+ export const NESTLABS_MAC_PREFIX = '18B430';
package/dist/devices.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Device support loader
2
2
  // Part of homebridge-nest-accfactory
3
3
  //
4
- // Code version 2025.06.12
4
+ // Code version 2025.07.25
5
5
  // Mark Hulskamp
6
6
  'use strict';
7
7
 
@@ -14,62 +14,50 @@ import url from 'node:url';
14
14
  import HomeKitDevice from './HomeKitDevice.js';
15
15
 
16
16
  // Define constants
17
- const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
18
- const DeviceType = Object.freeze({
19
- THERMOSTAT: 'Thermostat',
20
- TEMPSENSOR: 'TemperatureSensor',
21
- SMOKESENSOR: 'Protect',
22
- CAMERA: 'Camera',
23
- DOORBELL: 'Doorbell',
24
- FLOODLIGHT: 'FloodlightCamera',
25
- WEATHER: 'Weather',
26
- HEATLINK: 'Heatlink',
27
- LOCK: 'Lock',
28
- ALARM: 'Alarm',
29
- });
17
+ import { __dirname, DEVICE_TYPE } from './consts.js';
30
18
 
31
19
  async function loadDeviceModules(log, pluginDir = '') {
32
20
  let baseDir = path.join(__dirname, pluginDir);
33
21
  let deviceMap = new Map();
34
22
  let files = (await fs.readdir(baseDir)).sort();
35
23
 
24
+ log?.debug?.('Using base module v%s', HomeKitDevice.VERSION);
25
+
36
26
  for (const file of files) {
37
27
  if (file.endsWith('.js') === false) {
38
28
  continue;
39
29
  }
40
30
 
41
31
  try {
42
- let module = await import(url.pathToFileURL(path.join(baseDir, file)).href);
43
- let exportsToCheck = Object.values(module);
44
-
45
- for (const exported of exportsToCheck) {
46
- if (typeof exported !== 'function') {
47
- continue;
32
+ let modulePath = url.pathToFileURL(path.join(baseDir, file)).href;
33
+ let module = await import(modulePath);
34
+ let chosenClass = undefined;
35
+
36
+ // First pass: find a valid subclass of HomeKitDevice
37
+ for (const exported of Object.values(module)) {
38
+ if (
39
+ typeof exported === 'function' &&
40
+ HomeKitDevice.prototype.isPrototypeOf(exported.prototype) &&
41
+ typeof exported.TYPE === 'string' &&
42
+ typeof exported.VERSION === 'string'
43
+ ) {
44
+ chosenClass = exported;
45
+ break; // first valid one wins (like your original)
48
46
  }
47
+ }
49
48
 
50
- let proto = Object.getPrototypeOf(exported);
51
- while (proto !== undefined && proto.name !== '') {
52
- if (proto === HomeKitDevice) {
53
- if (
54
- typeof exported.TYPE !== 'string' ||
55
- exported.TYPE === '' ||
56
- typeof exported.VERSION !== 'string' ||
57
- exported.VERSION === ''
58
- ) {
59
- log?.warn?.('Skipping device module %s (missing TYPE or VERSION)', file);
60
- break;
61
- }
62
-
63
- if (deviceMap.has(exported.TYPE) === false) {
64
- deviceMap.set(exported.TYPE, exported);
65
- log?.info?.('Loaded device module "%s" (v%s)', exported.TYPE, exported.VERSION);
66
- }
67
-
68
- break;
69
- }
49
+ if (chosenClass && deviceMap.has(chosenClass.TYPE) === false) {
50
+ let entry = { class: chosenClass };
70
51
 
71
- proto = Object.getPrototypeOf(proto);
52
+ // Add additional named functions (like processRawData)
53
+ for (const [key, value] of Object.entries(module)) {
54
+ if (typeof value === 'function' && value !== chosenClass) {
55
+ entry[key] = value;
56
+ }
72
57
  }
58
+
59
+ deviceMap.set(chosenClass.TYPE, entry);
60
+ log?.info?.('Loaded %s module v%s', chosenClass.TYPE, chosenClass.VERSION);
73
61
  }
74
62
  } catch (error) {
75
63
  log?.warn?.('Failed to load device support file "%s": %s', file, error.message);
@@ -82,27 +70,31 @@ async function loadDeviceModules(log, pluginDir = '') {
82
70
  function getDeviceHKCategory(type) {
83
71
  let category = 1; // Categories.OTHER
84
72
 
85
- if (type === DeviceType.LOCK) {
73
+ if (type === DEVICE_TYPE.LOCK) {
86
74
  category = 6; // Categories.DOOR_LOCK
87
75
  }
88
76
 
89
- if (type === DeviceType.THERMOSTAT) {
77
+ if (type === DEVICE_TYPE.HEATLINK) {
78
+ category = 8; // Categories.SWITCH
79
+ }
80
+
81
+ if (type === DEVICE_TYPE.THERMOSTAT) {
90
82
  category = 9; // Categories.THERMOSTAT
91
83
  }
92
84
 
93
- if (type === DeviceType.TEMPSENSOR || type === DeviceType.HEATLINK || type === DeviceType.SMOKESENSOR || type === DeviceType.WEATHER) {
85
+ if (type === DEVICE_TYPE.TEMPSENSOR || type === DEVICE_TYPE.PROTECT || type === DEVICE_TYPE.WEATHER) {
94
86
  category = 10; // Categories.SENSOR
95
87
  }
96
88
 
97
- if (type === DeviceType.ALARM) {
89
+ if (type === DEVICE_TYPE.ALARM) {
98
90
  category = 11; // Categories.SECURITY_SYSTEM
99
91
  }
100
92
 
101
- if (type === DeviceType.CAMERA || type === DeviceType.FLOODLIGHT) {
93
+ if (type === DEVICE_TYPE.CAMERA || type === DEVICE_TYPE.FLOODLIGHT) {
102
94
  category = 17; // Categories.IP_CAMERA
103
95
  }
104
96
 
105
- if (type === DeviceType.DOORBELL) {
97
+ if (type === DEVICE_TYPE.DOORBELL) {
106
98
  category = 18; // Categories.VIDEO_DOORBELL
107
99
  }
108
100
 
@@ -110,4 +102,4 @@ function getDeviceHKCategory(type) {
110
102
  }
111
103
 
112
104
  // Define exports
113
- export { DeviceType, loadDeviceModules, getDeviceHKCategory };
105
+ export { loadDeviceModules, getDeviceHKCategory };