Tried to keep the custom settings of the objects when updating them with new data from HASS

This commit is contained in:
GermanBluefox
2026-05-05 10:01:38 +02:00
parent 9a24c2be91
commit a64ef243f0
6 changed files with 397 additions and 904 deletions
+2 -2
View File
@@ -28,7 +28,7 @@ jobs:
steps: steps:
- uses: ioBroker/testing-action-check@v1 - uses: ioBroker/testing-action-check@v1
with: with:
node-version: '22.x' node-version: '24.x'
lint: true lint: true
adapter-tests: adapter-tests:
@@ -67,6 +67,6 @@ jobs:
steps: steps:
- uses: ioBroker/testing-action-deploy@v1 - uses: ioBroker/testing-action-deploy@v1
with: with:
node-version: '22.x' node-version: '24.x'
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build: true build: true
+3
View File
@@ -106,6 +106,9 @@ Please check it https://www.smarthomejetzt.de/mit-iobroker-auf-eine-home-assista
--> -->
## Changelog ## Changelog
### **WORK IN PROGRESS**
* (@GermanBluefox) Tried to keep the custom settings of the objects when updating them with new data from HASS
### 2.0.3 (2026-04-02) ### 2.0.3 (2026-04-02)
* (@GermanBluefox) Adapter was updated and migrated to TypeScript * (@GermanBluefox) Adapter was updated and migrated to TypeScript
* (@Titanium177) Added roles for states and added debouncing for reading states from hass * (@Titanium177) Added roles for states and added debouncing for reading states from hass
+10 -25
View File
@@ -131,19 +131,16 @@
"adminUI": { "adminUI": {
"config": "json" "config": "json"
}, },
"license": "MIT", "licenseInformation": {
"type": "free",
"license": "MIT"
},
"extIcon": "https://raw.githubusercontent.com/ioBroker/ioBroker.hass/master/admin/hass.png", "extIcon": "https://raw.githubusercontent.com/ioBroker/ioBroker.hass/master/admin/hass.png",
"keywords": [ "keywords": ["HASS", "Home", "assistant"],
"HASS",
"Home",
"assistant"
],
"readme": "https://github.com/ioBroker/ioBroker.hass/blob/master/README.md", "readme": "https://github.com/ioBroker/ioBroker.hass/blob/master/README.md",
"loglevel": "info", "loglevel": "info",
"type": "iot-systems", "type": "iot-systems",
"authors": [ "authors": ["bluefox <dogafox@gmail.com>"],
"bluefox <dogafox@gmail.com>"
],
"dependencies": [ "dependencies": [
{ {
"js-controller": ">=6.0.11" "js-controller": ">=6.0.11"
@@ -166,10 +163,7 @@
{ {
"condition": { "condition": {
"operand": "and", "operand": "and",
"rules": [ "rules": ["oldVersion<1.1.0", "newVersion>=1.1.0"]
"oldVersion<1.1.0",
"newVersion>=1.1.0"
]
}, },
"title": { "title": {
"en": "Re-enter the password after Update!", "en": "Re-enter the password after Update!",
@@ -198,18 +192,12 @@
"uk": "Після встановлення цього оновлення пароль/токен, який використовується для доступу до встановлення HASS, потрібно повторно ввести в налаштуваннях екземпляра!" "uk": "Після встановлення цього оновлення пароль/токен, який використовується для доступу до встановлення HASS, потрібно повторно ввести в налаштуваннях екземпляра!"
}, },
"level": "warn", "level": "warn",
"buttons": [ "buttons": ["agree", "cancel"]
"agree",
"cancel"
]
}, },
{ {
"condition": { "condition": {
"operand": "and", "operand": "and",
"rules": [ "rules": ["oldVersion<1.2.0", "newVersion>=1.2.0"]
"oldVersion<1.2.0",
"newVersion>=1.2.0"
]
}, },
"title": { "title": {
"en": "Important notice!", "en": "Important notice!",
@@ -238,10 +226,7 @@
"uk": "Нова версія 1.2.x потенційно змінить назви об’єктів, якщо в атрибутах об’єктів використовувалися спеціальні символи, такі як «.», «-» або пробіли! За потреби видаліть старі об’єкти вручну." "uk": "Нова версія 1.2.x потенційно змінить назви об’єктів, якщо в атрибутах об’єктів використовувалися спеціальні символи, такі як «.», «-» або пробіли! За потреби видаліть старі об’єкти вручну."
}, },
"level": "warn", "level": "warn",
"buttons": [ "buttons": ["agree", "cancel"]
"agree",
"cancel"
]
} }
] ]
}, },
+139 -658
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -37,8 +37,7 @@
"@iobroker/eslint-config": "^2.2.0", "@iobroker/eslint-config": "^2.2.0",
"@iobroker/legacy-testing": "^2.0.2", "@iobroker/legacy-testing": "^2.0.2",
"@iobroker/testing": "^5.2.2", "@iobroker/testing": "^5.2.2",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1"
"mocha": "^11.7.5"
}, },
"main": "build/main.js", "main": "build/main.js",
"files": [ "files": [
+28 -3
View File
@@ -403,26 +403,51 @@ class HassAdapter extends Adapter {
private async deleteStaleObjects(expectedObjects: Set<string>): Promise<number> { private async deleteStaleObjects(expectedObjects: Set<string>): Promise<number> {
const objectsToDelete: string[] = []; const objectsToDelete: string[] = [];
let knownCount = 0;
for (const id in this.hassObjects) { for (const id in this.hassObjects) {
if ( if (
Object.prototype.hasOwnProperty.call(this.hassObjects, id) && Object.prototype.hasOwnProperty.call(this.hassObjects, id) &&
id.startsWith(`${this.namespace}.entities.`) && id.startsWith(`${this.namespace}.entities.`)
!expectedObjects.has(id)
) { ) {
knownCount++;
if (!expectedObjects.has(id)) {
objectsToDelete.push(id); objectsToDelete.push(id);
} }
} }
}
// Sanity guard: if HASS returned a drastically smaller entity set than what we
// know (e.g. mid-startup after a restart), assume the data is incomplete and
// skip deletion. The next sync will retry.
if (knownCount > 10 && objectsToDelete.length > knownCount / 2) {
this.log.warn(
`Skipping deletion of ${objectsToDelete.length}/${knownCount} stale objects — HASS likely returned an incomplete state list. Will retry on next sync.`,
);
return 0;
}
let deletedCount = 0;
for (const id of objectsToDelete) { for (const id of objectsToDelete) {
try { try {
// Never auto-delete objects that hold user configuration like
// common.custom (history/influxdb/sql adapter settings) — losing
// those silently on a transient HASS hiccup would force the user
// to recreate them. See issue #165.
const existing = await this.getForeignObjectAsync(id);
const custom = (existing?.common as { custom?: Record<string, unknown> } | undefined)?.custom;
if (custom && Object.keys(custom).length) {
this.log.debug(`Keeping "${id}" despite being stale: object holds custom adapter configuration`);
continue;
}
await this.delObjectAsync(id); await this.delObjectAsync(id);
delete this.hassObjects[id]; delete this.hassObjects[id];
deletedCount++;
} catch (err) { } catch (err) {
this.log.error(`Error deleting object ${id}: ${err}`); this.log.error(`Error deleting object ${id}: ${err}`);
} }
} }
return objectsToDelete.length; return deletedCount;
} }
private async parseStates(entities: HassEntity[], services: HassServices): Promise<void> { private async parseStates(entities: HassEntity[], services: HassServices): Promise<void> {