iobroker.lovelace 4.0.12 → 4.1.1

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 CHANGED
@@ -473,6 +473,15 @@ After that checkout modified version in `./build` folder. Then.
473
473
  PLACEHOLDER for next version:
474
474
  ### **WORK IN PROGRESS**
475
475
  -->
476
+ ### 4.1.1 (2024-01-02)
477
+ * (Garfonso) changed: determining user id
478
+ * (Garfosno) changed: history attributes handling
479
+ * (Garfonso) added: handle browser_mod/recall_id service call.
480
+ * (Garfonso) changed: all states are strings (fixes #483)
481
+
482
+ ### 4.1.0 (2023-12-18)
483
+ * (Garfons) add an option to show users on login screen (off by default)
484
+
476
485
  ### 4.0.12 (2023-12-15)
477
486
  * (Garfonso) fix lint errors
478
487
 
@@ -485,15 +494,9 @@ After that checkout modified version in `./build` folder. Then.
485
494
  * (Garfonso) fix: login & authorization
486
495
  * (Garfonso) added: user images & names on login screen
487
496
 
488
- ### 4.0.9 (2023-12-12)
489
- * (Garfonso) fixed: timestamp in legacy history data
490
-
491
- ### 4.0.8 (2023-12-12)
492
- * (Garfonso) re-add legacy history for custom cards
493
-
494
497
  ## License
495
498
 
496
- Copyright 2019-2023, bluefox <dogafox@gmail.com>
499
+ Copyright 2019-2024, bluefox <dogafox@gmail.com>
497
500
 
498
501
  Licensed under the Apache License, Version 2.0 (the "License");
499
502
  you may not use this file except in compliance with the License.
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Wenn Sie HTTPS nicht aktivieren oder andere Mittel zur Verschlüsselung der Kommunikation verwenden, wird Ihr Passwort unverschlüsselt über das Netzwerk gesendet. Öffnen Sie nicht einfach einen Port für Lovelace!",
178
178
  "Warning!": "Warnung!",
179
179
  "Ignore warning": "Warnung ignorieren",
180
- "Disable authentication": "Deaktivieren Sie die Authentifizierung"
180
+ "Disable authentication": "Deaktivieren Sie die Authentifizierung",
181
+ "showUsersOnLoginScreen": "Benutzer auf dem Anmeldebildschirm anzeigen"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "If you don't enable HTTPS or use other means to encrypt communication, your password will be sent unencrypted over the network. Do not just open a port for lovelace!",
178
178
  "Warning!": "Warning!",
179
179
  "Ignore warning": "Ignore warning",
180
- "Disable authentication": "Disable authentication"
180
+ "Disable authentication": "Disable authentication",
181
+ "showUsersOnLoginScreen": "Show users on login screen"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Si no habilita HTTPS o utiliza otros medios para cifrar la comunicación, su contraseña se enviará sin cifrar a través de la red. ¡No se limite a abrir un puerto para Lovelace!",
178
178
  "Warning!": "¡Advertencia!",
179
179
  "Ignore warning": "ignorar advertencia",
180
- "Disable authentication": "Deshabilitar la autenticación"
180
+ "Disable authentication": "Deshabilitar la autenticación",
181
+ "showUsersOnLoginScreen": "Mostrar usuarios en la pantalla de inicio de sesión"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Si vous n'activez pas HTTPS ou n'utilisez pas d'autres moyens pour crypter la communication, votre mot de passe sera envoyé en clair sur le réseau. Ne vous contentez pas d'ouvrir un port pour Lovelace !",
178
178
  "Warning!": "Avertissement!",
179
179
  "Ignore warning": "Ignorer l'avertissement",
180
- "Disable authentication": "Désactiver l'authentification"
180
+ "Disable authentication": "Désactiver l'authentification",
181
+ "showUsersOnLoginScreen": "Afficher les utilisateurs sur l'écran de connexion"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Se non abiliti HTTPS o non utilizzi altri mezzi per crittografare la comunicazione, la tua password verrà inviata non crittografata sulla rete. Non limitarti ad aprire una porta per l'amore!",
178
178
  "Warning!": "Avvertimento!",
179
179
  "Ignore warning": "Ignora l'avviso",
180
- "Disable authentication": "Disabilita l'autenticazione"
180
+ "Disable authentication": "Disabilita l'autenticazione",
181
+ "showUsersOnLoginScreen": "Mostra gli utenti nella schermata di accesso"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Als u HTTPS niet inschakelt of andere middelen gebruikt om de communicatie te versleutelen, wordt uw wachtwoord onversleuteld over het netwerk verzonden. Open niet zomaar een poort voor lovelace!",
178
178
  "Warning!": "Waarschuwing!",
179
179
  "Ignore warning": "Negeer waarschuwing",
180
- "Disable authentication": "Schakel authenticatie uit"
180
+ "Disable authentication": "Schakel authenticatie uit",
181
+ "showUsersOnLoginScreen": "Toon gebruikers op inlogscherm"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Jeśli nie włączysz protokołu HTTPS lub nie użyjesz innych sposobów szyfrowania komunikacji, Twoje hasło zostanie przesłane przez sieć w formie niezaszyfrowanej. Nie otwieraj portu tylko dla lovelace!",
178
178
  "Warning!": "Ostrzeżenie!",
179
179
  "Ignore warning": "Zignoruj ​​ostrzeżenie",
180
- "Disable authentication": "Wyłącz uwierzytelnianie"
180
+ "Disable authentication": "Wyłącz uwierzytelnianie",
181
+ "showUsersOnLoginScreen": "Pokaż użytkowników na ekranie logowania"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Se você não ativar o HTTPS ou usar outros meios para criptografar a comunicação, sua senha será enviada sem criptografia pela rede. Não basta abrir uma porta para lovelace!",
178
178
  "Warning!": "Aviso!",
179
179
  "Ignore warning": "Ignorar aviso",
180
- "Disable authentication": "Desabilitar autenticação"
180
+ "Disable authentication": "Desabilitar autenticação",
181
+ "showUsersOnLoginScreen": "Mostrar usuários na tela de login"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Если вы не включите HTTPS или не используете другие средства шифрования связи, ваш пароль будет отправлен по сети в незашифрованном виде. Не открывайте порт просто так для ловеласа!",
178
178
  "Warning!": "Предупреждение!",
179
179
  "Ignore warning": "Игнорировать предупреждение",
180
- "Disable authentication": "Отключить аутентификацию"
180
+ "Disable authentication": "Отключить аутентификацию",
181
+ "showUsersOnLoginScreen": "Показывать пользователей на экране входа в систему"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "Якщо ви не ввімкнете HTTPS або не використаєте інші засоби шифрування зв’язку, ваш пароль буде надіслано через мережу в незашифрованому вигляді. Не просто відкривайте порт для ловеласа!",
178
178
  "Warning!": "УВАГА!",
179
179
  "Ignore warning": "Ігнорувати попередження",
180
- "Disable authentication": "Вимкнути автентифікацію"
180
+ "Disable authentication": "Вимкнути автентифікацію",
181
+ "showUsersOnLoginScreen": "Показати користувачів на екрані входу"
181
182
  }
