homebridge-eufy-security
Version:
Control Eufy Security from homebridge.
635 lines (632 loc) • 31.9 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EufySecurityPlatform = void 0;
const process_1 = require("process");
const semver_1 = require("semver");
const settings_1 = require("./settings");
const config_1 = require("./config");
const StationAccessory_1 = require("./accessories/StationAccessory");
const EntrySensorAccessory_1 = require("./accessories/EntrySensorAccessory");
const MotionSensorAccessory_1 = require("./accessories/MotionSensorAccessory");
const CameraAccessory_1 = require("./accessories/CameraAccessory");
const LockAccessory_1 = require("./accessories/LockAccessory");
const AutoSyncStationAccessory_1 = require("./accessories/AutoSyncStationAccessory");
const eufy_security_client_1 = require("eufy-security-client");
const rotating_file_stream_1 = require("rotating-file-stream");
const fs_1 = __importDefault(require("fs"));
const node_os_1 = __importDefault(require("node:os"));
const node_process_1 = require("node:process");
const node_fs_1 = require("node:fs");
const utils_1 = require("./utils/utils");
const version_1 = require("./version");
class EufySecurityPlatform {
config;
api;
eufyClient = {};
// this is used to track restored cached accessories
accessories = [];
already_shutdown = false;
eufyPath;
activeAccessoryIds = [];
cleanCachedAccessoriesTimeout;
_hostSystem = '';
nodeJScompatible = false;
constructor(hblog, config, api) {
this.config = config;
this.api = api;
this.eufyPath = this.api.user.storagePath() + '/eufysecurity';
if (!fs_1.default.existsSync(this.eufyPath)) {
fs_1.default.mkdirSync(this.eufyPath);
}
// Identify what we're running on so we can take advantage of hardware-specific features.
this.probeHwOs();
this.initConfig(config);
this.configureLogger();
this.initSetup();
}
/**
* Initializes the configuration object with default values where properties are not provided.
* If a property is provided in the config object, it overrides the default value.
* Additionally, certain numeric properties are parsed to ensure they are of the correct type.
* @param config - Partial configuration object with user-provided values.
*/
initConfig(config) {
// Assigns the provided config object to this.config, casting it to the EufySecurityPlatformConfig type.
this.config = config;
// Iterates over each key in the DEFAULT_CONFIG_VALUES object.
Object.keys(config_1.DEFAULT_CONFIG_VALUES).forEach(key => {
// Checks if the corresponding property in the config object is undefined or null.
// If it is, assigns the default value from DEFAULT_CONFIG_VALUES to it.
this.config[key] ??= config_1.DEFAULT_CONFIG_VALUES[key];
});
// List of properties that need to be parsed as numeric values
const numericProperties = [
'CameraMaxLivestreamDuration',
'pollingIntervalMinutes',
'hkHome',
'hkAway',
'hkNight',
'hkOff'
];
// Iterate over each property in the config object
Object.entries(this.config).forEach(([key, value]) => {
// Check if the property is one of the numeric properties
if (numericProperties.includes(key)) {
// Parse the value to ensure it is of the correct type (number)
this.config[key] = (typeof value === 'string') ? parseInt(value) : value;
}
});
}
/**
* Configures the logging mechanism for the plugin.
*/
configureLogger() {
// Define options for logging
const logOptions = {
name: '[EufySecurity]', // Name prefix for log messages
prettyLogTemplate: '[{{mm}}/{{dd}}/{{yyyy}}, {{hh}}:{{MM}}:{{ss}}]\t{{name}}\t{{logLevelName}}\t', // Template for pretty log output
prettyErrorTemplate: '\n{{errorName}} {{errorMessage}}\nerror stack:\n{{errorStack}}', // Template for pretty error output
prettyErrorStackTemplate: ' • {{fileName}}\t{{method}}\n\t{{fileNameWithLine}}', // Template for error stack trace
prettyErrorParentNamesSeparator: '', // Separator for parent names in error messages
prettyErrorLoggerNameDelimiter: '\t', // Delimiter for logger name in error messages
stylePrettyLogs: true, // Enable styling for logs
minLevel: 3, // Minimum log level to display (3 corresponds to INFO)
prettyLogTimeZone: 'local', // Time zone for log timestamps
prettyLogStyles: {
logLevelName: {
'*': ['bold', 'black', 'bgWhiteBright', 'dim'], // Default style
SILLY: ['bold', 'white'], // Style for SILLY level
TRACE: ['bold', 'whiteBright'], // Style for TRACE level
DEBUG: ['bold', 'green'], // Style for DEBUG level
INFO: ['bold', 'blue'], // Style for INFO level
WARN: ['bold', 'yellow'], // Style for WARN level
ERROR: ['bold', 'red'], // Style for ERROR level
FATAL: ['bold', 'redBright'], // Style for FATAL level
},
dateIsoStr: 'gray', // Style for ISO date strings
filePathWithLine: 'white', // Style for file paths with line numbers
name: 'green', // Style for logger names
nameWithDelimiterPrefix: ['white', 'bold'], // Style for logger names with delimiter prefix
nameWithDelimiterSuffix: ['white', 'bold'], // Style for logger names with delimiter suffix
errorName: ['bold', 'bgRedBright', 'whiteBright'], // Style for error names
fileName: ['yellow'], // Style for file names
},
maskValuesOfKeys: [
'username',
'password',
'token',
'clientPrivateKey',
'private_key',
'login_hash',
'serverPublicKey',
'cloud_token',
'refreshToken',
'p2p_conn',
'app_conn',
'address',
'latitude',
'longitude',
'serialnumber',
'serialNumber',
'stationSerialNumber',
'data',
'ignoreStations',
'ignoreDevices',
'pincode',
],
};
// Modify log options if detailed logging is enabled
if (this.config.enableDetailedLogging) {
logOptions.name = `[EufySecurity-${version_1.LIB_VERSION}]`; // Modify logger name with plugin version
logOptions.prettyLogTemplate = '[{{mm}}/{{dd}}/{{yyyy}} {{hh}}:{{MM}}:{{ss}}]\t{{name}}\t{{logLevelName}}\t[{{fileNameWithLine}}]\t'; // Modify log template
logOptions.minLevel = 2; // Adjust minimum log level
}
// Initialize the global logger with the configured options
(0, utils_1.init_log)(logOptions);
// Configures log streams for various log files
this.configureLogStreams();
}
/**
* Configures log streams for various log files if log file storage is not omitted.
*/
configureLogStreams() {
// Log a message if log file storage will be omitted
if (this.config.omitLogFiles) {
utils_1.log.info('log file storage will be omitted.');
return;
}
// Log streams configuration
const logStreams = [
{ name: 'eufy-security.log', logger: utils_1.log },
{ name: 'ffmpeg.log', logger: utils_1.ffmpegLogger },
{ name: 'eufy-lib.log', logger: utils_1.tsLogger },
];
// Iterate over log streams
for (const { name, logger } of logStreams) {
// Create a log stream with specific configurations
const logStream = (0, rotating_file_stream_1.createStream)(name, {
path: this.eufyPath, // Log file path
interval: '1d', // Log rotation interval (1 day)
rotate: 3, // Number of rotated log files to keep
maxSize: '200M', // Maximum log file size
});
// Attach a transport function to write log messages to the log stream
logger.attachTransport((logObj) => {
const meta = logObj['_meta'];
const name = meta.name;
const level = meta.logLevelName;
const date = meta.date.toISOString();
const fileNameWithLine = meta.path?.fileNameWithLine || 'UNKNOWN_FILE';
// Initialize the message
let message = '';
// Loop through logObj from index 0 to 5 and append values to the message
for (let i = 0; i <= 5; i++) {
if (logObj[i]) {
message += ' ' + typeof logObj[i] === 'string' ? logObj[i] : JSON.stringify(logObj[i]);
}
}
// Write formatted log message to the log stream
logStream.write(date + '\t' + name + '\t' + level + '\t' + fileNameWithLine + '\t' + message + '\n');
});
}
}
// This function is responsible for identifying the hardware and operating system environment the application is running on.
probeHwOs() {
// Start off with a generic identifier.
this._hostSystem = 'generic';
// Take a look at the platform we're on for an initial hint of what we are.
switch (node_process_1.platform) {
// The beloved macOS.
case 'darwin':
// For macOS, we check the CPU model to determine if it's an Apple CPU or an Intel CPU.
this._hostSystem = 'macOS.' + (node_os_1.default.cpus()[0].model.includes('Apple') ? 'Apple' : 'Intel');
break;
// The indomitable Linux.
case 'linux':
// Let's further see if we're a small, but scrappy, Raspberry Pi.
try {
// As of the 4.9 kernel, Raspberry Pi prefers to be identified using this method and has deprecated cpuinfo.
const systemId = (0, node_fs_1.readFileSync)('/sys/firmware/devicetree/base/model', { encoding: 'utf8' });
// Check if it's a Raspberry Pi 4.
if (/Raspberry Pi (Compute Module )?4/.test(systemId)) {
// If it's a Pi 4, we identify the system as running Raspbian.
this._hostSystem = 'raspbian';
}
}
catch {
// Errors encountered while attempting to identify the system are ignored.
// We prioritize getting system information through hints rather than comprehensive detection.
}
break;
default:
// We aren't trying to solve for every system type.
// If the platform doesn't match macOS or Linux, we keep the generic identifier.
break;
}
}
// Utility to return the hardware environment we're on.
get hostSystem() {
return this._hostSystem;
}
initSetup() {
utils_1.log.debug('warning: planned changes, see https://github.com/homebridge-eufy-security/plugin/issues/1');
utils_1.log.debug('plugin data store:', this.eufyPath);
utils_1.log.debug('OS is', this.hostSystem);
utils_1.log.debug('Using bropats @homebridge-eufy-security/eufy-security-client library in version ', eufy_security_client_1.libVersion);
if (!this.checkNodeJSVersionCompatibility()) {
utils_1.log.error(`
***************************
****** ERROR MESSAGE ******
***************************
Error: Your current Node.js version (${process_1.version}) is incompatible with the RSA_PKCS1_PADDING used by the plugin.
If you run the plugin with an incompatible version of Node.js, livestream functionality will be disrupted.
You can override this warning by configuring a special parameter in the global configuration.
To resolve this issue, please consider downgrading to a compatible version using a command similar to: sudo hb-service update-node 20.11.0.
Versions known to cause compatibility issues with this plugin include those within the following ranges:
- Node.js 18.x.x (starting from 18.19.1 up to the next major release)
- Node.js 20.x.x (starting from 20.11.1 up to the next major release)
- Node.js 21.x.x (starting from 21.6.2 up to the next major release)
For instructions on how to upgrade or downgrade Node.js, please refer to: https://github.com/homebridge/homebridge/wiki/How-To-Update-Node.js
For more information on the security vulnerability affecting Node.js, visit:
https://nodejs.org/en/blog/vulnerability/february-2024-security-releases#nodejs-is-vulnerable-to-the-marvin-attack-timing-variant-of-the-bleichenbacher-attack-against-pkcs1-v15-padding-cve-2023-46809---medium
***************************
`);
}
// Log the final configuration object for debugging purposes
utils_1.log.debug('The config is:', this.config);
const eufyConfig = {
username: this.config.username,
password: this.config.password,
country: this.config.country,
trustedDeviceName: this.config.deviceName,
language: 'en',
persistentDir: this.eufyPath,
p2pConnectionSetup: 0,
pollingIntervalMinutes: this.config.pollingIntervalMinutes,
eventDurationSeconds: 10,
logging: {
level: (this.config.enableDetailedLogging) ? eufy_security_client_1.LogLevel.Debug : eufy_security_client_1.LogLevel.Info,
},
};
this.api.on("didFinishLaunching" /* APIEvent.DID_FINISH_LAUNCHING */, async () => {
await this.pluginSetup(eufyConfig);
});
this.api.on("shutdown" /* APIEvent.SHUTDOWN */, async () => {
await this.pluginShutdown();
});
utils_1.log.debug('Finished booting!');
}
async pluginSetup(eufyConfig) {
try {
this.eufyClient = await eufy_security_client_1.EufySecurity.initialize(eufyConfig, (this.config.enableDetailedLogging) ? utils_1.tsLogger : undefined);
this.eufyClient.on('station added', this.stationAdded.bind(this));
this.eufyClient.on('station removed', this.stationRemoved.bind(this));
this.eufyClient.on('device added', this.deviceAdded.bind(this));
this.eufyClient.on('device removed', this.deviceRemoved.bind(this));
this.eufyClient.on('push connect', () => {
utils_1.log.debug('Push Connected!');
});
this.eufyClient.on('push close', () => {
utils_1.log.debug('Push Closed!');
});
this.eufyClient.on('connect', () => {
utils_1.log.debug('Connected!');
});
this.eufyClient.on('close', () => {
utils_1.log.debug('Closed!');
});
this.eufyClient.on('connection error', async (error) => {
utils_1.log.debug(`Error: ${error}`);
await this.pluginShutdown();
});
this.eufyClient.once('captcha request', async () => {
utils_1.log.error(`
***************************
***** WARNING MESSAGE *****
***************************
Important Notice: CAPTCHA Required
Your account seems to have triggered a security measure that requires CAPTCHA verification for the next 24 hours...
Please abstain from any activities until this period elapses...
Should your issue persist beyond this timeframe, you may need to consider setting up a new account.
For more detailed instructions, please consult:
https://github.com/homebridge-eufy-security/plugin/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
***************************
`);
await this.pluginShutdown();
});
this.eufyClient.on('tfa request', async () => {
utils_1.log.error(`
***************************
***** WARNING MESSAGE *****
***************************
Attention: Two-Factor Authentication (2FA) Requested
It appears that your account is currently under a temporary 24-hour flag for security reasons...
Kindly refrain from making any further attempts during this period...
If your concern remains unresolved after 24 hours, you may need to consider creating a new account.
For additional information, refer to:
https://github.com/homebridge-eufy-security/plugin/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
***************************
`);
await this.pluginShutdown();
});
}
catch (e) {
utils_1.log.error(`Error while setup : ${e}`);
utils_1.log.error('Not connected can\'t continue!');
return;
}
try {
await this.eufyClient.connect();
utils_1.log.debug('EufyClient connected ' + this.eufyClient.isConnected());
}
catch (e) {
utils_1.log.error(`Error authenticating Eufy: ${e}`);
}
if (!this.eufyClient.isConnected()) {
utils_1.log.error('Not connected can\'t continue!');
return;
}
// give the connection 45 seconds to discover all devices
// clean old accessories after that time
this.cleanCachedAccessoriesTimeout = setTimeout(() => {
this.cleanCachedAccessories();
}, 45 * 1000);
if (this.config.CameraMaxLivestreamDuration > 86400) {
this.config.CameraMaxLivestreamDuration = 86400;
utils_1.log.warn('Your maximum livestream duration value is too large. Since this can cause problems it was reset to 86400 seconds (1 day maximum).');
}
this.eufyClient.setCameraMaxLivestreamDuration(this.config.CameraMaxLivestreamDuration);
utils_1.log.debug(`CameraMaxLivestreamDuration: ${this.eufyClient.getCameraMaxLivestreamDuration()}`);
}
/**
* Generates a UUID based on the given identifier and station flag.
* @param identifier The unique identifier.
* @param isStation Flag indicating whether the identifier belongs to a station.
* @returns The generated UUID.
*/
generateUUID(identifier, isStation) {
// Add prefix 's_' if it's a station identifier, otherwise, no prefix.
const prefix = isStation ? 's1_' : 'd1_';
// Generate UUID based on the prefix + identifier.
return utils_1.HAP.uuid.generate(prefix + identifier);
}
async delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Defines an accessory for a device or station.
*
* @param deviceContainer The container holding information about the device or station.
* @param isStation A boolean indicating whether the container represents a station.
* @returns A tuple containing the created or cached accessory and a boolean indicating whether the accessory was cached.
*/
defineAccessory(deviceContainer, isStation) {
// Generate UUID for the accessory based on device's unique identifier and whether it's a station
const uuid = this.generateUUID(deviceContainer.deviceIdentifier.uniqueId, isStation);
// Check if the accessory is already cached
const cachedAccessory = this.accessories.find((accessory) => accessory.UUID === uuid);
// If the accessory is cached, remove it from the accessories array
if (cachedAccessory) {
this.accessories.splice(this.accessories.indexOf(cachedAccessory), 1);
}
// Determine if the device is a camera
const isCamera = (deviceContainer.eufyDevice instanceof eufy_security_client_1.Device)
? deviceContainer.eufyDevice.isCamera()
: false;
// Create a new accessory if not cached, otherwise use the cached one
const accessory = cachedAccessory
|| new this.api.platformAccessory(deviceContainer.deviceIdentifier.displayName, uuid, isCamera ? 17 /* HAP.Categories.CAMERA */ : 11 /* HAP.Categories.SECURITY_SYSTEM */);
// Store device information in accessory context
accessory.context['device'] = deviceContainer.deviceIdentifier;
accessory.displayName = deviceContainer.deviceIdentifier.displayName;
return [accessory, !!cachedAccessory];
}
/**
* Adds or updates an accessory for a device or station.
*
* @param deviceContainer The container holding information about the device or station.
* @param isStation A boolean indicating whether the container represents a station.
*/
async addOrUpdateAccessory(deviceContainer, isStation) {
try {
// Define the accessory and check if it already exists
const [accessory, isExist] = this.defineAccessory(deviceContainer, isStation);
// Register the accessory based on whether it's a station or device
try {
if (isStation) {
this.register_station(accessory, deviceContainer);
}
else {
this.register_device(accessory, deviceContainer);
}
}
catch (error) {
// Remove station or device accessories created prior to plugin upgrade,
// which may have been subject to removal due to newly introduced logic.
if (isExist) {
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
}
throw error;
}
// Add the accessory's UUID to activeAccessoryIds if it's not already present
if (this.activeAccessoryIds.indexOf(accessory.UUID) === -1) {
this.activeAccessoryIds.push(accessory.UUID);
}
// Update or register the accessory with the platform
if (isExist) {
this.api.updatePlatformAccessories([accessory]);
utils_1.log.info(`Updating existing accessory: ${accessory.displayName}`);
}
else {
this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
utils_1.log.info(`Registering new accessory: ${accessory.displayName}`);
}
}
catch (error) {
// Log any errors that occur during accessory addition or update
utils_1.log.error(`Error in ${isStation ? 'stationAdded' : 'deviceAdded'}:`, error);
}
}
async stationAdded(station) {
try {
if (this.config.ignoreStations.includes(station.getSerial())) {
utils_1.log.debug(`${station.getName()}: Station ignored`);
return;
}
const rawStation = station.getRawStation();
if (rawStation.member.member_type !== eufy_security_client_1.UserType.ADMIN) {
await this.pluginShutdown();
utils_1.log.error(`
#########################
######### ERROR #########
#########################
You're not using a guest admin account with this plugin! You must use a guest admin account!
Please look here for more details:
https://github.com/homebridge-eufy-security/plugin/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
#########################
`);
return;
}
const deviceContainer = {
deviceIdentifier: {
uniqueId: station.getSerial(),
displayName: 'STATION ' + station.getName().replace(/[^a-zA-Z0-9]/g, ''),
type: station.getDeviceType(),
},
eufyDevice: station,
};
await this.delay(settings_1.STATION_INIT_DELAY);
utils_1.log.debug(`${deviceContainer.deviceIdentifier.displayName} pre-caching complete`);
this.addOrUpdateAccessory(deviceContainer, true);
}
catch (error) {
utils_1.log.error(`Error in stationAdded:, ${error}`);
}
}
async deviceAdded(device) {
try {
if (this.config.ignoreDevices.includes(device.getSerial())) {
utils_1.log.debug(`${device.getName()}: Device ignored`);
return;
}
const deviceContainer = {
deviceIdentifier: {
uniqueId: device.getSerial(),
displayName: 'DEVICE ' + device.getName().replace(/[^a-zA-Z0-9]/g, ''),
type: device.getDeviceType(),
},
eufyDevice: device,
};
await this.delay(settings_1.DEVICE_INIT_DELAY);
utils_1.log.debug(`${deviceContainer.deviceIdentifier.displayName} pre-caching complete`);
this.addOrUpdateAccessory(deviceContainer, false);
}
catch (error) {
utils_1.log.error(`Error in deviceAdded: ${error}`);
}
}
async stationRemoved(station) {
const serial = station.getSerial();
utils_1.log.debug(`A device has been removed: ${serial}`);
}
async deviceRemoved(device) {
const serial = device.getSerial();
utils_1.log.debug(`A device has been removed: ${serial}`);
}
async pluginShutdown() {
// Ensure a single shutdown to prevent corruption of the persistent file.
// This also enables captcha through the GUI and prevents repeated captcha or 2FA prompts upon plugin restart.
if (this.already_shutdown) {
return;
}
this.already_shutdown = true;
if (this.cleanCachedAccessoriesTimeout) {
clearTimeout(this.cleanCachedAccessoriesTimeout);
}
try {
if (this.eufyClient.isConnected()) {
this.eufyClient.close();
}
utils_1.log.info('Finished shutdown!');
}
catch (e) {
utils_1.log.error(`Error while shutdown: ${e}`);
}
}
/**
* This function is invoked when homebridge restores cached accessories from disk at startup.
* It should be used to setup event handlers for characteristics and update respective values.
*/
configureAccessory(accessory) {
utils_1.log.debug(`Loading accessory from cache: ${accessory.displayName}`);
// add the restored accessory to the accessories cache so we can track if it has already been registered
this.accessories.push(accessory);
}
cleanCachedAccessories() {
if (this.config.cleanCache) {
utils_1.log.debug('Looking for old cached accessories that seem to be outdated...');
let num = 0;
const staleAccessories = this.accessories.filter((item) => {
return this.activeAccessoryIds.indexOf(item.UUID) === -1;
});
staleAccessories.forEach((staleAccessory) => {
utils_1.log.info(`Removing cached accessory ${staleAccessory.UUID} ${staleAccessory.displayName}`);
num++;
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [staleAccessory]);
});
if (num > 0) {
utils_1.log.info('Removed ' + num + ' cached accessories');
}
else {
utils_1.log.info('No outdated cached accessories found.');
}
}
}
register_station(accessory, container) {
utils_1.log.debug(accessory.displayName + ' UUID:' + accessory.UUID);
const type = container.deviceIdentifier.type;
const station = container.eufyDevice;
if (type !== eufy_security_client_1.DeviceType.STATION) {
// Standalone Lock or Doorbell doesn't have Security Control
if (eufy_security_client_1.Device.isDoorbell(type) || eufy_security_client_1.Device.isLock(type)) {
throw new Error(`looks station but it's not could imply some errors! Type: ${type}. You can ignore this message.`);
}
}
if (this.config.autoSyncStation) {
new AutoSyncStationAccessory_1.AutoSyncStationAccessory(this, accessory, station);
}
else {
new StationAccessory_1.StationAccessory(this, accessory, station);
}
}
register_device(accessory, container) {
utils_1.log.debug(accessory.displayName + ' UUID:' + accessory.UUID);
const device = container.eufyDevice;
const type = container.deviceIdentifier.type;
if (eufy_security_client_1.Device.isMotionSensor(type)) {
utils_1.log.debug(accessory.displayName + ' isMotionSensor!');
new MotionSensorAccessory_1.MotionSensorAccessory(this, accessory, device);
}
if (eufy_security_client_1.Device.isEntrySensor(type)) {
utils_1.log.debug(accessory.displayName + ' isEntrySensor!');
new EntrySensorAccessory_1.EntrySensorAccessory(this, accessory, device);
}
if (eufy_security_client_1.Device.isLock(type)) {
utils_1.log.debug(accessory.displayName + ' isLock!');
new LockAccessory_1.LockAccessory(this, accessory, device);
}
if (eufy_security_client_1.Device.isCamera(type)) {
utils_1.log.debug(accessory.displayName + ' isCamera!');
new CameraAccessory_1.CameraAccessory(this, accessory, device);
}
if (eufy_security_client_1.Device.isKeyPad(type)) {
utils_1.log.debug(accessory.displayName + ' isKeypad!');
throw new Error('The keypad needs to be taken out because it serves no purpose. You can ignore this message.');
}
}
/**
* Checks compatibility of the current Node.js version with Livestream functionality.
* @returns {boolean} Returns true if the Node.js version is compatible, false otherwise.
*/
checkNodeJSVersionCompatibility() {
// Obtain the cleaned version of Node.js
const nodeVersion = (0, semver_1.clean)(process_1.version);
// If version cannot be determined, assume compatibility
if (!nodeVersion) {
return true;
}
// Log the Node.js version for debugging purposes
utils_1.log.debug('Node version is', nodeVersion);
// Define versions known to break compatibility with RSA_PKCS1_PADDING
this.nodeJScompatible = !(0, semver_1.satisfies)(nodeVersion, '>=18.19.1 <19.x || >=20.11.1 <21.x || >=21.6.2 <22');
// Log the Node.js version for debugging purposes
utils_1.log.debug('Node version is compatible', this.nodeJScompatible);
// Return true if the Node.js version is compatible, false otherwise
return this.nodeJScompatible;
}
}
exports.EufySecurityPlatform = EufySecurityPlatform;
//# sourceMappingURL=platform.js.map
;