Gladys V4 - Sensoren in der Oberfläche anzeigen

Hallo zusammen,

Ich wollte eine Nachricht hinterlassen, weil ich ein Problem mit dem Xiaomi-Modul habe.

Wenn man einen Sensor abruft, wird dieser hinzugefügt und ist zum Beispiel in der Ansicht „Gerät“ sichtbar. Das Problem ist die Benennung dieser Sensoren. Der Name muss eindeutig sein. Es ist jedoch albern, einen eindeutigen Namen zu verwenden, wenn der Sensor in jedem Raum sehr gut „Temperatursensor“ heißen kann. Aber aktuell ist es ein eindeutiger Name, also wird daraus „Sensor im Maschinenraum“ oder „Sensor 1“ …

Wäre es nicht interessanter, eine automatische Benennung zu implementieren, die den Sensortyp berücksichtigt und eine spezifische Benennung anzeigt? So müsste man auf der Entwicklerseite nur das Gerät hinzufügen und den gewünschten Namen festlegen, und die Frontend-Schnittstelle würde die Benennung automatisch verwalten.

Was denkt ihr?

Hallo,
Ich finde den eindeutigen Namen auch sehr einschränkend, da ich ebenfalls mehrere Temperatursensoren habe.
Dieser Name sollte vom Benutzer änderbar sein und nur als Beschriftung dienen, nicht als funktionaler Wert.
Jetzt, da er automatisch generiert wird, denke ich, dass das zu absurd klingenden Namen führen wird.

Nein, ich glaube, du hast nicht ganz verstanden.

Logischerweise hast du in deinem Raum einen Temperatursensor. Da er sich in diesem Raum befindet, kann er den Namen „Temperatursensor“ tragen. Dieser Name kann mehrmals vorkommen, da er für jeden Raum einzigartig ist.

Falls du zwei Sensoren hast, benennst du sie 1 - 2.

Die Generierung erfolgt dynamisch und wird logischerweise nicht in der Datenbank gespeichert.

Die zweite Idee ist, eine neue Variable zu definieren, die weder der Name ist noch einzigartig. Diese ermöglicht es, mehrere „Temperatursensoren“ mit demselben Namen zu haben, falls der Benutzer dies wünscht.

Im Grunde genommen schaust du dir andere Systeme an, bei denen kein Name definiert wird, sondern einfach nur ein Gerät einem Raum hinzugefügt wird.

@damalgos: Ich werde die Eindeutigkeitsbeschränkung für den Namen entfernen, das ergibt keinen Sinn :slight_smile: Auf jeden Fall, so wie wir mit der Entwicklung der Dienste in Gladys 4 begonnen haben, wird der « name » nicht benötigt und wird nicht verwendet :slight_smile:

Und das ist eine gute Nachricht :smiley:

Also, ich habe festgestellt, dass SQLite bei einer Migration keine eindeutigen Indizes entfernen kann, also ist es nicht so einfach wie gedacht ^^

Wenn wir in einer Produktionsversion wären, hätten wir:

  1. Die Foreign Keys vorübergehend für die Migration deaktivieren
  2. Die Tabelle t_device in t_device_old umbenennen
  3. Die neue Tabelle t_device erstellen
  4. Die Daten von der alten zur neuen Tabelle übertragen
  5. Die alte Tabelle löschen
  6. Die Foreign Keys wiederherstellen

Da wir aber noch in der Alpha-Phase sind und die Nutzer noch keine sensiblen Daten haben, ist der einfachste Ansatz für den Entwickler (um keine Zeit zu verlieren), die ursprünglichen Migrationsdateien direkt zu ändern. Das bedeutet, dass alle, die die Alpha installiert haben, ihre Daten verlieren und eine neue Datenbank erstellen müssen!

Ich werde die Gelegenheit nutzen, um die gesamte Datenbank zu überprüfen und zu sehen, ob es noch andere unnötige Einschränkungen gibt, die entfernt werden können. Ich habe gesehen, dass der Kalender und die Events das gleiche Problem haben, also entferne ich diese auch.

Nicht besonders lustig, aber es ist eine Alpha. Natürlich wird so eine Prozedur nie in der Produktion durchgeführt werden..

