diff --git a/CHANGELOG.md b/CHANGELOG.md index e69c81c63..13306a9e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,16 +10,17 @@ All notable changes to homebridge-config-ui-x will be documented in this file. - This mimics the behaviour of updating Homebridge itself - The option of 'Update Anyway' will still appear for other plugins when running an unsupported version of Node.js - GLIBC Version is now cached +- When uninstalling a plugin, it will also be removed from the disabled plugin list if it was previously disabled ### UI Changes #### General - Fixed icon widths throughout UI -- Updated menu (thanks [mkz212](https://github.com/mkz212)!) +- Updated menu (thanks [@mkz212](https://github.com/mkz212)!) - Added links to main dropdown menu: 'Logs' and 'Restart Homebridge' + reordered list - Changed the menu layout order - - Made the dropdown darker in dark mode ([1660](https://github.com/homebridge/homebridge-config-ui-x/pull/1660)) + - Made the dropdown darker in dark mode ([#1660](https://github.com/homebridge/homebridge-config-ui-x/pull/1660)) - Username is now displayed by logout Menu option #### Status Page @@ -31,7 +32,7 @@ All notable changes to homebridge-config-ui-x will be documented in this file. - Added alert icon and modal when running an older OS that cannot update to Node.js 18/20 - Added alert icon and modal when not running in service mode - **Homebridge Widget** - - Added ability to scroll Homebridge widget ([1651](https://github.com/homebridge/homebridge-config-ui-x/pull/1651)) + - Added ability to scroll Homebridge widget ([#1651](https://github.com/homebridge/homebridge-config-ui-x/pull/1651)) - 'Update available' icons are now up-arrows to match plugins page #### Plugins Page @@ -82,19 +83,19 @@ All notable changes to homebridge-config-ui-x will be documented in this file. - `plugins.manage.message_thanks_for_updating_restart` - `plugins.donate.message_learn_more` - `status.message_code_scan_instructions` -- **i18n:** Update pl.json ([1633](https://github.com/homebridge/homebridge-config-ui-x/pull/1633)) -- **i18n:** Update fr.json ([1629](https://github.com/homebridge/homebridge-config-ui-x/pull/1629)) -- **i18n:** Update es.json (Improves Spanish localization) ([1632](https://github.com/homebridge/homebridge-config-ui-x/pull/1632)) +- **i18n:** Update pl.json ([#1633](https://github.com/homebridge/homebridge-config-ui-x/pull/1633)) +- **i18n:** Update fr.json ([#1629](https://github.com/homebridge/homebridge-config-ui-x/pull/1629)) +- **i18n:** Update es.json (Improves Spanish localization) ([#1632](https://github.com/homebridge/homebridge-config-ui-x/pull/1632)) ### Other Changes -- Updated dependencies, including `@homebridge/node-pty-prebuilt-multiarch` to `0.11.10` +- Updated dependencies, including `@homebridge/node-pty-prebuilt-multiarch` to `0.11.10` (thanks [@NorthernMan54](https://github.com/NorthernMan54)!) ## 4.52.1 (2023-11-04) ### Other Changes -- **i18n:** Update de.json ([1627](https://github.com/homebridge/homebridge-config-ui-x/pull/1627)) +- **i18n:** Update de.json ([#1627](https://github.com/homebridge/homebridge-config-ui-x/pull/1627)) ### Bug Fixes @@ -126,10 +127,10 @@ All notable changes to homebridge-config-ui-x will be documented in this file. - `hb-service` will now refuse to install `node` versions less than `16.18.0` - Plugin node warning screen will appear when running a version of `node` lower than `18.15.0` (bumped up from `14.15.0`) - UI node warning screen will appear when running a version of `node` lower than `18.15.0` (bumped up from `10.17.0`) -- Obtain correct beta branch name for Homebridge (and UI) (https://github.com/homebridge/homebridge-config-ui-x/commit/212b3eb1d5cb3ccda01fe2c3be711b80af4d5bf6) -- Rename pre-release npm tag from `test` to `beta` for consistency with other Homebridge repositories (https://github.com/homebridge/homebridge-config-ui-x/commit/86ea73ffd0b35f372a164ee42e17a996905cffb6) +- Obtain correct beta branch name for Homebridge (and UI) ([#212b3eb](https://github.com/homebridge/homebridge-config-ui-x/commit/212b3eb1d5cb3ccda01fe2c3be711b80af4d5bf6)) +- Rename pre-release npm tag from `test` to `beta` for consistency with other Homebridge repositories ([#86ea73f](https://github.com/homebridge/homebridge-config-ui-x/commit/86ea73ffd0b35f372a164ee42e17a996905cffb6)) - Updated dependencies -- Update @homebridge/node-pty-prebuilt-multiarch to version v0.11.8 ( Updated build process and back-level support for Synology DSM devices ) +- Update `@homebridge/node-pty-prebuilt-multiarch` to version v0.11.8 (updated build process and back-level support for Synology DSM devices) ## 4.51.2 (2023-10-27) diff --git a/src/index.ts b/src/index.ts index 4dc2cefcf..316b63acd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,12 +7,12 @@ import * as path from 'path'; import { program } from 'commander'; import * as semver from 'semver'; -let homebridge; +let homebridge: any; class HomebridgeConfigUi { - log; + log: any; - constructor(log, config) { + constructor(log: any, config: any) { this.log = log; process.env.UIX_CONFIG_PATH = homebridge.user.configPath(); diff --git a/src/modules/accessories/accessories.service.ts b/src/modules/accessories/accessories.service.ts index 77c735ba0..8214aa198 100644 --- a/src/modules/accessories/accessories.service.ts +++ b/src/modules/accessories/accessories.service.ts @@ -28,13 +28,13 @@ export class AccessoriesService { * Connects the client to the homebridge service * @param client */ - public async connect(client) { + public async connect(client: any) { if (!this.configService.homebridgeInsecureMode) { this.logger.error('Homebridge must be running in insecure mode to control accessories'); return; } - let services; + let services: any[]; const loadAllAccessories = async (refresh: boolean) => { if (!refresh) { @@ -55,7 +55,7 @@ export class AccessoriesService { await loadAllAccessories(false); // handling incoming requests - const requestHandler = async (msg?) => { + const requestHandler = async (msg?: any) => { if (msg.set) { const service: ServiceType = services.find(x => x.uniqueId === msg.set.uniqueId); if (service) { @@ -76,12 +76,12 @@ export class AccessoriesService { const monitor = await this.hapClient.monitorCharacteristics(); - const updateHandler = (data) => { + const updateHandler = (data: any) => { client.emit('accessories-data', data); }; monitor.on('service-update', updateHandler); - const instanceUpdateHandler = async (data) => { + const instanceUpdateHandler = async (data: any) => { client.emit('accessories-reload-required', services); }; this.hapClient.on('instance-discovered', instanceUpdateHandler); @@ -113,13 +113,12 @@ export class AccessoriesService { * Refresh the characteristics from Homebridge * @param services */ - private refreshCharacteristics(services) { + private refreshCharacteristics(services: any[]) { services.forEach(service => service.refreshCharacteristics()); } /** * Load all the accessories from Homebridge - * @param refreshServices */ public async loadAccessories(): Promise { if (!this.configService.homebridgeInsecureMode) { @@ -141,7 +140,7 @@ export class AccessoriesService { } /** - * Get a single accessory and refresh it's characteristics + * Get a single accessory and refresh its characteristics * @param uniqueId */ public async getAccessory(uniqueId: string) { @@ -163,7 +162,7 @@ export class AccessoriesService { /** * Set a characteristics value * @param uniqueId - * @param iid + * @param characteristicType * @param value */ public async setAccessoryCharacteristic(uniqueId: string, characteristicType: string, value: number | boolean | string) { @@ -212,7 +211,7 @@ export class AccessoriesService { value = false; } } else if (typeof value === 'number') { - value = value === 1 ? true : false; + value = value === 1; } if (typeof value !== 'boolean') { @@ -238,7 +237,7 @@ export class AccessoriesService { if (username in accessoryLayout) { return accessoryLayout[username]; } else { - throw new Error('User not in Acccessory Layout'); + throw new Error('User not in Accessory Layout'); } } catch (e) { return [ @@ -256,7 +255,7 @@ export class AccessoriesService { * @param layout */ public async saveAccessoryLayout(user: string, layout: Record) { - let accessoryLayout; + let accessoryLayout: any; try { accessoryLayout = await fs.readJson(this.configService.accessoryLayoutPath); diff --git a/src/modules/backup/backup.service.ts b/src/modules/backup/backup.service.ts index 8475cd52f..b0c3165ce 100644 --- a/src/modules/backup/backup.service.ts +++ b/src/modules/backup/backup.service.ts @@ -29,7 +29,7 @@ const pump = util.promisify(pipeline); @Injectable() export class BackupService { - private restoreDirectory; + private restoreDirectory: string; constructor( private readonly configService: ConfigService, @@ -62,7 +62,7 @@ export class BackupService { } /** - * Creates the .tar.gz instance backup of the curent Homebridge instance + * Creates the .tar.gz instance backup of the current Homebridge instance */ private async createBackup() { // prepare a temp working directory @@ -180,7 +180,7 @@ export class BackupService { } /** - * Runs the job to create a a scheduled backup + * Runs the job to create a scheduled backup */ async runScheduledBackupJob() { // ensure backup path exists @@ -594,9 +594,9 @@ export class BackupService { // restore accessories const sourceAccessoriesPath = path.resolve(this.restoreDirectory, 'etc', 'accessories'); - const targetAccessoriestPath = path.resolve(storagePath, 'accessories'); + const targetAccessoriesPath = path.resolve(storagePath, 'accessories'); if (await fs.pathExists(sourceAccessoriesPath)) { - await fs.copy(sourceAccessoriesPath, targetAccessoriestPath, { + await fs.copy(sourceAccessoriesPath, targetAccessoriesPath, { filter: (filePath) => { client.emit('stdout', `Restoring ${path.basename(filePath)}\r\n`); return true; @@ -639,11 +639,11 @@ export class BackupService { // clone elements from the source config that we care about const targetConfig: HomebridgeConfig = JSON.parse(JSON.stringify({ bridge: sourceConfig.bridge, - accessories: sourceConfig.accessories?.map((x) => { + accessories: sourceConfig.accessories?.map((x: any) => { delete x.plugin_map; return x; }) || [], - platforms: sourceConfig.platforms?.map((x) => { + platforms: sourceConfig.platforms?.map((x: any) => { if (x.platform === 'google-home') { x.platform = 'google-smarthome'; x.notice = 'Keep your token a secret!'; @@ -721,7 +721,7 @@ export class BackupService { // if running in standalone mode, need to find the pid of homebridge and kill it if (os.platform() === 'linux' && this.configService.ui.standalone) { try { - // try get pid by port + // try to get pid by port const getPidByPort = (port: number): number => { try { return parseInt(child_process.execSync( @@ -732,7 +732,7 @@ export class BackupService { } }; - // try get pid by name + // try to get pid by name const getPidByName = (): number => { try { return parseInt(child_process.execSync('pidof homebridge').toString('utf8').trim(), 10); @@ -769,7 +769,7 @@ export class BackupService { } /** - * Checks the bridge.bind options are valid for the current system when restoring. + * Checks the 'bridge.bind' options are valid for the current system when restoring. */ private checkBridgeBindConfig(restoredConfig: HomebridgeConfig) { if (restoredConfig.bridge.bind) { diff --git a/src/modules/config-editor/config-editor.service.ts b/src/modules/config-editor/config-editor.service.ts index 4da4e6641..7d7cde54d 100644 --- a/src/modules/config-editor/config-editor.service.ts +++ b/src/modules/config-editor/config-editor.service.ts @@ -286,7 +286,7 @@ export class ConfigEditorService { const idx = config.disabledPlugins.findIndex(x => x === pluginName); - // Check plugin is in thw list + // Check plugin is in the list if (idx > -1) { config.disabledPlugins.splice(idx, 1); await this.updateConfigFile(config); diff --git a/src/modules/log/log.service.ts b/src/modules/log/log.service.ts index 6394dfcce..93c9fb1eb 100644 --- a/src/modules/log/log.service.ts +++ b/src/modules/log/log.service.ts @@ -74,6 +74,7 @@ export class LogService { /** * Connect pty * @param client + * @param size */ private tailLog(client: EventEmitter, size: LogTermSize) { const command = [...this.command]; @@ -212,11 +213,11 @@ export class LogService { } // watch for lines and emit to client - const onLine = (line) => { + const onLine = (line: string) => { client.emit('stdout', line + '\n\r'); }; - const onError = (err) => { + const onError = (err: Error) => { client.emit('stdout', err.message + '\n\r'); }; diff --git a/src/modules/platform-tools/hb-service/hb-service.service.ts b/src/modules/platform-tools/hb-service/hb-service.service.ts index ae65900ee..2e7f97aca 100644 --- a/src/modules/platform-tools/hb-service/hb-service.service.ts +++ b/src/modules/platform-tools/hb-service/hb-service.service.ts @@ -4,6 +4,7 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import * as fs from 'fs-extra'; import { ConfigService } from '../../../core/config/config.service'; import { Logger } from '../../../core/logger/logger.service'; +import { HbServiceStartupSettings } from './hb-service.dto'; @Injectable() export class HbServiceService { @@ -12,7 +13,8 @@ export class HbServiceService { constructor( private readonly configService: ConfigService, private readonly logger: Logger - ) { } + ) { + } /** * Returns the Homebridge startup settings @@ -43,7 +45,7 @@ export class HbServiceService { /** * Sets the Homebridge startup settings */ - async setHomebridgeStartupSettings(data) { + async setHomebridgeStartupSettings(data: HbServiceStartupSettings) { // restart ui on next restart this.configService.hbServiceUiRestartRequired = true; @@ -91,8 +93,8 @@ export class HbServiceService { } const removeColour = new stream.Transform({ - transform(chunk, encoding, callback) { - callback(null, chunk.toString('utf8').replace(/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]/g, '')); + transform(chunk, _encoding, callback) { + callback(null, chunk.toString().replace(/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]/g, '')); }, }); diff --git a/src/modules/platform-tools/terminal/terminal.service.ts b/src/modules/platform-tools/terminal/terminal.service.ts index f114db264..838156fe4 100644 --- a/src/modules/platform-tools/terminal/terminal.service.ts +++ b/src/modules/platform-tools/terminal/terminal.service.ts @@ -20,6 +20,7 @@ export class TerminalService { /** * Create a new terminal session * @param client + * @param size */ async startSession(client: WsEventEmitter, size: TermSize) { this.ending = false; diff --git a/src/modules/plugins/plugins.service.ts b/src/modules/plugins/plugins.service.ts index 976740a8e..2272cd617 100755 --- a/src/modules/plugins/plugins.service.ts +++ b/src/modules/plugins/plugins.service.ts @@ -273,7 +273,7 @@ export class PluginsService { } /** - * Get a single plugin from the registry using it's exact name + * Get a single plugin from the registry using its exact name * Used as a fallback if the search queries are not finding the desired plugin * @param query */ @@ -369,7 +369,7 @@ export class PluginsService { // check if the plugin is currently installed const existingPlugin = this.installedPlugins.find(x => x.name === pluginAction.name); - // if the plugin is already installed, match the install path + // if the plugin is already installed, match the installation path if (existingPlugin) { installPath = existingPlugin.installPath; } @@ -557,7 +557,7 @@ export class PluginsService { try { const config: HomebridgeConfig = await fs.readJson(this.configService.configPath); config.bridge.advertiser = 'ciao'; - await fs.writeJsonSync(this.configService.configPath, config); + fs.writeJsonSync(this.configService.configPath, config); } catch (e) { this.logger.warn('Could not update config.json', e.message); } @@ -670,7 +670,8 @@ export class PluginsService { /** * Do a UI update from the bundle - * @param version + * @param pluginAction + * @param client */ public async doUiBundleUpdate(pluginAction: PluginActionDto, client: EventEmitter) { const prefix = path.dirname(path.dirname(path.dirname(process.env.UIX_BASE_PATH))); @@ -686,7 +687,7 @@ export class PluginsService { /** * Sets a flag telling the system to update the package next time the UI is restarted - * Dependend on OS support - currently only supported by the homebridge/homebridge docker image + * Dependent on OS support - currently only supported by the homebridge/homebridge docker image */ public async updateSelfOffline(client: EventEmitter) { client.emit('stdout', color.yellow(`${this.configService.name} has been scheduled to update on the next container restart.\n\r\n\r`)); @@ -748,19 +749,14 @@ export class PluginsService { // filter some options from the UI config when using service mode if (this.configService.serviceMode) { - configSchema.layout = configSchema.layout.filter(x => { - if (x.ref === 'log') { - return false; - } - return true; + configSchema.layout = configSchema.layout.filter((x: any) => { + return x.ref !== 'log'; }); - const advanced = configSchema.layout.find(x => x.ref === 'advanced'); - advanced.items = advanced.items.filter(x => { - if (x === 'sudo' || x.key === 'restart') { - return false; - } - return true; + const advanced = configSchema.layout.find((x: any) => x.ref === 'advanced'); + advanced.items = advanced.items.filter((x: any) => { + return !(x === 'sudo' || x.key === 'restart'); + }); } } @@ -1019,7 +1015,7 @@ export class PluginsService { /** * Load any @scoped homebridge modules */ - private async getInstalledScopedModules(requiredPath, scope): Promise> { + private async getInstalledScopedModules(requiredPath: string, scope: string): Promise> { try { if ((await fs.stat(path.join(requiredPath, scope))).isDirectory()) { const scopedModules = await fs.readdir(path.join(requiredPath, scope)); @@ -1079,7 +1075,7 @@ export class PluginsService { } /** - * Return a boolean if the plugin is an @scoped/homebridge plugin + * Return a boolean if the plugin is a @scoped/homebridge plugin */ private isScopedPlugin(name: string): boolean { return (name.charAt(0) === '@' && name.split('/').length > 0 && name.split('/')[1].indexOf('homebridge-') === 0); @@ -1153,7 +1149,7 @@ export class PluginsService { } /** - * Get path from the npm prefix, eg. /usr/local/lib/node_modules + * Get path from the npm prefix, e.g. /usr/local/lib/node_modules */ private getNpmPrefixToSearchPaths(): string[] { const paths = []; @@ -1282,6 +1278,8 @@ export class PluginsService { * @param command * @param cwd * @param client + * @param cols + * @param rows */ private async runNpmCommand(command: Array, cwd: string, client: EventEmitter, cols?: number, rows?: number) { // remove synology @eaDir folders from the node_modules @@ -1313,7 +1311,7 @@ export class PluginsService { client.emit('stdout', color.yellow(`You may experience issues while running on Node.js ${process.version}.\n\r\n\r`)); } - // setup the environment for the call + // set up the environment for the call const env = {}; Object.assign(env, process.env); Object.assign(env, { @@ -1338,7 +1336,7 @@ export class PluginsService { }); } - // on windows we want to ensure the global prefix is the same as the install path + // on windows, we want to ensure the global prefix is the same as the installation path if (os.platform() === 'win32') { Object.assign(env, { npm_config_prefix: cwd, @@ -1448,7 +1446,7 @@ export class PluginsService { } /** - * Loads the list of verified plugins from github + * Loads the list of verified plugins from GitHub */ private async loadVerifiedPluginsList() { clearTimeout(this.verifiedPluginsRetryTimeout); diff --git a/src/modules/server/server.service.ts b/src/modules/server/server.service.ts index fcaaf2684..965b0816a 100644 --- a/src/modules/server/server.service.ts +++ b/src/modules/server/server.service.ts @@ -140,7 +140,7 @@ export class ServerService { delete device.pairedClientsPermission; try { - device._category = Object.entries(Categories).find(([name, value]) => value === device.category)[0].toLowerCase(); + device._category = Object.entries(Categories).find(([, value]) => value === device.category)[0].toLowerCase(); } catch (e) { device._category = 'Other'; } @@ -351,7 +351,7 @@ export class ServerService { // See https://github.com/sebhildebrandt/systeminformation/issues/775#issuecomment-1741836906 // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const networkInterfaces = fromCache || (await si.networkInterfaces()).filter((adapter) => { + const networkInterfaces = fromCache || (await si.networkInterfaces()).filter((adapter: any) => { return !adapter.internal && (adapter.ip4 || (adapter.ip6)); }); diff --git a/ui/src/app/modules/status/status.component.html b/ui/src/app/modules/status/status.component.html index 6d339a16f..a75b86656 100644 --- a/ui/src/app/modules/status/status.component.html +++ b/ui/src/app/modules/status/status.component.html @@ -3,9 +3,9 @@ diff --git a/ui/src/app/modules/status/status.component.ts b/ui/src/app/modules/status/status.component.ts index acc6f9325..56144e9f7 100644 --- a/ui/src/app/modules/status/status.component.ts +++ b/ui/src/app/modules/status/status.component.ts @@ -10,7 +10,6 @@ import { AuthService } from '@/app/core/auth/auth.service'; import { SettingsService } from '@/app/core/settings.service'; import { MobileDetectService } from '@/app/core/mobile-detect.service'; import { NotificationService } from '@/app/core/notification.service'; -import { ManagePluginsService } from '@/app/core/manage-plugins/manage-plugins.service'; import { WidgetControlComponent } from './widget-control/widget-control.component'; import { WidgetAddComponent } from './widget-add/widget-add.component'; @@ -39,7 +38,6 @@ export class StatusComponent implements OnInit, OnDestroy { private $notification: NotificationService, public $auth: AuthService, public $settings: SettingsService, - public $plugin: ManagePluginsService, public $md: MobileDetectService, ) { } @@ -97,7 +95,7 @@ export class StatusComponent implements OnInit, OnDestroy { }); // this allows widgets to trigger a save to the grid layout - // eg. when the order of the accessories in the accessories widget changes + // e.g. when the order of the accessories in the accessories widget changes this.saveWidgetsEvent.subscribe({ next: () => { this.gridChangedEvent(); @@ -123,7 +121,7 @@ export class StatusComponent implements OnInit, OnDestroy { ); } - setLayout(layout) { + setLayout(layout: any[]) { this.dashboard = layout.map((item) => { item.$resizeEvent = new Subject(); item.$configureEvent = new Subject(); @@ -172,7 +170,7 @@ export class StatusComponent implements OnInit, OnDestroy { this.options.api.optionsChanged(); } - gridResizeEvent(item, itemComponent) { + gridResizeEvent(item: any, itemComponent: any) { itemComponent.item.$resizeEvent.next('resize'); this.page.mobile = (window.innerWidth < 1024); }