From b7706f78fd9c76e97b19c6d04983cd57cff36cc5 Mon Sep 17 00:00:00 2001 From: Wojciech Marmurowicz Date: Mon, 15 Jun 2026 17:29:08 +0200 Subject: [PATCH 1/4] feat: allow starting z2m without mqtt (Frontend+WebSocket / WebSocket only mode) --- data/configuration.example.yaml | 4 ++- lib/controller.ts | 25 ++++++++++----- lib/mqtt.ts | 7 +++-- lib/types/api.ts | 1 + lib/util/settings.schema.json | 10 +++++- lib/util/settings.ts | 5 ++- lib/util/settingsMigration.ts | 40 +++++++++++++++++++++++- test/controller.test.ts | 21 +++++++++++++ test/extensions/bridge.test.ts | 1 + test/onboarding.test.ts | 21 +++++++++++-- test/settings.test.ts | 11 +++++++ test/settingsMigration.test.ts | 55 +++++++++++++++++++++++++++++++++ 12 files changed, 185 insertions(+), 16 deletions(-) diff --git a/data/configuration.example.yaml b/data/configuration.example.yaml index 7506fd7b83..4bb5877e99 100644 --- a/data/configuration.example.yaml +++ b/data/configuration.example.yaml @@ -1,5 +1,5 @@ # Indicates the configuration version (used by configuration migrations) -version: 5 +version: 6 # Home Assistant integration (MQTT discovery) homeassistant: @@ -12,6 +12,8 @@ frontend: # MQTT settings mqtt: + # Enable MQTT connection + enabled: true # MQTT base topic for zigbee2mqtt MQTT messages base_topic: zigbee2mqtt # MQTT server URL diff --git a/lib/controller.ts b/lib/controller.ts index 1bd694c771..09a15fc2a3 100644 --- a/lib/controller.ts +++ b/lib/controller.ts @@ -165,12 +165,21 @@ export class Controller { logger.info(`Currently ${deviceCount} devices are joined.`); // MQTT - try { - await this.mqtt.connect(); - } catch (error) { - logger.error(`MQTT failed to connect, exiting... (${(error as Error).message})`); - await this.zigbee.stop(); - return await this.exit(1); + if (settings.get().mqtt.enabled) { + try { + await this.mqtt.connect(); + } catch (error) { + logger.error(`MQTT failed to connect, exiting... (${(error as Error).message})`); + await this.zigbee.stop(); + return await this.exit(1); + } + } else { + if (!settings.get().frontend.enabled) { + logger.error("MQTT and Frontend are both disabled, process is unable to start, exiting..."); + await this.zigbee.stop(); + return await this.exit(1); + } + logger.info("MQTT is disabled, skipping connection"); } if (abortSignal.aborted) { @@ -360,7 +369,9 @@ export class Controller { // Wrap-up this.state.stop(); - await this.mqtt.disconnect(); + if (settings.get().mqtt.enabled) { + await this.mqtt.disconnect(); + } try { await this.zigbee.stop(); diff --git a/lib/mqtt.ts b/lib/mqtt.ts index 8104ad6f55..3b3ec6e64d 100644 --- a/lib/mqtt.ts +++ b/lib/mqtt.ts @@ -28,6 +28,9 @@ export default class Mqtt { public retainedMessages: {[s: string]: {topic: string; payload: string; options: MqttPublishOptions}} = {}; get info() { + if (!settings.get().mqtt.enabled) { + return {version: undefined, server: ""}; + } return { version: this.client.options.protocolVersion, server: `${this.client.options.protocol}://${this.client.options.host}:${this.client.options.port}`, @@ -140,7 +143,7 @@ export default class Mqtt { // Set timer at interval to check if connected to MQTT server. this.connectionTimer = setInterval(() => { - if (!this.isConnected()) { + if (settings.get().mqtt.enabled && !this.isConnected()) { logger.error("Not connected to MQTT server!"); } }, utils.seconds(10)); @@ -223,7 +226,7 @@ export default class Mqtt { this.eventBus.emitMQTTMessagePublished({topic, payload, options: finalOptions}); if (!this.isConnected()) { - if (!finalOptions.skipLog) { + if (!finalOptions.skipLog && settings.get().mqtt.enabled) { logger.error("Not connected to MQTT server!"); logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`); } diff --git a/lib/types/api.ts b/lib/types/api.ts index 373591cd5d..722e62803c 100644 --- a/lib/types/api.ts +++ b/lib/types/api.ts @@ -120,6 +120,7 @@ export interface Zigbee2MQTTSettings { passive: {timeout: number}; }; mqtt: { + enabled: boolean; base_topic: string; include_device_information: boolean; force_disable_retain: boolean; diff --git a/lib/util/settings.schema.json b/lib/util/settings.schema.json index 1f997aa861..b8c6d684f9 100644 --- a/lib/util/settings.schema.json +++ b/lib/util/settings.schema.json @@ -117,6 +117,13 @@ "type": "object", "title": "MQTT", "properties": { + "enabled": { + "type": "boolean", + "title": "Enabled", + "description": "Enable MQTT connection. When false, the application runs without connecting to any MQTT broker.", + "default": true, + "requiresRestart": true + }, "base_topic": { "type": "string", "title": "Base topic", @@ -130,6 +137,7 @@ "title": "MQTT server", "requiresRestart": true, "description": "MQTT server URL (use mqtts:// for SSL/TLS connection)", + "default": "mqtt://localhost", "examples": ["mqtt://localhost:1883"] }, "keepalive": { @@ -219,7 +227,7 @@ "maximum": 268435456 } }, - "required": ["server"] + "required": [] }, "serial": { "type": "object", diff --git a/lib/util/settings.ts b/lib/util/settings.ts index 38dc2861ad..fab6f76ffa 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -10,7 +10,7 @@ import yaml from "./yaml"; export {schemaJson}; // When updating also update: // - https://github.com/Koenkk/zigbee2mqtt/blob/dev/data/configuration.example.yaml#L2 -export const CURRENT_VERSION = 5; +export const CURRENT_VERSION = 6; /** NOTE: by order of priority, lower index is lower level (more important) */ export const LOG_LEVELS: readonly string[] = ["error", "warning", "info", "debug"] as const; export type LogLevel = "error" | "warning" | "info" | "debug"; @@ -45,7 +45,9 @@ export const defaults = { base_url: "/", }, mqtt: { + enabled: true, base_topic: "zigbee2mqtt", + server: "mqtt://localhost", include_device_information: false, force_disable_retain: false, // 1MB = roughly 3.5KB per device * 300 devices for `/bridge/devices` @@ -158,6 +160,7 @@ export function writeMinimalDefaults(): void { const minimal = { version: CURRENT_VERSION, mqtt: { + enabled: true, base_topic: defaults.mqtt.base_topic, server: "mqtt://localhost:1883", }, diff --git a/lib/util/settingsMigration.ts b/lib/util/settingsMigration.ts index d8a6ea8f8a..faffd1bc8e 100644 --- a/lib/util/settingsMigration.ts +++ b/lib/util/settingsMigration.ts @@ -28,7 +28,7 @@ interface SettingsCustomHandler extends Omit { execute: (currentSettings: Partial) => [validPath: boolean, previousValue: unknown, changed: boolean]; } -const SUPPORTED_VERSIONS: Settings["version"][] = [undefined, 2, 3, 4, settings.CURRENT_VERSION]; +const SUPPORTED_VERSIONS: Settings["version"][] = [undefined, 2, 3, 4, 5, settings.CURRENT_VERSION]; function backupSettings(version: number): void { const filePath = data.joinPath("configuration.yaml"); @@ -532,6 +532,40 @@ function migrateToFive( }); } +function migrateToSix( + _currentSettings: Partial, + transfers: SettingsTransfer[], + changes: SettingsChange[], + additions: SettingsAdd[], + removals: SettingsRemove[], + customHandlers: SettingsCustomHandler[], +): void { + transfers.push(); + changes.push({ + path: ["version"], + note: "Migrated settings to version 6", + newValue: 6, + }); + additions.push(); + removals.push(); + + customHandlers.push({ + note: "Added mqtt.enabled option (defaults to true).", + noteIf: () => true, + execute: (currentSettings) => { + const [validPath, previousValue] = getValue(currentSettings, ["mqtt", "enabled"]); + + if (!validPath) { + setValue(currentSettings, ["mqtt", "enabled"], true, true); + + return [true, undefined, true]; + } + + return [true, previousValue, false]; + }, + }); +} + /** * Order of execution: * - Transfer @@ -588,6 +622,10 @@ export function migrateIfNecessary(): void { migrationNotesFileName = "migration-4-to-5.log"; migrateToFive(currentSettings, transfers, changes, additions, removals, customHandlers); + } else if (currentSettings.version === 5) { + migrationNotesFileName = "migration-5-to-6.log"; + + migrateToSix(currentSettings, transfers, changes, additions, removals, customHandlers); } for (const transfer of transfers) { diff --git a/test/controller.test.ts b/test/controller.test.ts index 116524799c..5917b1b09b 100644 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -381,6 +381,27 @@ describe("Controller", () => { expect(settings.get().onboarding).toStrictEqual(true); }); + it("Start controller with MQTT disabled should be successful", async () => { + settings.set(["mqtt", "enabled"], false); + settings.set(["frontend", "enabled"], true); + await controller.start(); + await flushPromises(); + await controller.stop(); + expect(mockZHController.stop).toHaveBeenCalledTimes(1); + expect(mockExit).toHaveBeenCalledTimes(1); + expect(mockExit).toHaveBeenCalledWith(0, false); + }); + + it("Start controller fails due to MQTT and frontend being disabled at the same time", async () => { + settings.set(["mqtt", "enabled"], false); + settings.set(["frontend", "enabled"], false); + await controller.start(); + await flushPromises(); + expect(mockLogger.error).toHaveBeenCalledWith("MQTT and Frontend are both disabled, process is unable to start, exiting..."); + expect(mockExit).toHaveBeenCalledTimes(1); + expect(mockExit).toHaveBeenCalledWith(1, false); + }); + it("Start controller and stop with restart", async () => { await controller.start(); await controller.stop(true); diff --git a/test/extensions/bridge.test.ts b/test/extensions/bridge.test.ts index 5a3a45f556..737c0d4119 100644 --- a/test/extensions/bridge.test.ts +++ b/test/extensions/bridge.test.ts @@ -311,6 +311,7 @@ describe("Extension: Bridge", () => { }, }, mqtt: { + enabled: true, base_topic: "zigbee2mqtt", force_disable_retain: false, include_device_information: false, diff --git a/test/onboarding.test.ts b/test/onboarding.test.ts index 513fc4c0e4..206db7a820 100644 --- a/test/onboarding.test.ts +++ b/test/onboarding.test.ts @@ -80,6 +80,7 @@ vi.mock("zigbee2mqtt-windfront", () => ({ const SETTINGS_MINIMAL_DEFAULTS = { version: settings.CURRENT_VERSION, mqtt: { + enabled: settings.defaults.mqtt!.enabled, base_topic: settings.defaults.mqtt!.base_topic, server: "mqtt://localhost:1883", }, @@ -133,6 +134,7 @@ const SAMPLE_SETTINGS_INIT = { const SAMPLE_SETTINGS_SAVE = { version: settings.CURRENT_VERSION, mqtt: { + enabled: true, base_topic: "zigbee2mqtt2", server: "mqtt://192.168.1.200:1883", }, @@ -252,7 +254,11 @@ describe("Onboarding", () => { if (expectWriteMinimal) { const minimal = process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER ? Object.assign({}, SETTINGS_MINIMAL_DEFAULTS, { - mqtt: {server: process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER, base_topic: SETTINGS_MINIMAL_DEFAULTS.mqtt.base_topic}, + mqtt: { + enabled: SETTINGS_MINIMAL_DEFAULTS.mqtt.enabled, + server: process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER, + base_topic: SETTINGS_MINIMAL_DEFAULTS.mqtt.base_topic, + }, }) : SETTINGS_MINIMAL_DEFAULTS; @@ -978,6 +984,7 @@ describe("Onboarding", () => { port: SETTINGS_MINIMAL_DEFAULTS.frontend.port, }, mqtt: { + enabled: SETTINGS_MINIMAL_DEFAULTS.mqtt.enabled, base_topic: SETTINGS_MINIMAL_DEFAULTS.mqtt.base_topic, server: process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER, user: "abcd", @@ -1200,7 +1207,11 @@ describe("Onboarding", () => { await expect(p).resolves.toStrictEqual(true); expect(data.read()).toStrictEqual( Object.assign({}, SAMPLE_SETTINGS_SAVE, { - mqtt: {server: process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER, base_topic: SAMPLE_SETTINGS_SAVE.mqtt.base_topic}, + mqtt: { + enabled: SAMPLE_SETTINGS_SAVE.mqtt.enabled, + server: process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER, + base_topic: SAMPLE_SETTINGS_SAVE.mqtt.base_topic, + }, }), ); }); @@ -1216,7 +1227,11 @@ describe("Onboarding", () => { await expect(p).resolves.toStrictEqual(true); const expected = Object.assign({}, SETTINGS_MINIMAL_DEFAULTS, { - mqtt: {server: process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER, base_topic: SETTINGS_MINIMAL_DEFAULTS.mqtt.base_topic}, + mqtt: { + enabled: SETTINGS_MINIMAL_DEFAULTS.mqtt.enabled, + server: process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER, + base_topic: SETTINGS_MINIMAL_DEFAULTS.mqtt.base_topic, + }, }); // @ts-expect-error mock delete expected.onboarding; diff --git a/test/settings.test.ts b/test/settings.test.ts index a42363c46f..7cecbd3f11 100644 --- a/test/settings.test.ts +++ b/test/settings.test.ts @@ -72,6 +72,7 @@ describe("Settings", () => { enabled: true, }, mqtt: { + enabled: true, base_topic: "zigbee2mqtt", server: "mqtt://localhost", }, @@ -309,6 +310,7 @@ describe("Settings", () => { write(configurationFile, contentConfiguration); const expected = { + enabled: true, base_topic: "zigbee2mqtt", include_device_information: false, maximum_packet_size: 1048576, @@ -357,6 +359,7 @@ describe("Settings", () => { write(configurationFile, contentConfiguration); const expected = { + enabled: true, base_topic: "zigbee2mqtt", include_device_information: false, maximum_packet_size: 1048576, @@ -678,6 +681,7 @@ describe("Settings", () => { }, }; const expected = { + enabled: true, base_topic: "zigbee2mqtt", include_device_information: false, maximum_packet_size: 1048576, @@ -692,6 +696,13 @@ describe("Settings", () => { expect(settings.get().mqtt).toStrictEqual(expected); }); + it("Should disable MQTT when mqtt.enabled is false", () => { + write(configurationFile, { + mqtt: {enabled: false, server: "mqtt://localhost"}, + }); + expect(settings.get().mqtt.enabled).toStrictEqual(false); + }); + it("Should add groups with specific ID", () => { write(configurationFile, {}); diff --git a/test/settingsMigration.test.ts b/test/settingsMigration.test.ts index 3c9903333e..2116a3c910 100644 --- a/test/settingsMigration.test.ts +++ b/test/settingsMigration.test.ts @@ -992,4 +992,59 @@ describe("Settings Migration", () => { expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("is not valid JSON")); }); }); + + describe("Migrates v5 to v6", () => { + const BASE_CONFIG = { + version: 5, + mqtt: { + server: "mqtt://localhost", + }, + }; + + beforeEach(() => { + settings.testing.CURRENT_VERSION = 6; // stop update after this version + data.writeDefaultConfiguration(BASE_CONFIG); + settings.reRead(); + }); + + it("adds mqtt.enabled when not present", () => { + // @ts-expect-error workaround + const beforeSettings = objectAssignDeep.noMutate({}, settings.getPersistedSettings()); + // @ts-expect-error workaround + const afterSettings = objectAssignDeep.noMutate({}, settings.getPersistedSettings()); + afterSettings.version = 6; + afterSettings.mqtt.enabled = true; + + expect(settings.getPersistedSettings()).toStrictEqual(beforeSettings); + + settingsMigration.migrateIfNecessary(); + + const migratedSettings = settings.getPersistedSettings(); + expect(migratedSettings).toStrictEqual(afterSettings); + + const migrationNotes = mockedData.joinPath("migration-5-to-6.log"); + expect(existsSync(migrationNotes)).toStrictEqual(true); + const migrationNotesContent = readFileSync(migrationNotes, "utf8"); + expect(migrationNotesContent).toContain("[SPECIAL] Added mqtt.enabled option (defaults to true)."); + }); + + it("does not overwrite existing mqtt.enabled when already set to false", () => { + settings.set(["mqtt", "enabled"], false); + + // @ts-expect-error workaround + const afterSettings = objectAssignDeep.noMutate({}, settings.getPersistedSettings()); + afterSettings.version = 6; + + settingsMigration.migrateIfNecessary(); + + const migratedSettings = settings.getPersistedSettings(); + expect(migratedSettings).toStrictEqual(afterSettings); + expect(migratedSettings.mqtt.enabled).toStrictEqual(false); + + const migrationNotes = mockedData.joinPath("migration-5-to-6.log"); + expect(existsSync(migrationNotes)).toStrictEqual(true); + const migrationNotesContent = readFileSync(migrationNotes, "utf8"); + expect(migrationNotesContent).not.toContain("[SPECIAL] Added mqtt.enabled option"); + }); + }); }); From 512426a74fa1b2da9d6bd230819af755489c9f6a Mon Sep 17 00:00:00 2001 From: Wojciech Marmurowicz Date: Mon, 22 Jun 2026 13:08:54 +0200 Subject: [PATCH 2/4] fix(ignore): fixed Health stats check error when mqtt client is disabled --- lib/mqtt.ts | 3 +++ test/extensions/health.test.ts | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/lib/mqtt.ts b/lib/mqtt.ts index 3b3ec6e64d..eac798511e 100644 --- a/lib/mqtt.ts +++ b/lib/mqtt.ts @@ -38,6 +38,9 @@ export default class Mqtt { } get stats() { + if (!settings.get().mqtt.enabled) { + return {connected: false, queued: 0}; + } return { connected: this.isConnected(), queued: this.client.queue.length, diff --git a/test/extensions/health.test.ts b/test/extensions/health.test.ts index d27c6dc579..cc61829aba 100644 --- a/test/extensions/health.test.ts +++ b/test/extensions/health.test.ts @@ -312,4 +312,10 @@ describe("Extension: Health", () => { }); expect(calls[0][2]).toStrictEqual({retain: true, qos: 1}); }); + + it("reports mqtt stats as disconnected when MQTT is disabled", async () => { + settings.set(["mqtt", "enabled"], false); + + expect(controller.mqtt.stats).toStrictEqual({connected: false, queued: 0}); + }); }); From 5a2b013d9273721c7ce4bfc7ae9a813f9fe8f85e Mon Sep 17 00:00:00 2001 From: Wojciech Marmurowicz Date: Mon, 22 Jun 2026 13:10:57 +0200 Subject: [PATCH 3/4] fix(ignore): fix pipeline run --- test/extensions/health.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/health.test.ts b/test/extensions/health.test.ts index cc61829aba..df51f96c04 100644 --- a/test/extensions/health.test.ts +++ b/test/extensions/health.test.ts @@ -313,7 +313,7 @@ describe("Extension: Health", () => { expect(calls[0][2]).toStrictEqual({retain: true, qos: 1}); }); - it("reports mqtt stats as disconnected when MQTT is disabled", async () => { + it("reports mqtt stats as disconnected when MQTT is disabled", () => { settings.set(["mqtt", "enabled"], false); expect(controller.mqtt.stats).toStrictEqual({connected: false, queued: 0}); From 827552e907b6e26111bde54b6c87576479d6b736 Mon Sep 17 00:00:00 2001 From: Wojciech Marmurowicz Date: Mon, 22 Jun 2026 13:21:05 +0200 Subject: [PATCH 4/4] fix(ignore): remove migration 6, as it is unnecessary --- data/configuration.example.yaml | 2 +- lib/util/settings.ts | 2 +- lib/util/settingsMigration.ts | 40 +----------------------- test/settingsMigration.test.ts | 55 --------------------------------- 4 files changed, 3 insertions(+), 96 deletions(-) diff --git a/data/configuration.example.yaml b/data/configuration.example.yaml index 4bb5877e99..277938bf9a 100644 --- a/data/configuration.example.yaml +++ b/data/configuration.example.yaml @@ -1,5 +1,5 @@ # Indicates the configuration version (used by configuration migrations) -version: 6 +version: 5 # Home Assistant integration (MQTT discovery) homeassistant: diff --git a/lib/util/settings.ts b/lib/util/settings.ts index fab6f76ffa..35d436fdd1 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -10,7 +10,7 @@ import yaml from "./yaml"; export {schemaJson}; // When updating also update: // - https://github.com/Koenkk/zigbee2mqtt/blob/dev/data/configuration.example.yaml#L2 -export const CURRENT_VERSION = 6; +export const CURRENT_VERSION = 5; /** NOTE: by order of priority, lower index is lower level (more important) */ export const LOG_LEVELS: readonly string[] = ["error", "warning", "info", "debug"] as const; export type LogLevel = "error" | "warning" | "info" | "debug"; diff --git a/lib/util/settingsMigration.ts b/lib/util/settingsMigration.ts index faffd1bc8e..d8a6ea8f8a 100644 --- a/lib/util/settingsMigration.ts +++ b/lib/util/settingsMigration.ts @@ -28,7 +28,7 @@ interface SettingsCustomHandler extends Omit { execute: (currentSettings: Partial) => [validPath: boolean, previousValue: unknown, changed: boolean]; } -const SUPPORTED_VERSIONS: Settings["version"][] = [undefined, 2, 3, 4, 5, settings.CURRENT_VERSION]; +const SUPPORTED_VERSIONS: Settings["version"][] = [undefined, 2, 3, 4, settings.CURRENT_VERSION]; function backupSettings(version: number): void { const filePath = data.joinPath("configuration.yaml"); @@ -532,40 +532,6 @@ function migrateToFive( }); } -function migrateToSix( - _currentSettings: Partial, - transfers: SettingsTransfer[], - changes: SettingsChange[], - additions: SettingsAdd[], - removals: SettingsRemove[], - customHandlers: SettingsCustomHandler[], -): void { - transfers.push(); - changes.push({ - path: ["version"], - note: "Migrated settings to version 6", - newValue: 6, - }); - additions.push(); - removals.push(); - - customHandlers.push({ - note: "Added mqtt.enabled option (defaults to true).", - noteIf: () => true, - execute: (currentSettings) => { - const [validPath, previousValue] = getValue(currentSettings, ["mqtt", "enabled"]); - - if (!validPath) { - setValue(currentSettings, ["mqtt", "enabled"], true, true); - - return [true, undefined, true]; - } - - return [true, previousValue, false]; - }, - }); -} - /** * Order of execution: * - Transfer @@ -622,10 +588,6 @@ export function migrateIfNecessary(): void { migrationNotesFileName = "migration-4-to-5.log"; migrateToFive(currentSettings, transfers, changes, additions, removals, customHandlers); - } else if (currentSettings.version === 5) { - migrationNotesFileName = "migration-5-to-6.log"; - - migrateToSix(currentSettings, transfers, changes, additions, removals, customHandlers); } for (const transfer of transfers) { diff --git a/test/settingsMigration.test.ts b/test/settingsMigration.test.ts index 2116a3c910..3c9903333e 100644 --- a/test/settingsMigration.test.ts +++ b/test/settingsMigration.test.ts @@ -992,59 +992,4 @@ describe("Settings Migration", () => { expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("is not valid JSON")); }); }); - - describe("Migrates v5 to v6", () => { - const BASE_CONFIG = { - version: 5, - mqtt: { - server: "mqtt://localhost", - }, - }; - - beforeEach(() => { - settings.testing.CURRENT_VERSION = 6; // stop update after this version - data.writeDefaultConfiguration(BASE_CONFIG); - settings.reRead(); - }); - - it("adds mqtt.enabled when not present", () => { - // @ts-expect-error workaround - const beforeSettings = objectAssignDeep.noMutate({}, settings.getPersistedSettings()); - // @ts-expect-error workaround - const afterSettings = objectAssignDeep.noMutate({}, settings.getPersistedSettings()); - afterSettings.version = 6; - afterSettings.mqtt.enabled = true; - - expect(settings.getPersistedSettings()).toStrictEqual(beforeSettings); - - settingsMigration.migrateIfNecessary(); - - const migratedSettings = settings.getPersistedSettings(); - expect(migratedSettings).toStrictEqual(afterSettings); - - const migrationNotes = mockedData.joinPath("migration-5-to-6.log"); - expect(existsSync(migrationNotes)).toStrictEqual(true); - const migrationNotesContent = readFileSync(migrationNotes, "utf8"); - expect(migrationNotesContent).toContain("[SPECIAL] Added mqtt.enabled option (defaults to true)."); - }); - - it("does not overwrite existing mqtt.enabled when already set to false", () => { - settings.set(["mqtt", "enabled"], false); - - // @ts-expect-error workaround - const afterSettings = objectAssignDeep.noMutate({}, settings.getPersistedSettings()); - afterSettings.version = 6; - - settingsMigration.migrateIfNecessary(); - - const migratedSettings = settings.getPersistedSettings(); - expect(migratedSettings).toStrictEqual(afterSettings); - expect(migratedSettings.mqtt.enabled).toStrictEqual(false); - - const migrationNotes = mockedData.joinPath("migration-5-to-6.log"); - expect(existsSync(migrationNotes)).toStrictEqual(true); - const migrationNotesContent = readFileSync(migrationNotes, "utf8"); - expect(migrationNotesContent).not.toContain("[SPECIAL] Added mqtt.enabled option"); - }); - }); });