iobroker.lovelace 4.1.9 → 4.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/io-package.json +26 -5
- package/lib/modules/history.js +3 -2
- package/lib/modules/person.js +27 -14
- package/lib/server.js +31 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -473,6 +473,10 @@ After that checkout modified version in `./build` folder. Then.
|
|
|
473
473
|
PLACEHOLDER for the next version:
|
|
474
474
|
### **WORK IN PROGRESS**
|
|
475
475
|
-->
|
|
476
|
+
### 4.1.10 (2024-05-23)
|
|
477
|
+
* (Garfonso) device icons work again.
|
|
478
|
+
* (Garfonso) default user sometimes was not found in system.
|
|
479
|
+
|
|
476
480
|
### 4.1.9 (2024-04-26)
|
|
477
481
|
* (Garfonso) add support for new service call structure.
|
|
478
482
|
* (Garfonso) add support for delivering files from other adapters, for example, local cover images.
|
|
@@ -489,9 +493,6 @@ After that checkout modified version in `./build` folder. Then.
|
|
|
489
493
|
### 4.1.5 (2024-03-05)
|
|
490
494
|
* (Garfonso) fixed: possible crashes during startup
|
|
491
495
|
|
|
492
|
-
### 4.1.4 (2024-02-10)
|
|
493
|
-
* (Garfonso) improved fix: lamp icons now turn gray on switch off.
|
|
494
|
-
|
|
495
496
|
## License
|
|
496
497
|
|
|
497
498
|
Copyright 2019-2024, bluefox <dogafox@gmail.com>
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "lovelace",
|
|
4
|
-
"version": "4.1.
|
|
4
|
+
"version": "4.1.10",
|
|
5
5
|
"news": {
|
|
6
|
+
"4.1.10": {
|
|
7
|
+
"en": "device icons work again (if authorization is required).\ndefault user sometimes was not found in system.",
|
|
8
|
+
"de": "gerätesymbole funktionieren wieder (wenn authorisierung notwendig).\nder eingestellte standard-benutzer wurde manchmal nicht im system gefunden.",
|
|
9
|
+
"ru": "иконки устройства снова работают.\nпользователь по умолчанию иногда не был найден в системе.",
|
|
10
|
+
"pt": "ícones do dispositivo funcionam novamente.\nusuário padrão às vezes não foi encontrado no sistema.",
|
|
11
|
+
"nl": "apparaatpictogrammen werken opnieuw.\nde standaardgebruiker werd soms niet gevonden in het systeem.",
|
|
12
|
+
"fr": "les icônes du périphérique fonctionnent à nouveau.\nl'utilisateur par défaut n'a parfois pas été trouvé dans le système.",
|
|
13
|
+
"it": "le icone del dispositivo funzionano di nuovo.\nutente predefinito a volte non è stato trovato nel sistema.",
|
|
14
|
+
"es": "los iconos del dispositivo funcionan de nuevo.\nusuario predeterminado a veces no se encontró en el sistema.",
|
|
15
|
+
"pl": "ikony urządzenia działają ponownie.\ndomyślny użytkownik czasami nie został znaleziony w systemie.",
|
|
16
|
+
"uk": "знову працюють іконки пристрою.\nне знайдено типовий користувач.",
|
|
17
|
+
"zh-cn": "设备图标再次工作.\n默认用户有时在系统中找不到 ."
|
|
18
|
+
},
|
|
6
19
|
"4.1.9": {
|
|
7
20
|
"en": "add support for new service call structure.\nadd support for delivering files from other adapters, for example, local cover images.\ncleaned up service descriptions.",
|
|
8
21
|
"de": "unterstützung für neue service call-struktur hinzufügen.\nhinzufügen von unterstützung für die bereitstellung von dateien von anderen adaptern, zum beispiel lokale cover-bilder.\ngereinigte servicebeschreibungen.",
|
|
@@ -53,10 +66,6 @@
|
|
|
53
66
|
"4.1.4": {
|
|
54
67
|
"en": "improved fix: lamp icons now turn gray on switch off.",
|
|
55
68
|
"de": "verbesserter fix: lampensymbole werden nun grau beim ausschalten."
|
|
56
|
-
},
|
|
57
|
-
"4.1.3": {
|
|
58
|
-
"en": "prevent warning for browser_mod/recall_id service call\nfix: lamp icons now turn gray on switch off.\nfix: notifications via sendTo work again.",
|
|
59
|
-
"de": "Verhindere warnung für browser_mod/recall_id service call\nfix: Lampensymbole werden nun grau beim ausschalten.\nfix: Benachrichtigung mittels sendTo funktionieren wieder."
|
|
60
69
|
}
|
|
61
70
|
},
|
|
62
71
|
"titleLang": {
|
|
@@ -308,6 +317,18 @@
|
|
|
308
317
|
"def": false
|
|
309
318
|
}
|
|
310
319
|
},
|
|
320
|
+
{
|
|
321
|
+
"_id": "info.configUpdateProcessed",
|
|
322
|
+
"type": "state",
|
|
323
|
+
"common": {
|
|
324
|
+
"name": "If a config update was processed",
|
|
325
|
+
"type": "boolean",
|
|
326
|
+
"read": true,
|
|
327
|
+
"write": false,
|
|
328
|
+
"role": "state",
|
|
329
|
+
"def": false
|
|
330
|
+
}
|
|
331
|
+
},
|
|
311
332
|
{
|
|
312
333
|
"_id": "info.readyForClients",
|
|
313
334
|
"type": "state",
|
package/lib/modules/history.js
CHANGED
|
@@ -221,6 +221,7 @@ class HistoryModule {
|
|
|
221
221
|
constructor(options) {
|
|
222
222
|
this.adapter = options.adapter;
|
|
223
223
|
this.entityData = options.entityData;
|
|
224
|
+
this.personModule = options.personModule;
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
async processRequest(req, res) {
|
|
@@ -233,7 +234,7 @@ class HistoryModule {
|
|
|
233
234
|
entities.push(entity || id);
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
const newResult = await getHistory(this.adapter, entities, new Date(req.params.start).getTime(), new Date(req.query.end_time).getTime(), req.query.noAttributes,
|
|
237
|
+
const newResult = await getHistory(this.adapter, entities, new Date(req.params.start).getTime(), new Date(req.query.end_time).getTime(), req.query.noAttributes, this.personModule.getUserIDFromName(req._user));
|
|
237
238
|
const oldResult = [];
|
|
238
239
|
for (const [entity_id, states] of Object.entries(newResult)) {
|
|
239
240
|
const entityResult = [];
|
|
@@ -307,7 +308,7 @@ class HistoryModule {
|
|
|
307
308
|
entities.push(entity || id);
|
|
308
309
|
}
|
|
309
310
|
|
|
310
|
-
const historyData = await getHistory(this.adapter, entities, parameters.startTime, Date.now(), parameters.noAttributes, this.
|
|
311
|
+
const historyData = await getHistory(this.adapter, entities, parameters.startTime, Date.now(), parameters.noAttributes, this.personModule.getUserIDFromName(ws.__auth?.username));
|
|
311
312
|
sendHistoryResponse(ws, message.id, historyData, parameters);
|
|
312
313
|
return true;
|
|
313
314
|
}
|
package/lib/modules/person.js
CHANGED
|
@@ -28,24 +28,36 @@ class PersonModule {
|
|
|
28
28
|
* @returns {string}
|
|
29
29
|
*/
|
|
30
30
|
getUserIDFromName(name) {
|
|
31
|
+
if (!this.adapter.config.auth || name === this.adapter.config.defaultUser) {
|
|
32
|
+
return this.adapter.config.defaultUser;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof name !== 'string') { //prevent error if no user supplied.
|
|
36
|
+
throw new Error('Username supplied is not a string, can not find id: ' + name);
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
for (const userObj of Object.values(this.usersCache)) {
|
|
32
40
|
if (userObj.name === name) {
|
|
33
41
|
return userObj.iobId;
|
|
34
42
|
}
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
//fallback, default user "admin" is misleading, if user renamed admin.. hm. :-/
|
|
38
|
-
if (this.adapter.config.auth && name === 'admin') {
|
|
39
|
-
return 'system.user.admin';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
45
|
this.adapter.log.warn(`Could not get user id for ${name} - Trying with username` + JSON.stringify(this.usersCache['system.user.' + name.toLowerCase()]));
|
|
43
46
|
return 'system.user.' + name.toLowerCase(); //hack and not correct since js-controller 3.2
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Get username for id. Needed for default user -> name conversion, for example.
|
|
51
|
+
* @param {string} id
|
|
52
|
+
* @returns {string | undefined}
|
|
53
|
+
*/
|
|
54
|
+
getUserNameFromID(id) {
|
|
55
|
+
return this.usersCache[id]?.name;
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
onObjectChange(id, obj) {
|
|
47
59
|
if (id.startsWith('system.user.')) {
|
|
48
|
-
if (obj && obj.common && obj.common.enabled
|
|
60
|
+
if (obj && obj.common && obj.common.enabled) {
|
|
49
61
|
this.usersCache[id] = {
|
|
50
62
|
iobId: obj._id,
|
|
51
63
|
name: obj.common.name || '',
|
|
@@ -62,6 +74,9 @@ class PersonModule {
|
|
|
62
74
|
|
|
63
75
|
async init() {
|
|
64
76
|
const userObjects = await this.adapter.getForeignObjectsAsync('system.user.*', 'user');
|
|
77
|
+
if (!this.adapter.config.defaultUser.startsWith('system.user.')) { //augment default user to be full id here once.
|
|
78
|
+
this.adapter.config.defaultUser = `system.user.${this.adapter.config.defaultUser}`;
|
|
79
|
+
}
|
|
65
80
|
let defaultUserObject = null;
|
|
66
81
|
for (const [id, obj] of Object.entries(userObjects)) {
|
|
67
82
|
if (obj.common && obj.common.enabled) { //only show enabled persons?
|
|
@@ -72,7 +87,7 @@ class PersonModule {
|
|
|
72
87
|
picture: obj.common.icon,
|
|
73
88
|
description: obj.common.desc
|
|
74
89
|
};
|
|
75
|
-
if (
|
|
90
|
+
if (id === this.adapter.config.defaultUser) {
|
|
76
91
|
defaultUserObject = obj;
|
|
77
92
|
}
|
|
78
93
|
}
|
|
@@ -80,16 +95,14 @@ class PersonModule {
|
|
|
80
95
|
|
|
81
96
|
//default user only relevant for !auth.
|
|
82
97
|
if (!defaultUserObject) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.adapter.log.warn(`Could not find default user ${this.adapter.config.defaultUser} - Using admin - Please update your configuration.`);
|
|
98
|
+
if (!defaultUserObject && !this.adapter.config.auth) {
|
|
99
|
+
const configuredUserNotFound = `Could not find default user ${this.adapter.config.defaultUser}. Please update your configuration.`;
|
|
100
|
+
this.adapter.log.error(configuredUserNotFound);
|
|
101
|
+
|
|
102
|
+
throw new Error(configuredUserNotFound);
|
|
89
103
|
}
|
|
90
104
|
}
|
|
91
105
|
|
|
92
|
-
|
|
93
106
|
await this.adapter.subscribeObjectsAsync('system.user.*');
|
|
94
107
|
}
|
|
95
108
|
}
|
package/lib/server.js
CHANGED
|
@@ -165,10 +165,6 @@ class WebServer {
|
|
|
165
165
|
adapter: this.adapter,
|
|
166
166
|
objects: this._objectData.objects
|
|
167
167
|
}),
|
|
168
|
-
history: new HistoryModule({
|
|
169
|
-
adapter: this.adapter,
|
|
170
|
-
entityData: entityData
|
|
171
|
-
}),
|
|
172
168
|
conversation: new ConversationModule({
|
|
173
169
|
adapter: this.adapter,
|
|
174
170
|
sendResponse: this._sendResponse,
|
|
@@ -198,6 +194,11 @@ class WebServer {
|
|
|
198
194
|
adapter: this.adapter
|
|
199
195
|
})
|
|
200
196
|
};
|
|
197
|
+
this._modules.history = new HistoryModule({
|
|
198
|
+
adapter: this.adapter,
|
|
199
|
+
entityData: entityData,
|
|
200
|
+
personModule: this._modules.person
|
|
201
|
+
});
|
|
201
202
|
|
|
202
203
|
this.converter = {
|
|
203
204
|
[Types.socket]: converterSwitch.processSocket,
|
|
@@ -276,6 +277,13 @@ class WebServer {
|
|
|
276
277
|
}
|
|
277
278
|
this.adapter.setState('info.readyForClients', true, true);
|
|
278
279
|
this.log.debug('Initialization done.');
|
|
280
|
+
}).catch((err) => {
|
|
281
|
+
this.log.error(`Initialization error: ${err}`);
|
|
282
|
+
if (typeof this.adapter.terminate === 'function') {
|
|
283
|
+
this.adapter.terminate(utils.EXIT_CODES.INVALID_ADAPTER_CONFIG);
|
|
284
|
+
} else {
|
|
285
|
+
process.exit(utils.EXIT_CODES.INVALID_ADAPTER_CONFIG);
|
|
286
|
+
}
|
|
279
287
|
});
|
|
280
288
|
}
|
|
281
289
|
|
|
@@ -297,6 +305,7 @@ class WebServer {
|
|
|
297
305
|
}
|
|
298
306
|
await this._getAllStates();
|
|
299
307
|
await this._manageSubscribesFromConfig();
|
|
308
|
+
this.log.debug('entitiesUpdated for startup.');
|
|
300
309
|
await this.adapter.setStateAsync('info.entitiesUpdated', true, true);
|
|
301
310
|
}
|
|
302
311
|
|
|
@@ -525,7 +534,7 @@ class WebServer {
|
|
|
525
534
|
}
|
|
526
535
|
|
|
527
536
|
async _processSingleCall(ws, data, entity_id) {
|
|
528
|
-
const user = this._modules.person.getUserIDFromName(ws.__auth
|
|
537
|
+
const user = this._modules.person.getUserIDFromName(ws.__auth?.username);
|
|
529
538
|
|
|
530
539
|
const entity = entityData.entityId2Entity[entity_id];
|
|
531
540
|
const id = entity.context.STATE.setId;
|
|
@@ -696,7 +705,7 @@ class WebServer {
|
|
|
696
705
|
entity.state = 'unknown';
|
|
697
706
|
if (entity.context.STATE && entity.context.STATE.getId) {
|
|
698
707
|
try {
|
|
699
|
-
const user = this.
|
|
708
|
+
const user = this.config.defaultUser;
|
|
700
709
|
const state = await this.adapter.getForeignStateAsync(entity.context.STATE.getId, {user});
|
|
701
710
|
if (state) {
|
|
702
711
|
await this.onStateChange(entity.context.STATE.getId, state);
|
|
@@ -1495,7 +1504,7 @@ class WebServer {
|
|
|
1495
1504
|
file = file.substring(0, pos);
|
|
1496
1505
|
}
|
|
1497
1506
|
try {
|
|
1498
|
-
const user = this._modules.person.getUserIDFromName(
|
|
1507
|
+
const user = this._modules.person.getUserIDFromName(req._user);
|
|
1499
1508
|
let data;
|
|
1500
1509
|
if (file.startsWith('/lovelace/')) {
|
|
1501
1510
|
file = file.replace('/lovelace/', '');
|
|
@@ -1519,7 +1528,7 @@ class WebServer {
|
|
|
1519
1528
|
file = file.substring(0, pos);
|
|
1520
1529
|
}
|
|
1521
1530
|
try {
|
|
1522
|
-
const user = this._modules.person.getUserIDFromName(
|
|
1531
|
+
const user = this._modules.person.getUserIDFromName(req._user);
|
|
1523
1532
|
const data = await this.adapter.readFileAsync(this.adapter.namespace, file.replace('/local/custom_ui/', '/cards/'), {user});
|
|
1524
1533
|
const pos = req.url.lastIndexOf('.');
|
|
1525
1534
|
res.setHeader('content-type', (mime.getType || mime.lookup).call(data.mimeType, file.substring(pos + 1).toLowerCase()));
|
|
@@ -1864,15 +1873,17 @@ class WebServer {
|
|
|
1864
1873
|
}
|
|
1865
1874
|
}
|
|
1866
1875
|
}
|
|
1867
|
-
} else {
|
|
1868
|
-
req._user = this.config.defaultUser;
|
|
1869
1876
|
}
|
|
1870
1877
|
|
|
1878
|
+
//if no auth or page does not require auth, use default user:
|
|
1879
|
+
//invalid token run into return above and close connection.
|
|
1880
|
+
req._user = req._user || this.config.defaultUser;
|
|
1881
|
+
|
|
1871
1882
|
next();
|
|
1872
1883
|
}
|
|
1873
1884
|
});
|
|
1874
1885
|
|
|
1875
|
-
//handle local images
|
|
1886
|
+
//handle local images that are content of some states:
|
|
1876
1887
|
this._app.use(async (req, res, next) => {
|
|
1877
1888
|
if (this._requestableFiles.includes(req.url)) {
|
|
1878
1889
|
if (!req._user) { //sadly frontend does not send auth info with most request.... :-/
|
|
@@ -1925,7 +1936,7 @@ class WebServer {
|
|
|
1925
1936
|
if (obj && obj.common.type === 'file') {
|
|
1926
1937
|
contentType = (mime.getType || mime.lookup).call(mime, fileName[0]);
|
|
1927
1938
|
}
|
|
1928
|
-
const user = this._modules.person.getUserIDFromName(
|
|
1939
|
+
const user = this._modules.person.getUserIDFromName(req._user);
|
|
1929
1940
|
const data = await this.adapter.getBinaryStateAsync(fileName[0], {user});
|
|
1930
1941
|
if (data !== null && obj !== undefined) {
|
|
1931
1942
|
if (data && typeof data === 'object' && data.val !== undefined && data.ack !== undefined) {
|
|
@@ -1994,7 +2005,7 @@ class WebServer {
|
|
|
1994
2005
|
return res.status(404).json({error: 'Start or end misformated'});
|
|
1995
2006
|
}
|
|
1996
2007
|
|
|
1997
|
-
const user = this._modules.person.getUserIDFromName(
|
|
2008
|
+
const user = this._modules.person.getUserIDFromName(req._user);
|
|
1998
2009
|
try {
|
|
1999
2010
|
const state = await this.adapter.getForeignStateAsync(entity.context.STATE.getId, {user});
|
|
2000
2011
|
if (state && state.val) {
|
|
@@ -2076,10 +2087,10 @@ class WebServer {
|
|
|
2076
2087
|
}
|
|
2077
2088
|
|
|
2078
2089
|
async _getCurrentUser(ws) {
|
|
2079
|
-
const user = this._modules.person.getUserIDFromName(ws.__auth.username
|
|
2090
|
+
const user = this._modules.person.getUserIDFromName(ws.__auth.username);
|
|
2080
2091
|
const userObj = {
|
|
2081
2092
|
id: user,
|
|
2082
|
-
name: ws.__auth.username || this.config.defaultUser,
|
|
2093
|
+
name: ws.__auth.username || this._modules.person.getUserNameFromID(this.config.defaultUser),
|
|
2083
2094
|
is_owner: user === 'system.user.admin',
|
|
2084
2095
|
is_admin: user === 'system.user.admin',
|
|
2085
2096
|
credentials: [{auth_provider_type: 'iobroker', auth_provider_id: null}],
|
|
@@ -2165,7 +2176,7 @@ class WebServer {
|
|
|
2165
2176
|
throw new Error('no entity found');
|
|
2166
2177
|
} else {
|
|
2167
2178
|
let id;
|
|
2168
|
-
let userName
|
|
2179
|
+
let userName; // will be ignored in case of no authentication enabled.
|
|
2169
2180
|
if (this.config.auth !== false && (token || access_token)) {
|
|
2170
2181
|
if (access_token) {
|
|
2171
2182
|
const now = Date.now();
|
|
@@ -2669,7 +2680,8 @@ class WebServer {
|
|
|
2669
2680
|
if (id === `${this.adapter.namespace}.configuration`) {
|
|
2670
2681
|
this._lovelaceConfig = obj.native;
|
|
2671
2682
|
await this._manageSubscribesFromConfig();
|
|
2672
|
-
|
|
2683
|
+
this.log.debug(`configUpdateProcessed for config.`);
|
|
2684
|
+
await this.adapter.setStateAsync('info.configUpdateProcessed', true, true);
|
|
2673
2685
|
} else if (id === 'system.config') {
|
|
2674
2686
|
if (obj && obj.common) {
|
|
2675
2687
|
this.lang = obj.common.language || this.lang || 'en';
|
|
@@ -2677,6 +2689,7 @@ class WebServer {
|
|
|
2677
2689
|
this.systemConfig = obj.common;
|
|
2678
2690
|
this._updateConstantEntities();
|
|
2679
2691
|
this.log.debug(`${id} -> config updated, constant entities updated.`);
|
|
2692
|
+
this.log.debug('entitiesUpdated for system.config.');
|
|
2680
2693
|
await this.adapter.setStateAsync('info.entitiesUpdated', true, true);
|
|
2681
2694
|
}
|
|
2682
2695
|
} else {
|
|
@@ -2753,6 +2766,7 @@ class WebServer {
|
|
|
2753
2766
|
for (const id of idsWithUpdate) {
|
|
2754
2767
|
await this.onStateChange(id, null, true);
|
|
2755
2768
|
}
|
|
2769
|
+
this.log.debug('entitiesUpdated for object changes.');
|
|
2756
2770
|
await this.adapter.setStateAsync('info.entitiesUpdated', true, true);
|
|
2757
2771
|
this.log.debug('Had changes, updated states and notified entitiesUpdated state.');
|
|
2758
2772
|
}
|