diff --git a/CHANGELOG.md b/CHANGELOG.md index 6be023f085..bd27313997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,17 @@ # Changelog - -## __WORK IN PROGRESS__ +## **WORK IN PROGRESS** +* (bluefox) Added support for dynamic notification layout (in Admin) * (@foxriver76) updated plugin base and sentry plugin to version 2 * (@foxriver76) enhanced translations for the `diskSpaceIssues` notification category * (@foxriver76) extend the time to wait until controller is stopped on controller UI upgrade * (@foxriver76) improved backup/restore process to work for arbitrary large installations ## 6.0.11 (2024-08-21) - Kiera -* (@foxriver76) only generate `packageUpdates` notification, if new updates detected +* (foxriver76) only generate `packageUpdates` notification, if new updates detected ## 6.0.10 (2024-08-05) - Kiera * (foxriver76) fixed "alias subscription error" log @@ -32,7 +32,7 @@ ## 6.0.6 (2024-06-30) - Kiera * (foxriver76) fixed Windows installation -* (foxriver76) fixed issue on package updates (e.g. Admin Node.js update) +* (foxriver76) fixed issue on package updates (e.g., Admin Node.js update) ## 6.0.5 (2024-06-16) - Kiera @@ -43,25 +43,25 @@ **Features** * (foxriver76) Added possibility to automatically upgrade adapters (see https://github.com/ioBroker/ioBroker.js-controller?tab=readme-ov-file#automatic-adapter-upgrade) -* (foxriver76) if updates for OS packages are available a notification is generated (Linux only) +* (foxriver76) if updates for OS packages are available, a notification is generated (Linux only) * (foxriver76) the controller creates a notification if free disk space is critical (see https://github.com/ioBroker/ioBroker.js-controller?tab=readme-ov-file#disk-space-warnings) -* (foxriver76) allow to ignore specific adapter versions (see https://github.com/ioBroker/ioBroker.js-controller?tab=readme-ov-file#ignoring-specific-adapter-version) -* (foxriver76) if an adapter is blocklisted and thus stopped the controller now generates a notification +* (foxriver76) allow ignoring specific adapter versions (see https://github.com/ioBroker/ioBroker.js-controller?tab=readme-ov-file#ignoring-specific-adapter-version) +* (foxriver76) if an adapter is blocklisted and thus stopped, the controller now generates a notification * (foxriver76) allow to configure redis tls during `setup custom` **Optimizations and fixes** * (foxriver76) we now send `SIGKILL` instead of `SIGTERM` if adapter does not stop in normal time to prevent ghost processes -* (foxriver76) prevent crash case if an invalid pattern is scanned in the database -* (foxriver76) we now log the pid if a adapter process is stopped +* (foxriver76) prevent a crash case if an invalid pattern is scanned in the database +* (foxriver76) we now log the pid if an adapter process is stopped * (foxriver76/Apollon77) fixed crash case on file rotation * (foxriver76) optimized error messages and help text for cli commands `url` and `install` -* (foxriver76) if users want to install non existing adapters we now hint to the `url` command instead of recommending the use of npm -* (foxriver76) when interacting with aliases we no longer check permissions of the alias and the original object, we now only check the alias -* (foxriver76) host object is now already created during `setup first` run, allowing eg to disable sentry globally before first start of ioBroker -* (foxriver76) if the user sets a custom title for instances this is now preserved during upload -* (foxriver76) on reinstallation of adapters we uninstall the package manually first to ensure a correct reinstall +* (foxriver76) if users want to install non-existing adapters, we now hint to the `url` command instead of recommending the use of npm +* (foxriver76) when interacting with aliases, we no longer check permissions of the alias and the original object, we now only check the alias +* (foxriver76) host object is now already created during `setup first` run, allowing e.g., to disable sentry globally before first start of ioBroker +* (foxriver76) if the user sets a custom title for instances, this is now preserved during upload +* (foxriver76) on re-installation of adapters we uninstall the package manually first to ensure a correct reinstalling * (foxriver76) fixed problem on multihost discover -* (foxriver76) if `getState` is called on a non existing or non linked alias we return `null` like for all other non existing states +* (foxriver76) if `getState` is called on a non-existing or non-linked alias we return `null` like for all other non-existing states * (foxriver76) optimize alias subscribe performance for non-redis dbs * (foxriver76/bluefox/Apollon77) updated dependencies * (foxriver76/bluefox/Apollon77) minor fixes and stability improvements @@ -75,17 +75,17 @@ * (foxriver76) ioPack mode `subscribe` has been removed as you can achieve the same with mode `once` and setting `system.adapter.xy.alive` state (also removed `common.wakeup` and `common.subscribe` because of this) **Developer relevant new features** -* (foxriver76) js-controller (and thus the whole ioBroker) is now running as an ESM module internally while staying a 100 % backward compatible to adapters written in cjs +* (foxriver76) js-controller (and thus the whole ioBroker) is now running as an ESM module internally while staying a 100% backward compatible with adapters written in cjs * (foxriver76) adapters can now be written as ESM modules having full support (including compact mode) * (foxriver76) we provide all exports as ESM and as CJS to allow adapter developers to choose what to use * (foxriver76) added convenient methods to manage node modules (see https://github.com/ioBroker/ioBroker.js-controller?tab=readme-ov-file#managing-node-modules) -* (foxriver76) allow to specify reason and exit code on `adapter.stop` +* (foxriver76) allow specifying reason and exit code on `adapter.stop` * (foxriver76) if you blocklist a version in your `io-package.json` the controller won't start it anymore and will generate a notification * (foxriver76) for adapters of type `schedule` and `connectionType` set to `cloud` the schedule will be automatically delayed by up to 60 seconds randomly per user if the CRON does not contain a seconds argument, this is to prevent DDoS attacks **Developer relevant optimizations and fixes** -* (foxriver76) fixed crash case if an malformed object was defined in ioPack instanceObjects -* (foxriver76) when interacting with aliases we no longer check permissions of the alias and the original object, we now only check the alias +* (foxriver76) fixed crash case if a malformed object was defined in ioPack instanceObjects +* (foxriver76) when interacting with aliases, we no longer check permissions of the alias and the original object, we now only check the alias * (foxriver76) if `getState` is called on a non-existing or non-linked alias we return `null` like for all other non-existing states * (foxriver76/bluefox) multiple improvements on type level @@ -95,13 +95,13 @@ ## 5.0.18 (2024-01-30) - Jana **BREAKING CHANGES** * Support for Node.js 12 and 14 is dropped! Supported are Node.js 16.4.0+ and 18.x -* Backups created with the new js-controller version cannot be restored on hosts with lower js-controller version! +* Backups created with the new js-controller version cannot be restored on hosts with a lower js-controller version! * Update recommended npm version to 8 * Deprecate binary states, Adapters will change to use Files instead! **Features** -* (foxriver76) added method `sendToUserInterfaceClient` to push messages to UI client -* (foxriver76) Show npm error message on failing adapter installations and update also without debug parameter +* (foxriver76) added method `sendToUserInterfaceClient` to push messages to the UI client +* (foxriver76) Show npm error message on failing adapter installations and update also without a debug parameter * (bluefox/Apollon77/foxriver76) Try to solve `ENOTEMPTY` errors automatically on adapter upgrades/installations * (foxriver76) Introduce iobroker setting (dnsResolution) to choose between verbatim and ipv4 first dns resolution order * (foxriver76) Add support for windows for `iob fix` @@ -124,20 +124,20 @@ * (bluefox) Do not show warning on a requested adapter stop/termination * (bluefox) Make sure that the first log line is not missing in Admin * (foxriver76) Fix wrong formatting of cli get binary state if no encoding is passed -* (foxriver76) Fix restoring backups from pre js-controller 4.x which were created without config.system +* (foxriver76) Fix restoring backups from pre js-controller 4.x which were created without `config.system` * (foxriver76) Preserve tier setting on adapter upload * (foxriver76) update apt sources before installing os dependencies -* (foxriver76) Only skip erase on upload if opted out explicitly to always cleanup unneeded files +* (foxriver76) Only skip the erasing on upload if opted out explicitly to always clean up unneeded files * (foxriver76) Try to fix strange alias errors, inform us if it happens again! * (foxriver76) Remove windows bat files completely, installer will create them when needed -* (AlCalzone/Apollon77) Update jsonl db to prevent locking issues -* (foxriver76) Fixes `iob file sync` not working for jsonl +* (AlCalzone/Apollon77) Update JSONL db to prevent locking issues +* (foxriver76) Fixes `iob file sync` not working for JSONL * (foxriver76) Do not crash if we cannot initialize db backup directory on start * (foxriver76) Fixed issue with certificate validity on leap years * (Apollon77/foxriver76) Make sure that all relevant files are removed when eraseOnUpload is used * (foxriver76) fix wrong hostname after backup restore -* (bluefox) allow CLI vendor update without explicitly specifying vendor file (default file is used) -* (foxriver76) fix backup restore restoring to old database and using new one afterwards +* (bluefox) allow CLI vendor update without explicitly specifying a vendor file (a default file is used) +* (foxriver76) fix backup restore restoring to an old database and using new one afterwards * (foxriver76) fix that memory limit of instance was not applied * (foxriver76) validate cron jobs before executing them for `schedule` adapters * (bluefox) new notification category `securityIssues` @@ -146,13 +146,13 @@ **Developer relevant DEPRECATIONS/WARNINGS** * Deprecate binary states, Migrate your adapters to use Files instead! -* If you need to access special js-controller common tools please use adapter-core instead of js-controller directly - see https://github.com/ioBroker/adapter-core#commontools +* If you need to access special js-controller common tools, please use adapter-core instead of js-controller directly - see https://github.com/ioBroker/adapter-core#commontools * Parameter that contain patterns (e.g. `mqtt.*`) are now checked for allowed characters and general correctness! **Developer relevant new Features** * (bluefox) Added options to subscribe to file changes: subscribeForeignFiles, unsubscribeForeignFiles, onAdapterFileChanged (event contains id, fileName, size and not the whole content of the file!) -* (foxriver76) Introduce on("install") handler for adapter which is automatically called when adapter process is started with --install -* (foxriver76) Introduce io-package flag common.nodeProcessParams to allow to pass adapter specific nodejs process parameters (disables compact mode for the adapter!) +* (foxriver76) Introduce on("install") handler for adapter which is automatically called when an adapter process is started with --install +* (foxriver76) Introduce `io-package` flag common.nodeProcessParams to allow passing the adapter-specific Node.js process parameters (disables compact mode for the adapter!) * (foxriver76) Respect dns resolution (ipv4/ipv6 first) from config internally and provide relevant methods via adapter-core * (foxriver76) Add JSON-Schema for iobroker.json * (bluefox) Allowed export of password.js (to be used with adapter-core) @@ -162,7 +162,7 @@ * (bluefox) Extend getSuitableLicenses to look for other licenses * (buefox/foxriver76) Added new category for notifications: blocked and respect js-controller common.blockedVersions to block versions from starting * (bluefox) Added new system view "custom-full" to return the full objects for objects with a custom part and not only the custom details -* (foxriver76) Added support for getForeignObjects with an array of strings as pattern +* (foxriver76) Added support for getForeignObjects with an array of strings as a pattern * (AlCalzone) Preparations to allow js-controller Dev version to be used with dev-server * (foxriver76) Validate max timeout value (max 32bit max number) on setTimeout/setInterval and throw on error to prevent issues * (foxriver76) new `io-package` flag `common.supportedMessages` to replace `messagebox` and other messagebox-related properties @@ -180,22 +180,22 @@ ## 4.0.24 (2022-12-16) * (Bluefox) Excluded iot and cloud from masking special properties -* (AlCalzone) Added time based JSONL compaction (once a day) +* (AlCalzone) Added time-based JSONL compaction (once a day) * (Apollon77/foxriver76) Optimized message sequence id generation -* (Apollon77) Use ipv4 as primary DNS lookup option also in Node.js 18+ +* (Apollon77) Used ipv4 as a primary DNS lookup option also in Node.js 18+ * (buanet) Extended Docker detection to prevent issues in new Debian ## 4.0.23 (2022-04-19) * (AlCalzone) normalize JSONL options to prevent issues because of admin adding empty setting objects -* (Apollon77/foxriver76) Prevent some crash cases reported by Sentry +* (Apollon77/foxriver76) Prevented some crash cases reported by Sentry ## 4.0.21 (2022-03-12) -* (Apollon77) Fix Backup Restore crash case +* (Apollon77) Fixed Backup Restore crash case ## 4.0.20 (2022-03-12) -* (foxriver76) Add missing axios dependency -* (Apollon77) Only log an error if an error is happening when activating/deactivating adapter via .alive states -* (foxriver76) Fix edge case in redis simulator MULTI/EXEC logic +* (foxriver76) Added missing axios dependency +* (Apollon77) Only log an error if an error is happening when activating/deactivating adapter via `.alive` states +* (foxriver76) Fixed edge case in redis simulator MULTI/EXEC logic * (Apollon77/foxriver76) Prevent some crash cases reported by Sentry ## 4.0.19 (2022-03-05) @@ -206,7 +206,7 @@ * (foxriver76) Also stop instances before GitHub installs on windows * (Apollon77) Fix eraseOnUpload logic * (Apollon77) Fix invalid backup period warning -* (Apollon77) Fix maxSize property for logging in dist file +* (Apollon77) Fix maxSize property for logging in a dist file * (Apollon77/foxriver76) Prevent some crash cases reported by Sentry ## 4.0.15 (2022-02-03 - 2022-02-20) Release Isabelle @@ -217,7 +217,7 @@ * CLI command `iob rebuild adaptername` is no longer supported because of the new way of automatic rebuilds and some unwanted side effects * CLI command `iob state get ` will no longer handle binary state values (which was never really working before). We added `iob state getBinary ` as new way. * Ensure that on a backup-restore the same adapters and adapter versions are restored as existing on backup time. Also check js-controller version and error on mismatch (is allowed to be forced accepted by --force parameter for restore) -* The "file" database will be automatically converted into JSONL and the database types that use "file" are adjusted to "jsonl" on installation (and backup restore). This means that a rollback of js-controller is only possible to 3.3 after 4.0 was installed! Rollback to former versions require a manual migration to "file" DB before the downgrade! (COMMUNICATION, TESTFOKUS) +* The "file" database will be automatically converted into JSONL and the database types that use "file" are adjusted to "JSONL" on installation (and backup restore). This means that a rollback of js-controller is only possible to 3.3 after 4.0 was installed! Rollback to former versions requires a manual migration to "file" DB before the downgrade! (COMMUNICATION, TESTFOKUS) **Features** * (bluefox) Added complexity rules for user passwords: New created passwords need to follow the following rules (TODO ADMIN UI INFO ISSUE): @@ -247,17 +247,17 @@ * (AlCalzone) Removed extraneous "npm install" inside adapter directory * (foxriver76/AlCalzone) reduce `JSONL` compression frequency to reduce I/O (relevant when experimental `JSONL` database modules are used) * (foxriver76) prevent uploading js-controller (creating system.adapter.js-controller object) and remove existing cases on setup first -* (foxriver76) prevent crash when multihost password is invalid and multihost active +* (foxriver76) prevent crash when multihost password is invalid and multihost active * (bluefox/foxriver76) bigger internal refactorings in cli commands (TESTFOKUS) * (foxriver76) made logging of not fulfilled adapter dependencies more user-friendly * (foxriver76) Check user and group assignments and remove unknown users from groups (could have happened in earlier versions) in setup first * (foxriver76) Prevent crash on adapter install/update if version string in repo is invalid * (AlCalzone) Update and optimize JSONL database integration and configuration options -* (foxriver76) make sure that settings for file/jsonl DB in configfile are also respected +* (foxriver76) make sure that settings for file/JSONL DB in configfile are also respected * (foxriver76) Update seq integration for logging * (foxriver76) If logging can not be initialized because of a fatal error do not start js-controller -* (foxriver76) Prevent start of a debug session for an instance that is already running -* (Apollon77) Fix an edge case for file db which could lead to main and backup file being broken in strange situations with multiple crashes in a row +* (foxriver76) Prevent the start of a debug session for an instance that is already running +* (Apollon77) Fixed an edge case for file db which could lead to the main and backup file being broken in strange situations with multiple crashes in a row * (foxriver76) make sure that admin, backitup and discovery instances are created when update of controller happens if installed and no instance exists * (Apollon77) Optimize Stop Handling to prevent errors * (Apollon77) Optimize Adapter process initialization to prevent edge case errors @@ -265,32 +265,32 @@ * (Apollon77) make sure to really end CLI process when they should end in error cases * (Apollon77) catch error when streaming data to stdout and this is closed already * (Apollon77) Optimize some special cases on adapter start -* (AlCalzone) Prevent issues when backup interval is configured with invalid values -* (AlCalzone) Prevent db-file-locking issues for jsonl database; the connectTimeout for databases is now minimum 5s (overrides lower values from configuration) -* (foxriver76) allow instance deletion when executed from an other host +* (AlCalzone) Prevent issues when a backup interval is configured with invalid values +* (AlCalzone) Prevent db-file-locking issues for JSONL database; the connectTimeout for databases is now minimum 5s (overrides lower values from configuration) +* (foxriver76) allow instance deletion when executed from another host * (foxriver76) stop the database before executing upgrade self; Will be effective for all upgrades >4.0.5 * (Apollon77/foxriver76) Optimize database initialization and destroys * (Apollon77) Update winston-syslog to prevent errors * (Apollon77, foxriver76, bluefox, AlCalzone) Several fixes and refactorings to prevent potential crash cases reported by Sentry and other sources **Developer relevant DEPRECATIONS/WARNINGS** -* **js-controller is no longer installable from GitHub because is now a monorepo. Use `@dev` tag on npm to get the nightly build of master js-controller!** +* **js-controller is no longer installable from GitHub because it is now a monorepo. Use `@dev` tag on npm to get the nightly build of master js-controller!** * log info when `setState` is used for an object of type file - use `setBinaryState` instead! -* log info when default value of an object is invalid (e.g. does not match object type) +* log info when the default value of an object is invalid (e.g., does not match object type) * log info when `common.states` is used and not an object (deprecate String usage) -* log info when `common.min`/`common.max` exists on non numbers and contain invalid values/types +* log info when `common.min`/`common.max` exists on non-numbers and contain invalid values/types * add get/setForeignBinaryState methods as copy from get/setBinaryState allow adapter to migrate; get/setBinaryState will be changed in 4.1 to be "non Foreign" -* Enhanced object checks: adapter need to have a name as string -* Decline calls for getForeignObjects with non string pattern (was pot. crashing before) -* adapter.tools is deprecated and replaced by a shim. Use methods in adapter class or adapter-core instead or open issues if you need more internal functions +* Enhanced object checks: adapter needs to have a name as string +* Decline calls for getForeignObjects with a non-string pattern (was pot. crashing before) +* `adapter.tools` is deprecated and replaced by a shim. Use methods in adapter class or adapter-core instead or open issues if you need more internal functions The object view definition "custom/state" is now removed from js-controller after being replaced by "system/custom" in js-controller 3.3. All relevant adapters are updated (COMMUNICATION) * remove all *Fifo* Methods from adapter.js because deprecated since 1.x * remove adapter.objects.* methods because deprecated since 2.x **Developer relevant new Features** -* (jogibear9988) Add new "unload-safe" promise based "adapter.delay" method to delay further code execution, but still make sure code do not continue after unload was called. This method can **not** be used inside the "unload" method itself! +* (jogibear9988) Add new "unload-safe" promise based "adapter.delay" method to delay further code execution, but still make sure code does not continue after unload was called. This method can **not** be used inside the "unload" method itself! * (jogibear9988/AlCalzone/foxriver76/Apollon77) Revamp adapter.*Timeout and adapter.*Interval methods to be "unload-safe" and also clear missing timeouts without warnings for more developer convenience! The methods to set a timeout or interval can **not** be used inside the "unload" method itself! -* (foxriver76) Allow to set "null" for common.states and not log error for object (also for extend) +* (foxriver76) Allow to set "null" for `common.states` and not log error for an object (also for extend) * (AlCalzone) Introduce new methods in tools for Node.js module management: installNodeModule, uninstallNodeModule (TODO Issues adapter that use npm install -> Move)) * (bluefox) Add license management functionality to host. Adapters can use adapter.getSuitableLicenses to get available relevant licenses (TODO DOCS) * (AlCalzone) Switch NPM relevant handling to library pak to be more flexible for the future which package manager we want to use. Important: There are still parts that rely on npm for now! @@ -321,7 +321,7 @@ The object view definition "custom/state" is now removed from js-controller afte ## 3.3.19 (2021-07-26 - 2021-11-17) Release Hannah **BREAKING CHANGES** -* None, Supported are nodejs 10.x, 12.x and 14.x (Node.js 16.x is also working WHEN USED WITH npm 6!!, but officially not yet supported because we do not have enough results) +* None, Supported are Node.js 10.x, 12.x and 14.x (Node.js 16.x is also working WHEN USED WITH npm 6!!, but officially not yet supported because we do not have enough results) * The experimental `JSONL` db libraries are now included in js-controller directly too * (Apollon77) Do not install info adapter by default * (foxriver76) changed default behaviour of cli update command -> only list installed, allow --all as parameter to see all again diff --git a/README.md b/README.md index c85162cb1d..58eb39d7d3 100644 --- a/README.md +++ b/README.md @@ -512,6 +512,12 @@ This method takes the following parameters: * scope: scope to be addressed * category: category to be addressed, if a null message will be checked by regex of given scope * message: message to be stored/checked +* options: Available with js-controller version 6.1. Additional options for the notification, currently you can provide additional `contextData` which is also stored with the notification information. Notification processing adapters can use this data + +Note, that the structure of the `contextData` which can be stored via the options object is not defined by the controller. Adapters which handle messages can use individual data attributes. +Currently, it is planned to support individual notification customization in the `admin` adapter. More information will be available in the `admin` adapter as soon as this feature is ready. + +As a best practice the top-level of `contextData` should not be populated with individual data belonging to instances. Use a `key` specific to the adapter or if a feature is supported by all adapters of a type, the type (e.g. `messaging`) is also fine. When a regex is defined then `console.error` output from the adapter is always checked by the regex and notifications are registered automatically when the regex matches! diff --git a/package.json b/package.json index d82073a397..8163c85c7b 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "build:ts": "lerna run build --ignore '@iobroker/types'", "build:types": "npm run build --workspace=@iobroker/types", "build": "npm run build:ts && npm run build:types", + "npm": "npm i --ignore-scripts", "postbuild": "npm run update-schema", "preinstall": "lerna run preinstall", "install": "lerna run install", diff --git a/packages/adapter/src/lib/_Types.ts b/packages/adapter/src/lib/_Types.ts index 53517bb5a7..49d9e1708c 100644 --- a/packages/adapter/src/lib/_Types.ts +++ b/packages/adapter/src/lib/_Types.ts @@ -598,3 +598,13 @@ export interface InternalInstallNodeModuleOptions extends InstallNodeModuleOptio /** Name of the npm module or an installable url ẁorking with `npm install` */ moduleNameOrUrl: string; } + +/** + * Options for the generated notification + */ +export interface NotificationOptions { + /** + * Additional context for the notification which can be used by notification processing adapters + */ + contextData: ioBroker.NotificationContextData; +} diff --git a/packages/adapter/src/lib/adapter/adapter.ts b/packages/adapter/src/lib/adapter/adapter.ts index 538ec35443..4bc9231eb3 100644 --- a/packages/adapter/src/lib/adapter/adapter.ts +++ b/packages/adapter/src/lib/adapter/adapter.ts @@ -132,7 +132,8 @@ import type { InstallNodeModuleOptions, InternalInstallNodeModuleOptions, StopParameters, - InternalStopParameters + InternalStopParameters, + NotificationOptions } from '@/lib/_Types.js'; import { UserInterfaceMessagingController } from '@/lib/adapter/userInterfaceMessagingController.js'; import { SYSTEM_ADAPTER_PREFIX } from '@iobroker/js-controller-common-db/constants'; @@ -7589,17 +7590,19 @@ export class AdapterClass extends EventEmitter { registerNotification( scope: Scope, category: ioBroker.NotificationScopes[Scope] | null, - message: string + message: string, + options?: NotificationOptions ): Promise; /** * Send notification with given scope and category to host of this adapter * * @param scope - scope to be addressed - * @param category - to be addressed, if null message will be checked by regex of given scope + * @param category - to be addressed, if a null message will be checked by regex of given scope * @param message - message to be stored/checked + * @param options - Additional options for the notification, currently `contextData` is supported */ - async registerNotification(scope: unknown, category: unknown, message: unknown): Promise { + async registerNotification(scope: unknown, category: unknown, message: unknown, options?: unknown): Promise { if (!this.#states) { // if states is no longer existing, we do not need to set this._logger.info( @@ -7614,9 +7617,19 @@ export class AdapterClass extends EventEmitter { } Validator.assertString(message, 'message'); + if (options !== undefined) { + Validator.assertObject(options, 'options'); + } + const obj = { command: 'addNotification', - message: { scope, category, message, instance: this.namespace }, + message: { + scope, + category, + message, + instance: this.namespace, + contextData: options?.contextData + }, from: `system.adapter.${this.namespace}` }; diff --git a/packages/cli/src/lib/setup.ts b/packages/cli/src/lib/setup.ts index 84b4394f45..c06c78d971 100644 --- a/packages/cli/src/lib/setup.ts +++ b/packages/cli/src/lib/setup.ts @@ -755,12 +755,12 @@ async function processCommand( ); await notificationHandler.addConfig(ioPackage.notifications); - await notificationHandler.addMessage( - 'system', - 'fileToJsonl', - `Migrated: ${migrated}`, - `system.host.${hostname}` - ); + await notificationHandler.addMessage({ + scope: 'system', + category: 'fileToJsonl', + message: `Migrated: ${migrated}`, + instance: `system.host.${hostname}` + }); notificationHandler.storeNotifications(); } catch (e) { diff --git a/packages/common/src/lib/common/notificationHandler.ts b/packages/common/src/lib/common/notificationHandler.ts index 30ae6097fc..a4ac6bf5c0 100644 --- a/packages/common/src/lib/common/notificationHandler.ts +++ b/packages/common/src/lib/common/notificationHandler.ts @@ -36,7 +36,7 @@ export type Severity = 'info' | 'notify' | 'alert'; export interface CategoryConfigEntry { category: string; name: MultilingualObject; - /** `info` will only be shown by admin, while `notify` might also be used by messaging adapters, `alert` ensures both */ + /** Allows defining the severity of the notification with `info` being the lowest, `notify` representing middle priority, `alert` representing high priority and often containing critical information */ severity: Severity; description: MultilingualObject; regex: string[]; @@ -46,6 +46,7 @@ export interface CategoryConfigEntry { interface NotificationMessageObject { message: string; ts: number; + contextData?: ioBroker.NotificationContextData; } interface NotificationsObject { @@ -99,6 +100,19 @@ interface ScopeStateValue { }; } +interface AddMessageOptions { + /** Scope of the message */ + scope: string; + /** Category of the message, if non we check against regex of scope */ + category?: string | null; + /** Message to add */ + message: string; + /** Instance e.g., hm-rpc.1 or hostname, if hostname it needs to be prefixed like system.host.rpi */ + instance: string; + /** Additional context for the notification which can be used by notification processing adapters */ + contextData?: ioBroker.NotificationContextData; +} + export class NotificationHandler { private states: StatesInRedisClient; private objects: ObjectsInRedisClient; @@ -128,14 +142,14 @@ export class NotificationHandler { // create the initial notifications object let obj; try { - obj = await this.objects.getObjectAsync(`system.host.${this.host}.notifications`); + obj = await this.objects.getObject(`system.host.${this.host}.notifications`); } catch { // ignore } if (!obj) { try { - await this.objects.setObjectAsync(`system.host.${this.host}.notifications`, { + await this.objects.setObject(`system.host.${this.host}.notifications`, { type: 'folder', common: { name: { @@ -168,7 +182,7 @@ export class NotificationHandler { }); for (const entry of res.rows) { - // check that instance has notifications settings + // check that instance has notification settings if (entry.value.notifications) { await this.addConfig(entry.value.notifications); } @@ -202,7 +216,7 @@ export class NotificationHandler { /** * Add a new category to the given scope with a provided optional list of regex * - * @param notifications - notifications array + * @param notifications - Array with notifications */ async addConfig(notifications: NotificationsConfigEntry[]): Promise { // if valid attributes, store it @@ -211,14 +225,14 @@ export class NotificationHandler { // create the state object for each scope if non-existing let obj; try { - obj = await this.objects.getObjectAsync(`system.host.${this.host}.notifications.${scopeObj.scope}`); + obj = await this.objects.getObject(`system.host.${this.host}.notifications.${scopeObj.scope}`); } catch { // ignore } if (!obj) { try { - await this.objects.setObjectAsync(`system.host.${this.host}.notifications.${scopeObj.scope}`, { + await this.objects.setObject(`system.host.${this.host}.notifications.${scopeObj.scope}`, { type: 'state', common: { type: 'object', @@ -283,17 +297,12 @@ export class NotificationHandler { /** * Add a message to the scope and category * - * @param scope - scope of the message - * @param category - category of the message, if non we check against regex of scope - * @param message - message to add - * @param instance - instance e.g., hm-rpc.1 or hostname, if hostname it needs to be prefixed like system.host.rpi + * @param options The scope, category, message, instance and contextData information */ - async addMessage( - scope: string, - category: string | null | undefined, - message: string, - instance: string - ): Promise { + async addMessage(options: AddMessageOptions): Promise { + const { message, scope, category, contextData } = options; + let { instance } = options; + if (typeof instance !== 'string') { this.log.error( `${this.logPrefix} [addMessage] Instance has to be of type "string", got "${typeof instance}"` @@ -330,7 +339,7 @@ export class NotificationHandler { this.currentNotifications[scope][_category][instance] || []; if (!this.setup[scope]?.categories[_category]) { - // no setup for this instance/category combination found - so nothing to add + // no setup for this instance/category combination found - so we have nothing to add this.log.warn( `${this.logPrefix} No configuration found for scope "${scope}" and category "${_category}"` ); @@ -346,7 +355,7 @@ export class NotificationHandler { } // add a new element at the beginning - this.currentNotifications[scope][_category][instance].unshift({ message, ts: Date.now() }); + this.currentNotifications[scope][_category][instance].unshift({ message, ts: Date.now(), contextData }); } } @@ -361,7 +370,7 @@ export class NotificationHandler { // set updated scope state try { - await this.states.setStateAsync(`system.host.${this.host}.notifications.${scope}`, { + await this.states.setState(`system.host.${this.host}.notifications.${scope}`, { val: JSON.stringify(stateVal), ack: true }); @@ -423,7 +432,7 @@ export class NotificationHandler { } /** - * Load notifications from file + * Load notifications from a file */ private _loadNotifications(): void { try { @@ -469,7 +478,11 @@ export class NotificationHandler { continue; } - res[scope] = { categories: {}, description: this.setup[scope].description, name: this.setup[scope].name }; + res[scope] = { + categories: {}, + description: this.setup[scope].description, + name: this.setup[scope].name + }; for (const category of Object.keys(this.currentNotifications[scope])) { if (categoryFilter && categoryFilter !== category) { diff --git a/packages/controller/src/main.ts b/packages/controller/src/main.ts index 1120e13e59..c3fc54e1fc 100644 --- a/packages/controller/src/main.ts +++ b/packages/controller/src/main.ts @@ -987,13 +987,14 @@ async function checkSystemLocaleSupported(): Promise { const isSupported = await objects.isSystemLocaleSupported(); if (!isSupported) { - await notificationHandler.addMessage( - 'system', - 'databaseErrors', - 'Your redis server is using an unsupported locale. This can lead to unexpected behavior of your ioBroker installation as well as data loss. ' + + await notificationHandler.addMessage({ + category: 'system', + scope: 'databaseErrors', + message: + 'Your redis server is using an unsupported locale. This can lead to unexpected behavior of your ioBroker installation as well as data loss. ' + 'Please configure your Redis Server according to https://forum.iobroker.net/topic/52976/wichtiger-hinweis-f%C3%BCr-redis-installationen?_=1678099836122', - `system.host.${hostname}` - ); + instance: `system.host.${hostname}` + }); } } @@ -1126,12 +1127,12 @@ async function reportStatus(): Promise { const isDiskWarningActive = percentageFree < diskWarningLevel; if (isDiskWarningActive) { - await notificationHandler.addMessage( - 'system', - 'diskSpaceIssues', - `Your system has only ${percentageFree.toFixed(2)} % of disk space left.`, - `system.host.${hostname}` - ); + await notificationHandler.addMessage({ + scope: 'system', + category: 'diskSpaceIssues', + message: `Your system has only ${percentageFree.toFixed(2)} % of disk space left.`, + instance: `system.host.${hostname}` + }); } states.setState(`${id}.diskSize`, { @@ -2924,12 +2925,14 @@ async function processMessage(msg: ioBroker.SendableMessage): Promise { return; } - await notificationHandler.addMessage('system', 'packageUpdates', packages.join('\n'), `system.host.${hostname}`); + await notificationHandler.addMessage({ + scope: 'system', + category: 'packageUpdates', + message: packages.join('\n'), + instance: `system.host.${hostname}` + }); } /** @@ -5831,7 +5844,12 @@ async function checkRebootRequired(): Promise { } } - await notificationHandler.addMessage('system', 'systemRebootRequired', message, `system.host.${hostname}`); + await notificationHandler.addMessage({ + scope: 'system', + category: 'systemRebootRequired', + message, + instance: `system.host.${hostname}` + }); } /** @@ -5847,21 +5865,25 @@ async function autoUpgradeAdapters(): Promise { const { upgradedAdapters, failedAdapters } = await autoUpgradeManager.upgradeAdapters(); if (upgradedAdapters.length) { - await notificationHandler.addMessage( - 'system', - 'automaticAdapterUpgradeSuccessful', - upgradedAdapters.map(entry => `${entry.name}: ${entry.oldVersion} -> ${entry.newVersion}`).join('\n'), - `system.host.${hostname}` - ); + await notificationHandler.addMessage({ + scope: 'system', + category: 'automaticAdapterUpgradeSuccessful', + message: upgradedAdapters + .map(entry => `${entry.name}: ${entry.oldVersion} -> ${entry.newVersion}`) + .join('\n'), + instance: `system.host.${hostname}` + }); } if (failedAdapters.length) { - await notificationHandler.addMessage( - 'system', - 'automaticAdapterUpgradeFailed', - failedAdapters.map(entry => `${entry.name}: ${entry.oldVersion} -> ${entry.newVersion}`).join('\n'), - `system.host.${hostname}` - ); + await notificationHandler.addMessage({ + scope: 'system', + category: 'automaticAdapterUpgradeFailed', + message: failedAdapters + .map(entry => `${entry.name}: ${entry.oldVersion} -> ${entry.newVersion}`) + .join('\n'), + instance: `system.host.${hostname}` + }); } } catch (e) { logger.error(`${hostLogPrefix} An error occurred while processing automatic adapter upgrades: ${e.message}`); @@ -5885,7 +5907,12 @@ async function disableBlocklistedInstances(): Promise { const message = `Instance "${disabledInstance._id}" has been stopped and disabled because the version "${disabledInstance.common.version}" has been blocked by the developer`; logger.error(`${hostLogPrefix} ${message}`); - await notificationHandler.addMessage('system', 'blockedVersions', message, SYSTEM_HOST_PREFIX + hostname); + await notificationHandler.addMessage({ + scope: 'system', + category: 'blockedVersions', + message, + instance: SYSTEM_HOST_PREFIX + hostname + }); } } diff --git a/packages/types-dev/index.d.ts b/packages/types-dev/index.d.ts index 403e674d45..146a673abb 100644 --- a/packages/types-dev/index.d.ts +++ b/packages/types-dev/index.d.ts @@ -323,6 +323,13 @@ declare global { | 'fileToJsonl'; [other: string]: string; } + + /** Additional context for the notification which can be used by notification processing adapters */ + interface NotificationContextData { + /** Use a `key` specific to the adapter or if a feature is supported by all adapters of a type, the type (e.g. `messaging`) is also fine. */ + [adapterNameOrAdapterType: string]: unknown; + } + interface AdapterConfig { // This is a stub to be augmented in every adapter } diff --git a/packages/types-dev/objects.d.ts b/packages/types-dev/objects.d.ts index d1676de504..8d90a123c5 100644 --- a/packages/types-dev/objects.d.ts +++ b/packages/types-dev/objects.d.ts @@ -486,7 +486,7 @@ declare global { }; /** - * Object which defines, if the adapter supports receiving messages via sendTo. + * Object which defines if the adapter supports receiving messages via sendTo. * Additionally, it defines if specific messages are supported. * If one property is enabled, the object `system.adapter...messagebox will be created to send messages to the adapter (used for email, pushover, etc...) */ @@ -522,8 +522,8 @@ declare global { /** Use 'paid' for adapters which do not work without a paid license. Use 'commercial' for adapters which require a license for commercial use only. Use 'limited' if some functionalities are not available without a paid license. */ type: 'free'; /** - * Hyperlink, where information about the license can be found. For non-free licenses the linked page should contain information about free features (if applicable), time of validity, link to shop and seller information. - * This is required if the license type is different from 'free'. For 'free' licenses an optional link to the license file can be placed here. + * Hyperlink, where information about the license can be found. For non-free licenses, the linked page should contain information about free features (if applicable), time of validity, link to shop and seller information. + * This is required if the license type is different from 'free'. For 'free' licenses, an optional link to the license file can be placed here. */ link?: string; } @@ -534,8 +534,8 @@ declare global { /** Use 'paid' for adapters which do not work without a paid license. Use 'commercial' for adapters which require a license for commercial use only. Use 'limited' if some functionalities are not available without a paid license. */ type: PaidLicenseType; /** - * Hyperlink, where information about the license can be found. For non-free licenses the linked page should contain information about free features (if applicable), time of validity, link to shop and seller information. - * This is required if the license type is different from 'free'. For 'free' licenses an optional link to the license file can be placed here. + * Hyperlink, where information about the license can be found. For non-free licenses, the linked page should contain information about free features (if applicable), time of validity, link to shop and seller information. + * This is required if the license type is different from 'free'. For 'free' licenses, an optional link to the license file can be placed here. */ link: string; } @@ -1089,7 +1089,7 @@ declare global { }; interface Notification { - /** Each adapter can define its own "scopes" for own notifications with its own categories which then will be available in the system. Adapters should only register one scope which matches the name of the adapter. */ + /** E.g., `system`. Each adapter can define its own "scopes" for own notifications with its own categories which then will be available in the system. Adapters should only register one scope which matches the name of the adapter. */ scope: string; /** The human-readable name of this scope */ name: Translated; diff --git a/schemas/io-package.json b/schemas/io-package.json index da2df6bbb7..d499bef211 100644 --- a/schemas/io-package.json +++ b/schemas/io-package.json @@ -953,7 +953,6 @@ "communication", "date-and-time", "energy", - "metering", "garden", "general", "geoposition", @@ -965,6 +964,7 @@ "lighting", "logic", "messaging", + "metering", "misc-data", "multimedia", "network",