@@ -177,5 +177,6 @@
177
177
  "Unsecure_Auth": "如果您未启用 HTTPS 或使用其他方式加密通信,您的密码将通过网络以未加密的方式发送。不要只为lovelace打开一个端口!",
178
178
  "Warning!": "警告!",
179
179
  "Ignore warning": "忽略警告",
180
- "Disable authentication": "禁用身份验证"
180
+ "Disable authentication": "禁用身份验证",
181
+ "showUsersOnLoginScreen": "在登录屏幕上显示用户"
181
182
  }
@@ -288,8 +288,11 @@
288
288
  $('.tab-login').removeClass('disabled');
289
289
  $('#defaultUser').val('admin');
290
290
  $('.col-defaultUser').hide();
291
+ $('.col-showUsersOnLoginScreen').show();
292
+ $('#showUsersOnLoginScreen').show();
291
293
  $('.col-ttl').show();
292
294
 
295
+
293
296
  if ((id === 'auth' || id === 'secure') && !$secure.prop('checked')) {
294
297
  confirmMessage(_('Unsecure_Auth'), _('Warning!'), 'security', [_('Ignore warning'), _('Disable authentication')], function (result) {
295
298
  if (result === 1) {
@@ -301,7 +304,9 @@
301
304
  } else {
302
305
  $('.tab-login').addClass('disabled');
303
306
  $('.col-defaultUser').show();
304
- //$('.col-ttl').hide();
307
+ $('.col-showUsersOnLoginScreen').hide();
308
+ $('#showUsersOnLoginScreen').hide();
309
+ $('.col-ttl').hide();
305
310
  }
306
311
  if ($('#loginBackgroundImage').prop('checked')) {
307
312
  $('.background').show();
@@ -748,10 +753,6 @@
748
753
  </div>
749
754
  </div>
750
755
  <div class="row">
751
- <div class="input-field col s12 m6 l2 col-ttl">
752
- <input class="value" type="number" id="ttl" />
753
- <label class="translate" for="ttl">Login timeout(sec)</label>
754
- </div>
755
756
  <div class="input-field col s12 m6 l2">
756
757
  <input class="value" id="auth" type="checkbox" />
757
758
  <label class="translate" for="auth">Authentication</label>
@@ -760,6 +761,14 @@
760
761
  <select class="value" id="defaultUser"></select>
761
762
  <label class="translate" for="defaultUser">Run as</label>
762
763
  </div>
764
+ <div class="input-field col s12 m6 l2 col-ttl">
765
+ <input class="value" type="number" id="ttl" />
766
+ <label class="translate" for="ttl">Login timeout(sec)</label>
767
+ </div>
768
+ <div class="input-field col s12 m6 l2 col-showUsersOnLoginScreen">
769
+ <input class="value" id="showUsersOnLoginScreen" type="checkbox" />
770
+ <label class="translate" for="showUsersOnLoginScreen">showUsersOnLoginScreen</label>
771
+ </div>
763
772
  </div>
764
773
  <div class="row">
765
774
  <div class="input-field col s12 m6 l2 col-language">
package/admin/words.js CHANGED
@@ -187,6 +187,7 @@ const lovelace_systemDictionary = {
187
187
  "Warning!": { "en": "Warning!", "de": "Warnung!", "ru": "Предупреждение!", "pt": "Aviso!", "nl": "Waarschuwing!", "fr": "Avertissement!", "it": "Avvertimento!", "es": "¡Advertencia!", "pl": "Ostrzeżenie!", "uk": "УВАГА!", "zh-cn": "警告!"},
188
188
  "Ignore warning": { "en": "Ignore warning", "de": "Warnung ignorieren", "ru": "Игнорировать предупреждение", "pt": "Ignorar aviso", "nl": "Negeer waarschuwing", "fr": "Ignorer l'avertissement", "it": "Ignora l'avviso", "es": "ignorar advertencia", "pl": "Zignoruj ​​ostrzeżenie", "uk": "Ігнорувати попередження", "zh-cn": "忽略警告"},
189
189
  "Disable authentication": { "en": "Disable authentication", "de": "Deaktivieren Sie die Authentifizierung", "ru": "Отключить аутентификацию", "pt": "Desabilitar autenticação", "nl": "Schakel authenticatie uit", "fr": "Désactiver l'authentification", "it": "Disabilita l'autenticazione", "es": "Deshabilitar la autenticación", "pl": "Wyłącz uwierzytelnianie", "uk": "Вимкнути автентифікацію", "zh-cn": "禁用身份验证"},
190
+ "showUsersOnLoginScreen": { "en": "Show users on login screen", "de": "Benutzer auf dem Anmeldebildschirm anzeigen", "ru": "Показывать пользователей на экране входа в систему", "pt": "Mostrar usuários na tela de login", "nl": "Toon gebruikers op inlogscherm", "fr": "Afficher les utilisateurs sur l'écran de connexion", "it": "Mostra gli utenti nella schermata di accesso", "es": "Mostrar usuarios en la pantalla de inicio de sesión", "pl": "Pokaż użytkowników na ekranie logowania", "uk": "Показати користувачів на екрані входу", "zh-cn": "在登录屏幕上显示用户"},
190
191
  };
191
192
 
192
193
  if (typeof module !== 'undefined' && module.parent) { module.exports = lovelace_systemDictionary; }
package/io-package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "lovelace",
4
- "version": "4.0.12",
4
+ "version": "4.1.1",
5
5
  "news": {
6
+ "4.1.1": {
7
+ "en": "changed: determining user id\nchanged: history attributes handling\nadded: handle browser_mod/recall_id service call.\nchanged: all states are strings!",
8
+ "de": "geändert: bestimmung der benutzer-id\ngeändert: historie für attribute\nhinzugefügt: handle browser_mod/recall_id service call.\ngeändert: alle zustände sind strings!"
9
+ },
10
+ "4.1.0": {
11
+ "en": "add an option to show users on login screen (off by default)",
12
+ "de": "Neu Option um Benutzer auf dem Login-Bildschirm anzuzeigen"
13
+ },
6
14
  "4.0.12": {
7
15
  "en": "fix lint errors",
8
16
  "de": "lint fehler beheben",
@@ -40,23 +48,6 @@
40
48
  "4.0.8": {
41
49
  "en": "re-add legacy history for custom cards",
42
50
  "de": "re-add die alten history methoden für benutzerdefinierte karten"
43
- },
44
- "4.0.7": {
45
- "en": "history should be working again.",
46
- "de": "history sollte wieder funktionieren.",
47
- "ru": "история должна работать снова.",
48
- "pt": "a história deve estar a trabalhar de novo.",
49
- "nl": "de geschiedenis zou weer moeten werken.",
50
- "fr": "l'histoire devrait retravailler.",
51
- "it": "la storia dovrebbe funzionare di nuovo.",
52
- "es": "la historia debe estar trabajando de nuevo.",
53
- "pl": "historia powinna być kontynuowana.",
54
- "uk": "історія повинна працювати знову.",
55
- "zh-cn": "历史应该再次发挥作用。."
56
- },
57
- "4.0.6": {
58
- "en": "fixed: thermostat card for thermostats without mode / off state\nfixed: history and history graph missing for some stuff",
59
- "de": "repariert: thermostatkarte für thermostate ohne modus / aus zustand\nrepariert: history und history diagramm fehlt für einige sachen"
60
51
  }
61
52
  },
62
53
  "title": "Visualization with Lovelace-UI",
@@ -209,6 +200,7 @@
209
200
  "defaultUser": "admin",
210
201
  "onlyAllowWhenUserIsOwner": false,
211
202
  "ttl": 3600,
203
+ "showUsersOnLoginScreen": false,
212
204
  "themes": "",
213
205
  "defaultTheme": "default",
214
206
  "defaultThemeDark": "default",
@@ -237,11 +237,15 @@ function fillClimateEntityFromStates(states, objects, entity, iobType) {
237
237
 
238
238
  //controls hvac_mode which can be 'off' but not 'on', so translate 'on' to heat / cool depending on type.
239
239
  if (states.state || states.stateRead) {
240
+ if (!entity.attributes.hvac_modes) {
241
+ entity.attributes.hvac_modes = [];
242
+ }
243
+ entity.attributes.hvac_modes.push('off');
240
244
  if (!states.hvac_mode) {
241
245
  if (iobType === typeDetector.Types.airCondition) {
242
- entity.attributes.hvac_modes = ['off', 'cool'];
246
+ entity.attributes.hvac_modes.push('cool');
243
247
  } else {
244
- entity.attributes.hvac_modes = ['off', 'heat'];
248
+ entity.attributes.hvac_modes.push('heat');
245
249
  }
246
250
  }
247
251
  entity.context.STATE.getParser = function (entity, attr, state) {
@@ -48,8 +48,6 @@ exports.numericDeviceClasses = [
48
48
  'distance'
49
49
  ];
50
50
 
51
- const numericDeviceClassesIob = exports.numericDeviceClasses.concat(['timestamp']);
52
-
53
51
  exports.iobState2EntityState = function (entity, val, attribute) {
54
52
  let type = entity.context.type;
55
53
  const pos = type.lastIndexOf('.');
@@ -61,14 +59,17 @@ exports.iobState2EntityState = function (entity, val, attribute) {
61
59
  return val ? 'on' : 'off';
62
60
  } else if (type === 'binary_sensor') {
63
61
  return val ? 'on' : 'off';
64
- } else if (typeof val === 'number' && entity.attributes && (numericDeviceClassesIob.includes(entity.attributes.device_class) || entity.attributes.unit_of_measurement)) {
65
- return val; //do not convert timestamps or values that have a unit of measurement..
66
- } else if (typeof val === 'number' && entity.attributes && ['date'].includes(entity.attributes.device_class)) {
67
- const dateStr = new Date(val).toDateString(); //convert to date string
62
+ }else if (typeof val === 'number' && entity.attributes && ['date', 'timestamp'].includes(entity.attributes.device_class)) {
63
+ //convert to date string
64
+ const date = new Date(val);
65
+ let dateStr = date.toDateString();
66
+ if (attribute === 'timestamp') {
67
+ dateStr = date.toISOString();
68
+ }
68
69
  return dateStr === 'Invalid Date' ? 'unknown' : dateStr;
69
70
  } else if (type === 'lock') {
70
71
  return val ? 'unlocked' : 'locked';
71
- } else if (typeof val === 'boolean' && type !== 'media_player') {
72
+ } else if (typeof val === 'boolean' && type !== 'media_player' && !attribute) { //attributes can have true/false.
72
73
  return val ? 'on' : 'off';
73
74
  } else if (typeof val === 'number' && entity.context.STATE.map2lovelace) {
74
75
  return entity.context.STATE.map2lovelace[val] || val;
@@ -344,6 +344,7 @@ class BrowserModModule {
344
344
  instance: message.browserID,
345
345
  ws
346
346
  };
347
+ ws.browserID = message.browserID; //store browserID in ws object to handle recall service call later.
347
348
 
348
349
  ws.send(JSON.stringify([{id: message.id, type: 'result', success: true, result: null}, {
349
350
  id: message.id, type: 'event', event: {
@@ -399,6 +400,8 @@ class BrowserModModule {
399
400
  this.adapter.log.debug('Updated browser_mod settings: ' + message.key + ' to ' + message.value);
400
401
  }
401
402
  //think about making this permanent somehow? -> but only if we find a way to allow browser_mod_settings panel.
403
+ } else if (method === 'recall_id') {
404
+ ws.send(JSON.stringify({id: message.id, type: 'result', success: true, result: ws.browserID}));
402
405
  } else {
403
406
  this.adapter.log.warn('Unknown browser_mod method: ' + JSON.stringify(message));
404
407
  }
@@ -67,7 +67,7 @@ async function getHistory(adapter, entities, start, end, noAttributes, user) {
67
67
  }
68
68
  let found = false;
69
69
  let best = null;
70
- let bestDiff = 1000;
70
+ let bestDiff = 10000;
71
71
  const results = attributesResult[attribute.attribute].result;
72
72
  for (const result of results) {
73
73
  if (result.val !== null) {
@@ -81,10 +81,10 @@ async function getHistory(adapter, entities, start, end, noAttributes, user) {
81
81
  }
82
82
  if (found) {
83
83
  attributeValues[attribute.attribute] = typeof attribute.historyParser === 'function' ? attribute.historyParser(id, best.val) : best.val;
84
- best.used = true;
85
- } else {
84
+ best.used = true; //will be checked later, all "unused" attributes will be added without state later.
85
+ } /*else { //let's try to leave attribute empty, for now, if no value in history that is closer than 10 seconds.
86
86
  attributeValues[attribute.attribute] = entity.attributes[attribute.attribute]; //use current value as default if none found.
87
- }
87
+ }*/
88
88
  }
89
89
  }
90
90
  for (const key of Object.keys(entity.attributes)) {
@@ -103,7 +103,7 @@ async function getHistory(adapter, entities, start, end, noAttributes, user) {
103
103
  s: typeof entity.context.STATE.historyParser === 'function' ?
104
104
  entity.context.STATE.historyParser(id, e.val).toString() :
105
105
  iobState2EntityState(entity, e.val),
106
- a: noAttributes ? undefined : getAttributeValues(e, attributesResult, attributesUsed),
106
+ a: noAttributes ? {} : getAttributeValues(e, attributesResult, attributesUsed),
107
107
  lc: 1,
108
108
  lu: 1
109
109
  };
@@ -113,6 +113,8 @@ async function getHistory(adapter, entities, start, end, noAttributes, user) {
113
113
  //add unused attribute values:
114
114
  if (!noAttributes && entity.context.ATTRIBUTES) {
115
115
  for (const attribute of entity.context.ATTRIBUTES) {
116
+ //find other attributes for this type. Will use this attribute as "state" for the getAttributeValues function.
117
+ // so we don't want to find this attribute again. Will add all matching attributes anyway. So in later runs this attribute will be "empty", i.e. all used.
116
118
  attributesUsed.push(attribute.attribute);
117
119
  const results = attributesResult[attribute.attribute].result;
118
120
  for (const result of results) {
@@ -125,7 +127,7 @@ async function getHistory(adapter, entities, start, end, noAttributes, user) {
125
127
  //state: null,
126
128
  lc: 1,
127
129
  lu: 1,
128
- a: noAttributes ? undefined : getAttributeValues(result, attributesResult, attributesUsed, attributeValues)
130
+ a: noAttributes ? {} : getAttributeValues(result, attributesResult, attributesUsed, attributeValues)
129
131
  };
130
132
  updateTimestamps(data, result, true);
131
133
  historyPerEntity.push(data);
@@ -137,6 +139,7 @@ async function getHistory(adapter, entities, start, end, noAttributes, user) {
137
139
  if (historyPerEntity.length === 0) {
138
140
  historyPerEntity.push({
139
141
  s: entity.state,
142
+ a: {},
140
143
  lu: start / 1000
141
144
  });
142
145
  }
@@ -177,12 +180,12 @@ function sendHistoryResponse(ws, id, historyData, parameters) {
177
180
  state.s = 'unknown';
178
181
  }
179
182
 
180
- if (parameters.noAttributes) {
183
+ /*if (parameters.noAttributes) {
181
184
  delete state.a;
182
185
  }
183
186
  if (parameters.minimalResponse) {
184
187
  delete state.lc;
185
- }
188
+ }*/
186
189
  }
187
190
  }
188
191
 
@@ -275,7 +278,7 @@ class HistoryModule {
275
278
  id: Number(message.id)
276
279
  };
277
280
 
278
- // add subscribe here.
281
+ // add subscription here.
279
282
  ws._subscribes.history = ws._subscribes.history || [];
280
283
  ws._subscribes.history.push(parameters);
281
284
  } else if (message.type === 'history/history_during_period') {
@@ -22,6 +22,27 @@ class PersonModule {
22
22
  return result;
23
23
  }
24
24
 
25
+ /**
26
+ * Get the user id from the username
27
+ * @param {string} name
28
+ * @returns {string}
29
+ */
30
+ getUserIDFromName(name) {
31
+ for (const userObj of Object.values(this.usersCache)) {
32
+ if (userObj.name === name) {
33
+ return userObj.iobId;
34
+ }
35
+ }
36
+
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
+ this.adapter.log.warn(`Could not get user id for ${name} - Trying with username` + JSON.stringify(this.usersCache['system.user.' + name.toLowerCase()]));
43
+ return 'system.user.' + name.toLowerCase(); //hack and not correct since js-controller 3.2
44
+ }
45
+
25
46
  onObjectChange(id, obj) {
26
47
  if (id.startsWith('system.user.')) {
27
48
  if (obj && obj.common && obj.common.enabled()) {
@@ -41,6 +62,7 @@ class PersonModule {
41
62
 
42
63
  async init() {
43
64
  const userObjects = await this.adapter.getForeignObjectsAsync('system.user.*', 'user');
65
+ let defaultUserObject = null;
44
66
  for (const [id, obj] of Object.entries(userObjects)) {
45
67
  if (obj.common && obj.common.enabled) { //only show enabled persons?
46
68
  this.usersCache[id] = {
@@ -50,8 +72,24 @@ class PersonModule {
50
72
  picture: obj.common.icon,
51
73
  description: obj.common.desc
52
74
  };
75
+ if (obj.common.name === this.adapter.config.defaultUser) {
76
+ defaultUserObject = obj;
77
+ }
78
+ }
79
+ }
80
+
81
+ //default user only relevant for !auth.
82
+ if (!defaultUserObject) {
83
+ //Ok, we did not find defaultUser object. Might be renamed admin?
84
+ defaultUserObject = userObjects['system.user.admin'];
85
+ this.adapter.config.defaultUser = defaultUserObject.common.name;
86
+ if (this.adapter.config.defaultUser !== 'admin' && !this.adapter.config.auth) {
87
+ //Activating auth will hide this setting. Not sure what to do then... In general: all users will be able to read everything in this case.
88
+ this.adapter.log.warn(`Could not find default user ${this.adapter.config.defaultUser} - Using admin - Please update your configuration.`);
53
89
  }
54
90
  }
91
+
92
+
55
93
  await this.adapter.subscribeObjectsAsync('system.user.*');
56
94
  }
57
95
  }
@@ -181,6 +181,9 @@ class TodoModule {
181
181
  if (state && state.val) {
182
182
  try {
183
183
  todoList.items = JSON.parse(state.val);
184
+ if (!todoList.items || !Array.isArray(todoList.items)) {
185
+ todoList.items = [];
186
+ }
184
187
  //convert legacy items:
185
188
  for (const item of todoList.items) {
186
189
  if (item.name && !item.summary) {
package/lib/server.js CHANGED
@@ -518,7 +518,7 @@ class WebServer {
518
518
  }
519
519
 
520
520
  async _processSingleCall(ws, data, entity_id) {
521
- const user = await this._getUserId(ws.__auth.username || this.config.defaultUser);
521
+ const user = this._modules.person.getUserIDFromName(ws.__auth.username || this.config.defaultUser);
522
522
 
523
523
  const entity = entityData.entityId2Entity[entity_id];
524
524
  const id = entity.context.STATE.setId;
@@ -681,7 +681,7 @@ class WebServer {
681
681
  entity.state = 'unknown';
682
682
  if (entity.context.STATE && entity.context.STATE.getId) {
683
683
  try {
684
- const user = await this._getUserId(this.config.defaultUser); //TODO: why is this always defaultUser?
684
+ const user = this._modules.person.getUserIDFromName(this.config.defaultUser); //TODO: why is this always defaultUser?
685
685
  const state = await this.adapter.getForeignStateAsync(entity.context.STATE.getId, {user});
686
686
  if (state) {
687
687
  await this.onStateChange(entity.context.STATE.getId, state);
@@ -1477,7 +1477,7 @@ class WebServer {
1477
1477
  file = file.substring(0, pos);
1478
1478
  }
1479
1479
  try {
1480
- const user = await this._getUserId(this.config.defaultUser); //TODO: why is this always default user?
1480
+ const user = this._modules.person.getUserIDFromName(this.config.defaultUser); //TODO: why is this always default user?
1481
1481
  let data;
1482
1482
  if (file.startsWith('/lovelace/')) {
1483
1483
  file = file.replace('/lovelace/', '');
@@ -1501,7 +1501,7 @@ class WebServer {
1501
1501
  file = file.substring(0, pos);
1502
1502
  }
1503
1503
  try {
1504
- const user = await this._getUserId(this.config.defaultUser); //TODO: why is this always default user?
1504
+ const user = this._modules.person.getUserIDFromName(this.config.defaultUser); //TODO: why is this always default user?
1505
1505
  const data = await this.adapter.readFileAsync(this.adapter.namespace, file.replace('/local/custom_ui/', '/cards/'), {user});
1506
1506
  const pos = req.url.lastIndexOf('.');
1507
1507
  res.setHeader('content-type', (mime.getType || mime.lookup).call(data.mimeType, file.substring(pos + 1).toLowerCase()));
@@ -1655,7 +1655,7 @@ class WebServer {
1655
1655
  });
1656
1656
 
1657
1657
  this._app.use('/auth/providers', (req, res) => {
1658
- res.json([{id: null, name: 'ioBroker Local', type: 'iobroker', users: this._modules.person.getShorList()}]);
1658
+ res.json([{id: null, name: 'ioBroker Local', type: 'iobroker', users: this.config.showUsersOnLoginScreen ? this._modules.person.getShorList() : undefined}]);
1659
1659
  });
1660
1660
 
1661
1661
  this._app.post('/auth/login_flow', (req, res) => {
@@ -1844,7 +1844,7 @@ class WebServer {
1844
1844
  }
1845
1845
 
1846
1846
  try {
1847
- const user = await this._getUserId(this.config.defaultUser); //TODO: why is this always default user?
1847
+ const user = this._modules.person.getUserIDFromName(this.config.defaultUser); //TODO: why is this always default user?
1848
1848
  const image = await this.adapter.readFileAsync(id, url, {user});
1849
1849
  if (image.file === null || image.file === undefined) {
1850
1850
  throw new Error('File empty');
@@ -1868,7 +1868,7 @@ class WebServer {
1868
1868
  if (obj && obj.common.type === 'file') {
1869
1869
  contentType = (mime.getType || mime.lookup).call(mime, fileName[0]);
1870
1870
  }
1871
- const user = await this._getUserId(this.config.defaultUser);
1871
+ const user = this._modules.person.getUserIDFromName(this.config.defaultUser);
1872
1872
  const data = await this.adapter.getBinaryStateAsync(fileName[0], {user});
1873
1873
  if (data !== null && obj !== undefined) {
1874
1874
  if (data && typeof data === 'object' && data.val !== undefined && data.ack !== undefined) {
@@ -1937,7 +1937,7 @@ class WebServer {
1937
1937
  return res.status(404).json({error: 'Start or end misformated'});
1938
1938
  }
1939
1939
 
1940
- const user = await this._getUserId(this.config.defaultUser);
1940
+ const user = this._modules.person.getUserIDFromName(this.config.defaultUser);
1941
1941
  try {
1942
1942
  const state = await this.adapter.getForeignStateAsync(entity.context.STATE.getId, {user});
1943
1943
  if (state && state.val) {
@@ -2018,29 +2018,8 @@ class WebServer {
2018
2018
  });
2019
2019
  }
2020
2020
 
2021
- async _getUserId(user) {
2022
- let userId = this._userNamesToIds[user];
2023
- if (userId) {
2024
- return userId;
2025
- }
2026
-
2027
- if (typeof this.adapter.getUserID === 'function') {
2028
- try {
2029
- userId = await this.adapter.getUserID(user);
2030
- this._userNamesToIds[user] = userId;
2031
- } catch (err) {
2032
- this.log.warn(`Could not get user id for ${user} - ${err}`);
2033
- }
2034
- }
2035
- if (!userId) {
2036
- this.log.warn(`Could not get user id for ${user} - Trying with username`);
2037
- userId = 'system.user.' + user.toLowerCase(); //hack and not correct since js-controller 3.2
2038
- }
2039
- return userId;
2040
- }
2041
-
2042
2021
  async _getCurrentUser(ws) {
2043
- const user = await this._getUserId(ws.__auth.username || this.config.defaultUser);
2022
+ const user = this._modules.person.getUserIDFromName(ws.__auth.username || this.config.defaultUser);
2044
2023
  const userObj = {
2045
2024
  id: user,
2046
2025
  name: ws.__auth.username || this.config.defaultUser,
@@ -2098,6 +2077,7 @@ class WebServer {
2098
2077
  type: 'event',
2099
2078
  event: {
2100
2079
  event_type: eventType,
2080
+ data: {},
2101
2081
  origin: 'LOCAL',
2102
2082
  time_fired: Date.now() / 1000
2103
2083
  }
@@ -2158,7 +2138,7 @@ class WebServer {
2158
2138
  }
2159
2139
  if (id) {
2160
2140
  try {
2161
- const user = await this._getUserId(userName);
2141
+ const user = this._modules.person.getUserIDFromName(userName);
2162
2142
  const state = await this.adapter.getForeignStateAsync(id, {user});
2163
2143
  if (state && state.val && typeof state.val === 'string') {
2164
2144
  const val = state.val.split('?')[0] || '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.lovelace",
3
- "version": "4.0.12",
3
+ "version": "4.1.1",
4
4
  "description": "With this adapter you can build visualization for ioBroker with Home Assistant Lovelace UI",
5
5
  "author": {
6
6
  "name": "bluefox",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@iobroker/adapter-core": "^3.0.4",
23
- "axios": "^1.6.2",
23
+ "axios": "^1.6.3",
24
24
  "body-parser": "^1.20.2",
25
25
  "express": "^4.18.2",
26
26
  "iobroker.type-detector": "^3.0.5",
@@ -31,7 +31,7 @@
31
31
  "nyc": "^15.1.0",
32
32
  "pinyin": "^3.1.0",
33
33
  "translit-rus-eng": "^1.0.8",
34
- "ws": "^8.15.1"
34
+ "ws": "^8.16.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@alcalzone/release-script": "^3.7.0",
@@ -44,13 +44,13 @@
44
44
  "@types/chai-as-promised": "^7.1.8",
45
45
  "@types/gulp": "^4.0.17",
46
46
  "@types/mocha": "^10.0.6",
47
- "@types/node": "^16.18.68 < 17",
47
+ "@types/node": "^16.18.69 < 17",
48
48
  "@types/proxyquire": "^1.3.31",
49
49
  "@types/sinon": "^17.0.2",
50
50
  "@types/sinon-chai": "^3.2.12",
51
51
  "chai": "^4.3.10",
52
52
  "chai-as-promised": "^7.1.1",
53
- "eslint": "^8.55.0",
53
+ "eslint": "^8.56.0",
54
54
  "gulp": "^4.0.2",
55
55
  "mocha": "^10.2.0",
56
56
  "proxyquire": "^2.1.3",