Hallo @Pierre-Gilles, ich hatte das gleiche Problem wie du, um die Eindeutigkeit der Datenbank zu entfernen, aber es ist schließlich möglich. Schau dir den PR CalDAV an: https://github.com/GladysAssistant/Gladys/pull/507/commits/24e95947bbad98c23856e7a36491102916b6c054#diff-875173103ade96e5af1bdede5b6ae683R3

Bist du sicher, dass das funktioniert? Nach meinen Recherchen und Tests erlaubt SQLite nicht, eine Eindeutigkeitsbeschränkung zu entfernen, man muss eine Tabelle neu erstellen und migrieren. Vielleicht liege ich aber auch falsch, sag mir Bescheid :slight_smile:

Ja, das funktioniert sehr gut. Ich hatte ein Problem mit der Eindeutigkeit der Namen wiederkehrender Ereignisse. Damit kann man jetzt so viele Ereignisse wie man möchte unter Pepper's Geburtstag speichern.

Okay, danke für dein Feedback! Ich werde das mal testen :slight_smile:

@bertrandda leider funktioniert deine Lösung nicht zu 100 %, tatsächlich hast du einen Bug (oder ein Feature^^) von Sequelize entdeckt!

In SQLite gibt es kein ALTER TABLE. Die einzige Möglichkeit, eine Tabelle zu aktualisieren, besteht darin, sie umzubenennen, neu zu erstellen und die Daten zu migrieren.

Sequelize hat diesen Teil abstrahiert und tut dies, wenn du changeColumn ausführst:

Executing (default): PRAGMA TABLE_INFO(`t_device`);
Executing (default): CREATE TABLE IF NOT EXISTS `t_device_backup` (`id` UUID NOT NULL PRIMARY KEY, `service_id` UUID NOT NULL, `room_id` UUID, `name` VARCHAR(255) NOT NULL, `selector` VARCHAR(255) NOT NULL, `model` VARCHAR(255), `external_id` VARCHAR(255) NOT NULL, `should_poll` TINYINT(1) NOT NULL DEFAULT 0, `poll_frequency` INTEGER, `created_at` DATETIME NOT NULL, `updated_at` DATETIME NOT NULL);
Executing (default): INSERT INTO `t_device_backup` SELECT `id`, `service_id`, `room_id`, `name`, `selector`, `model`, `external_id`, `should_poll`, `poll_frequency`, `created_at`, `updated_at` FROM `t_device`;
Executing (default): DROP TABLE `t_device`;
Executing (default): CREATE TABLE IF NOT EXISTS `t_device` (`id` UUID NOT NULL PRIMARY KEY, `service_id` UUID NOT NULL, `room_id` UUID, `name` VARCHAR(255) NOT NULL, `selector` VARCHAR(255) NOT NULL, `model` VARCHAR(255), `external_id` VARCHAR(255) NOT NULL, `should_poll` TINYINT(1) NOT NULL DEFAULT 0, `poll_frequency` INTEGER, `created_at` DATETIME NOT NULL, `updated_at` DATETIME NOT NULL);
Executing (default): INSERT INTO `t_device` SELECT `id`, `service_id`, `room_id`, `name`, `selector`, `model`, `external_id`, `should_poll`, `poll_frequency`, `created_at`, `updated_at` FROM `t_device_backup`;
Executing (default): DROP TABLE `t_device_backup`;

Oder, wenn du den Code genau liest, gibt es einen Bug in dieser Implementierung: Die Foreign Keys und Constraints werden nicht migriert. Daher hat dein Name keine Constraints mehr: aber alle anderen Spalten haben ihre Constraints ebenfalls verloren, und die Tabelle hat keinen Schutz mehr. Der Selector ist nicht mehr eindeutig, die Foreign Keys sind weg, usw.

Siehe Code:

Nach einer Suche im Sequelize-GitHub gibt es ein Issue, das 2017 erstellt wurde und das Problem referenziert:

Aber anscheinend ist der Bug immer noch nicht behoben..

Drei Optionen:

  • Wir verwenden einen „Hack“, der eine der Funktionen von Sequelize während dieser Migration überschreibt, um die Constraints wieder hinzuzufügen.
  • Wir finden einen Weg, Sequelize zu beheben, und schlagen einen PR vor (allerdings bin ich mir nicht sicher, ob er vor unserem Bedarf gemerged wird)
  • Wir vergessen, changeColumn zu verwenden, und bitten die Leute, die DB neu zu erstellen… was kurzfristig löst, aber wenn wir langfristig eine Spalte ändern wollen, ist das Problem immer noch da!

