mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-29 15:18:41 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a14feef8c1 | ||
|
|
d6a5128aa8 | ||
|
|
9d0ee03f38 | ||
|
|
37927e698a | ||
|
|
89cdb1f180 | ||
|
|
e90afb286a | ||
|
|
fb4d01ebce | ||
|
|
9e481fe5e5 | ||
|
|
0b7d0039f7 | ||
|
|
5257c7eae1 | ||
|
|
952116d327 |
+2
-1
@@ -1,2 +1,3 @@
|
||||
pnpm-lock.yaml
|
||||
CHANGELOG.md
|
||||
CHANGELOG.md
|
||||
release-notes.md
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "2.2.0"
|
||||
".": "2.2.1"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## [2.2.1](https://github.com/Koenkk/zigbee2mqtt/compare/2.2.0...2.2.1) (2025-04-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix issue with some settings when migrating from 1.x.x to 2.2.x ([#26947](https://github.com/Koenkk/zigbee2mqtt/issues/26947)) ([9e481fe](https://github.com/Koenkk/zigbee2mqtt/commit/9e481fe5e57a8e664dd58cff7765b5365630c7c1))
|
||||
* Fix settings being overwriting when env var is set to a ref ([#26988](https://github.com/Koenkk/zigbee2mqtt/issues/26988)) ([d6a5128](https://github.com/Koenkk/zigbee2mqtt/commit/d6a5128aa819a85e2a06e57307329d2b35938397))
|
||||
* **ignore:** update zigbee-herdsman-converters to 23.19.0 ([#26938](https://github.com/Koenkk/zigbee2mqtt/issues/26938)) ([0b7d003](https://github.com/Koenkk/zigbee2mqtt/commit/0b7d0039f709215ac1a4895f15f06db41b4d4998))
|
||||
* **ignore:** update zigbee-herdsman-converters to 23.19.1 ([#26961](https://github.com/Koenkk/zigbee2mqtt/issues/26961)) ([e90afb2](https://github.com/Koenkk/zigbee2mqtt/commit/e90afb286a15c5416fd1126864acdfc4c03c3a64))
|
||||
* **ignore:** update zigbee-herdsman-converters to 23.20.0 ([#26978](https://github.com/Koenkk/zigbee2mqtt/issues/26978)) ([37927e6](https://github.com/Koenkk/zigbee2mqtt/commit/37927e698abcb242965423e9ee37623255feadab))
|
||||
* **ignore:** update zigbee-herdsman-converters to 23.20.1 ([#26989](https://github.com/Koenkk/zigbee2mqtt/issues/26989)) ([9d0ee03](https://github.com/Koenkk/zigbee2mqtt/commit/9d0ee03f38ee6881dce3b17c219a6580303607ef))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.9.6 ([#26959](https://github.com/Koenkk/zigbee2mqtt/issues/26959)) ([fb4d01e](https://github.com/Koenkk/zigbee2mqtt/commit/fb4d01ebced7046919d6657c7609a196d39a1a6c))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.9.7 ([#26976](https://github.com/Koenkk/zigbee2mqtt/issues/26976)) ([89cdb1f](https://github.com/Koenkk/zigbee2mqtt/commit/89cdb1f180bb343b56a50c94dc6a23dda1345582))
|
||||
|
||||
## [2.2.0](https://github.com/Koenkk/zigbee2mqtt/compare/2.1.3...2.2.0) (2025-04-01)
|
||||
|
||||
|
||||
|
||||
+4
-10
@@ -526,14 +526,14 @@ export async function onboard(): Promise<boolean> {
|
||||
}
|
||||
|
||||
const confExists = existsSync(data.joinPath('configuration.yaml'));
|
||||
let checkMigration = true;
|
||||
|
||||
if (!confExists) {
|
||||
settings.writeMinimalDefaults();
|
||||
|
||||
// don't check for migration if conf was just written
|
||||
checkMigration = false;
|
||||
} else {
|
||||
// migrate first
|
||||
const {migrateIfNecessary} = await import('./settingsMigration.js');
|
||||
|
||||
migrateIfNecessary();
|
||||
// trigger initial writing of `ZIGBEE2MQTT_CONFIG_*` ENVs
|
||||
settings.write();
|
||||
}
|
||||
@@ -550,12 +550,6 @@ export async function onboard(): Promise<boolean> {
|
||||
|
||||
settings.reRead();
|
||||
|
||||
if (checkMigration) {
|
||||
const {migrateIfNecessary} = await import('./settingsMigration.js');
|
||||
|
||||
migrateIfNecessary();
|
||||
}
|
||||
|
||||
const errors = settings.validate();
|
||||
|
||||
if (errors.length > 0) {
|
||||
|
||||
@@ -181,24 +181,22 @@ export function write(): void {
|
||||
const settings = getPersistedSettings();
|
||||
const toWrite: KeyValue = objectAssignDeep({}, settings);
|
||||
|
||||
applyEnvironmentVariables(toWrite);
|
||||
|
||||
// Read settings to check if we have to split devices/groups into separate file.
|
||||
const actual = yaml.read(CONFIG_FILE_PATH);
|
||||
|
||||
// In case the setting is defined in a separate file (e.g. !secret network_key) update it there.
|
||||
for (const path of [
|
||||
for (const [ns, key] of [
|
||||
['mqtt', 'server'],
|
||||
['mqtt', 'user'],
|
||||
['mqtt', 'password'],
|
||||
['advanced', 'network_key'],
|
||||
['frontend', 'auth_token'],
|
||||
]) {
|
||||
if (actual[path[0]] && actual[path[0]][path[1]]) {
|
||||
const ref = parseValueRef(actual[path[0]][path[1]]);
|
||||
if (actual[ns] && actual[ns][key]) {
|
||||
const ref = parseValueRef(actual[ns][key]);
|
||||
if (ref) {
|
||||
yaml.updateIfChanged(data.joinPath(ref.filename), ref.key, toWrite[path[0]][path[1]]);
|
||||
toWrite[path[0]][path[1]] = actual[path[0]][path[1]];
|
||||
yaml.updateIfChanged(data.joinPath(ref.filename), ref.key, toWrite[ns][key]);
|
||||
toWrite[ns][key] = actual[ns][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,6 +224,9 @@ export function write(): void {
|
||||
|
||||
writeDevicesOrGroups('devices');
|
||||
writeDevicesOrGroups('groups');
|
||||
|
||||
applyEnvironmentVariables(toWrite);
|
||||
|
||||
yaml.writeIfChanged(CONFIG_FILE_PATH, toWrite);
|
||||
|
||||
_settings = read();
|
||||
|
||||
@@ -491,7 +491,7 @@ function migrateToFour(
|
||||
* Should allow the most flexibility whenever combination of migrations is necessary (e.g. Transfer + Change)
|
||||
*/
|
||||
export function migrateIfNecessary(): void {
|
||||
let currentSettings = settings.getPersistedSettings();
|
||||
const currentSettings = settings.getPersistedSettings();
|
||||
|
||||
if (!SUPPORTED_VERSIONS.includes(currentSettings.version)) {
|
||||
throw new Error(
|
||||
@@ -502,7 +502,11 @@ export function migrateIfNecessary(): void {
|
||||
/* v8 ignore next */
|
||||
const finalVersion = process.env.VITEST_WORKER_ID ? settings.testing.CURRENT_VERSION : settings.CURRENT_VERSION;
|
||||
|
||||
// when same version as current, nothing left to do
|
||||
if (currentSettings.version === finalVersion) {
|
||||
// when same version as current, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
while (currentSettings.version !== finalVersion) {
|
||||
let migrationNotesFileName: string | undefined;
|
||||
// don't duplicate outputs
|
||||
@@ -577,10 +581,9 @@ export function migrateIfNecessary(): void {
|
||||
|
||||
console.log(`Migration notes written in ${migrationNotesFilePath}`);
|
||||
}
|
||||
|
||||
// don't throw to allow stepping through versions (validates against current schema)
|
||||
settings.apply(currentSettings as unknown as Record<string, unknown>, false);
|
||||
settings.reRead();
|
||||
currentSettings = settings.getPersistedSettings();
|
||||
}
|
||||
|
||||
// don't throw, onboarding will validate at end of process
|
||||
settings.apply(currentSettings as unknown as Record<string, unknown>, false);
|
||||
settings.reRead();
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zigbee2mqtt",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1",
|
||||
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
|
||||
"main": "index.js",
|
||||
"packageManager": "pnpm@10.4.1",
|
||||
@@ -63,8 +63,8 @@
|
||||
"winston-transport": "^4.9.0",
|
||||
"ws": "^8.18.1",
|
||||
"zigbee-herdsman": "3.4.11",
|
||||
"zigbee-herdsman-converters": "23.18.0",
|
||||
"zigbee2mqtt-frontend": "0.9.5"
|
||||
"zigbee-herdsman-converters": "23.20.1",
|
||||
"zigbee2mqtt-frontend": "0.9.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/core": "^0.12.0",
|
||||
|
||||
Generated
+10
-10
@@ -78,11 +78,11 @@ importers:
|
||||
specifier: 3.4.11
|
||||
version: 3.4.11
|
||||
zigbee-herdsman-converters:
|
||||
specifier: 23.18.0
|
||||
version: 23.18.0
|
||||
specifier: 23.20.1
|
||||
version: 23.20.1
|
||||
zigbee2mqtt-frontend:
|
||||
specifier: 0.9.5
|
||||
version: 0.9.5
|
||||
specifier: 0.9.7
|
||||
version: 0.9.7
|
||||
devDependencies:
|
||||
'@eslint/core':
|
||||
specifier: ^0.12.0
|
||||
@@ -1823,8 +1823,8 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
zigbee-herdsman-converters@23.18.0:
|
||||
resolution: {integrity: sha512-AL3jFKWWefk9PTOb2jX6IfyWCfvLbIIEqvvfLiC2gqXzPnjmo1Qd4kseEMM9z5Ia5IjoKjKZRV0Uf9FNAFBSFg==}
|
||||
zigbee-herdsman-converters@23.20.1:
|
||||
resolution: {integrity: sha512-cNlOnZcY3Hyqhbd28llqzaJzRMrtjk/CxbXBK2ADcJFztARpx1L2h5ySdvcrzLvUXGlWvJXNRy5M7larwDUvZg==}
|
||||
|
||||
zigbee-herdsman@3.4.11:
|
||||
resolution: {integrity: sha512-OzJXvkmXXWgy0rBUaKAeSPvggGJnr+0XfWf1gUaiBrCvlbpSPYRzAqpiqM1dgfX3Cg6zJ5cofy5eEd9UW0sBlQ==}
|
||||
@@ -1833,8 +1833,8 @@ packages:
|
||||
resolution: {integrity: sha512-RP1LNZynW4C2y79W5qihw5/NNV8lVmmu4wAr7Kitj3isDFOAt7RXX2eiDrz98wiLmbFwBIOhfArrG/Pb/jaZIA==}
|
||||
engines: {node: '>=20.17.0'}
|
||||
|
||||
zigbee2mqtt-frontend@0.9.5:
|
||||
resolution: {integrity: sha512-pTHL9mvMSUbjXRiV1IL9hhXZeCd3nngmum/pX5gd/nn4kccTXhiMjQRSmXuAWNr+8n/gk6hTB1QeDYY+u7aGFQ==}
|
||||
zigbee2mqtt-frontend@0.9.7:
|
||||
resolution: {integrity: sha512-IqwoLEMp6lE9FUN0X2Ton73d9p2AMPeIj983Mh40YfAmr4KN/DLjjr35jJKZodYO3v9AGsPSvicE7HPvuOOWHw==}
|
||||
engines: {node: '>=20.11'}
|
||||
|
||||
snapshots:
|
||||
@@ -3491,7 +3491,7 @@ snapshots:
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
zigbee-herdsman-converters@23.18.0:
|
||||
zigbee-herdsman-converters@23.20.1:
|
||||
dependencies:
|
||||
buffer-crc32: 1.0.0
|
||||
iconv-lite: 0.6.3
|
||||
@@ -3516,4 +3516,4 @@ snapshots:
|
||||
|
||||
zigbee-on-host@0.1.10: {}
|
||||
|
||||
zigbee2mqtt-frontend@0.9.5: {}
|
||||
zigbee2mqtt-frontend@0.9.7: {}
|
||||
|
||||
@@ -166,6 +166,8 @@ describe('Onboarding', () => {
|
||||
delete process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_PAN_ID;
|
||||
delete process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_EXT_PAN_ID;
|
||||
delete process.env.ZIGBEE2MQTT_CONFIG_FRONTEND_PORT;
|
||||
delete process.env.ZIGBEE2MQTT_CONFIG_FRONTEND;
|
||||
delete process.env.ZIGBEE2MQTT_CONFIG_HOMEASSISTANT_ENABLED;
|
||||
|
||||
data.writeDefaultConfiguration(SAMPLE_SETTINGS_INIT);
|
||||
data.removeState();
|
||||
@@ -697,6 +699,30 @@ describe('Onboarding', () => {
|
||||
expect(settings.get().version).toStrictEqual(settings.CURRENT_VERSION);
|
||||
});
|
||||
|
||||
it('runs 1.x.x conflict migrations', async () => {
|
||||
data.writeDefaultConfiguration({
|
||||
mqtt: {
|
||||
server: 'mqtt://core-mosquitto:1883',
|
||||
},
|
||||
homeassistant: true,
|
||||
advanced: {
|
||||
network_key: 'GENERATE',
|
||||
pan_id: 'GENERATE',
|
||||
ext_pan_id: 'GENERATE',
|
||||
},
|
||||
});
|
||||
settings.reRead();
|
||||
process.env.ZIGBEE2MQTT_CONFIG_FRONTEND = '{"enabled":true,"port": 8099}';
|
||||
process.env.ZIGBEE2MQTT_CONFIG_HOMEASSISTANT_ENABLED = 'true';
|
||||
|
||||
const p = onboard();
|
||||
|
||||
await expect(p).resolves.toStrictEqual(true);
|
||||
expect(settings.get().version).toStrictEqual(settings.CURRENT_VERSION);
|
||||
expect(settings.get().homeassistant).toMatchObject({enabled: true});
|
||||
expect(settings.get().frontend).toMatchObject({enabled: true, port: 8099});
|
||||
});
|
||||
|
||||
it('handles validation failure', async () => {
|
||||
settings.set(['serial', 'adapter'], 'emberz');
|
||||
|
||||
@@ -719,6 +745,7 @@ describe('Onboarding', () => {
|
||||
|
||||
it('handles creating data path', async () => {
|
||||
rmSync(data.mockDir, {force: true, recursive: true});
|
||||
settings.testing.clear();
|
||||
|
||||
let p;
|
||||
await new Promise<[string, string]>((resolve, reject) => {
|
||||
|
||||
+46
-10
@@ -105,6 +105,8 @@ describe('Settings', () => {
|
||||
});
|
||||
|
||||
it('Should apply environment variables as overrides', () => {
|
||||
write(secretFile, {password: 'the-password'}, false);
|
||||
process.env.ZIGBEE2MQTT_CONFIG_MQTT_PASSWORD = '!secret.yaml password';
|
||||
process.env.ZIGBEE2MQTT_CONFIG_SERIAL_DISABLE_LED = 'true';
|
||||
process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_CHANNEL = '15';
|
||||
process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_OUTPUT = 'attribute_and_json';
|
||||
@@ -122,12 +124,6 @@ describe('Settings', () => {
|
||||
},
|
||||
};
|
||||
|
||||
write(configurationFile, {});
|
||||
write(devicesFile, contentDevices);
|
||||
expect(settings.write()); // trigger writing of ENVs
|
||||
expect(settings.validate()).toStrictEqual([]);
|
||||
|
||||
const s = settings.get();
|
||||
// @ts-expect-error workaround
|
||||
const expected = objectAssignDeep.noMutate({}, settings.testing.defaults);
|
||||
expected.devices = {
|
||||
@@ -144,14 +140,54 @@ describe('Settings', () => {
|
||||
expected.map_options.graphviz.colors.fill = {enddevice: '#ff0000', coordinator: '#00ff00', router: '#0000ff'};
|
||||
expected.mqtt.base_topic = 'testtopic';
|
||||
expected.mqtt.server = 'testserver';
|
||||
expected.mqtt.password = 'the-password';
|
||||
expected.advanced.network_key = 'GENERATE';
|
||||
|
||||
expect(s).toStrictEqual(expected);
|
||||
write(configurationFile, {mqtt: {password: 'config-password'}});
|
||||
write(devicesFile, contentDevices);
|
||||
|
||||
settings.set(['advanced', 'channel'], 25);
|
||||
const writeAndCheck = (): void => {
|
||||
expect(settings.write()); // trigger writing of ENVs
|
||||
expect(settings.validate()).toStrictEqual([]);
|
||||
expect(settings.get()).toStrictEqual(expected);
|
||||
|
||||
expect(settings.get().advanced.channel).toStrictEqual(15);
|
||||
expect(read(configurationFile)).toMatchObject({advanced: {channel: 15}});
|
||||
settings.set(['advanced', 'channel'], 25);
|
||||
expect(settings.get().advanced.channel).toStrictEqual(15);
|
||||
expect(read(configurationFile)).toMatchObject({advanced: {channel: 15}});
|
||||
|
||||
expect(read(secretFile)).toMatchObject({password: 'the-password'});
|
||||
expect(read(configurationFile)).toHaveProperty('mqtt.password', '!secret.yaml password');
|
||||
};
|
||||
|
||||
// Write trice to ensure there are no side effects.
|
||||
writeAndCheck();
|
||||
writeAndCheck();
|
||||
writeAndCheck();
|
||||
});
|
||||
|
||||
it('Should write environment variables as overrides to configuration.yaml, not in the ref file', () => {
|
||||
write(secretFile, {password: 'password-in-secret-file'}, false);
|
||||
write(configurationFile, {mqtt: {password: '!secret password', server: 'server'}});
|
||||
process.env.ZIGBEE2MQTT_CONFIG_MQTT_PASSWORD = 'password-in-env-var';
|
||||
|
||||
const writeAndCheck = (): void => {
|
||||
expect(settings.write()); // trigger writing of ENVs
|
||||
expect(settings.validate()).toStrictEqual([]);
|
||||
|
||||
const s = settings.get();
|
||||
// @ts-expect-error workaround
|
||||
const expected = objectAssignDeep.noMutate({groups: {}, devices: {}}, settings.testing.defaults);
|
||||
expected.mqtt.password = 'password-in-env-var';
|
||||
expected.mqtt.server = 'server';
|
||||
expect(s).toStrictEqual(expected);
|
||||
expect(read(secretFile)).toMatchObject({password: 'password-in-secret-file'});
|
||||
expect(read(configurationFile)).toMatchObject({mqtt: {password: 'password-in-env-var', server: 'server'}});
|
||||
};
|
||||
|
||||
// Write trice to ensure there are no side effects.
|
||||
writeAndCheck();
|
||||
writeAndCheck();
|
||||
writeAndCheck();
|
||||
});
|
||||
|
||||
it('Should add devices', () => {
|
||||
|
||||
Reference in New Issue
Block a user