Ich habe es geschafft, den Hack durchzuführen. Hier ist das Ergebnis:

module.exports = {
  up: async (queryInterface, Sequelize) => {
    const backupDescribeTable = queryInterface.describeTable;
    queryInterface.describeTable = async (tableName, options) => {
      const tableDatas = await backupDescribeTable.call(queryInterface, tableName, options);
      // Hole Fremdschlüssel-Informationen
      const foreignKeyDatas = (await queryInterface.sequelize.query(`PRAGMA FOREIGN_KEY_LIST("${tableName}");`))[0];
      foreignKeyDatas.forEach((foreignKeyData) => {
        const tableData = tableDatas[foreignKeyData.from];
        tableData.references = { model: foreignKeyData.table, key: foreignKeyData.to };
        tableData.onDelete = foreignKeyData.on_delete;
        tableData.onUpdate = foreignKeyData.on_update;
      });
      if (tableName === 't_device') {
        tableDatas.selector.unique = true;
        tableDatas.external_id.unique = true;
      }
      return tableDatas;
    };
    await queryInterface.changeColumn('t_device', 'name', {
      allowNull: false,
      type: Sequelize.STRING,
      unique: false,
    });
    queryInterface.describeTable = backupDescribeTable;
  },

  down: async (queryInterface, Sequelize) => {
    const backupDescribeTable = queryInterface.describeTable;
    queryInterface.describeTable = async (tableName, options) => {
      const tableDatas = await backupDescribeTable.call(queryInterface, tableName, options);
      // Hole Fremdschlüssel-Informationen
      const foreignKeyDatas = (await queryInterface.sequelize.query(`PRAGMA FOREIGN_KEY_LIST("${tableName}");`))[0];
      foreignKeyDatas.forEach((foreignKeyData) => {
        const tableData = tableDatas[foreignKeyData.from];
        tableData.references = { model: foreignKeyData.table, key: foreignKeyData.to };
        tableData.onDelete = foreignKeyData.on_delete;
        tableData.onUpdate = foreignKeyData.on_update;
      });
      if (tableName === 't_device') {
        tableDatas.name.unique = true;
        tableDatas.selector.unique = true;
        tableDatas.external_id.unique = true;
      }
      return tableDatas;
    };
    await queryInterface.changeColumn('t_device', 'name', {
      allowNull: false,
      type: Sequelize.STRING,
      unique: true,
    });
    queryInterface.describeTable = backupDescribeTable;
  },
};

Na gut, es ist nicht so einfach… Es ist sogar schlimmer, als ich dachte ^^

Mit « ON DELETE CASCADE » werden beim Migrieren der Tabelle t_device (Löschen + Neuerstellung) alle Einträge der Tabelle t_device_feature und folglich auch die von t_device_feature_state gelöscht… Nicht einfach!

Die einzige Lösung, die ich für unser Problem sehe, ist entweder:

  • Wir machen etwas speziell für SQLite, indem wir die Migrationsskripte selbst in SQL schreiben, und verlieren die Idee, den Nutzern irgendwann die Verwendung von MySQL zu ermöglichen. Oder wir machen zwei Fälle: einen für MySQL und einen für SQLite (möglich, aber das ist Arbeit).

  • Wir bleiben bei der ursprünglichen Idee, da es sich um eine Alpha-Version handelt, bitten wir die Nutzer, die Datenbank neu zu erstellen, und ändern die Migrationsskripte.

Ich denke ehrlich gesagt, dass wir, angesichts des Umfangs, die ursprünglichen Migrationsskripte ändern sollten (da wir noch « in Entwicklung » sind und das Produkt noch nicht veröffentlicht wurde) und uns die Frage stellen, wenn wir wirklich keine andere Wahl haben (das heißt, wenn wir wirklich eine Spalte ändern müssen). Dabei sollte man bedenken, dass dies sehr selten vorkommt (es muss wirklich einen Designfehler geben, wie hier). Normalerweise ist das Hinzufügen von Spalten am häufigsten, und das unterstützt SQLite.

Wir sind noch in der Entwicklung, oder wir erstellen alles neu, was es uns auch ermöglichen wird, die Dienste erneut zu testen und die „Unvollkommenheiten“ zu notieren.