Gladys 4 - Entwicklung des Arduino-Dienstes

@billona
Kannst du (falls es nicht zu komplex ist) eine Test-LED für eine PIN hinzufügen? Das hilft zu erkennen, ob der Arduino abstürzt oder ob der Code vom Gerät nicht empfangen wird.
Funktion: « LED » EIN
STATUS « EIN »/« AUS »

@benPi du kannst das einfach mit der IR-Ausstrahlung machen, indem du die IR-LED durch eine normale LED ersetzt. Danach kannst du die Anzahl der Impulse erhöhen, um das Ergebnis besser zu visualisieren :wink:

Hallo zusammen!

Kurzer Stand zum Fortschritt des Dienstes:

1. Der Sendeteil

Ich habe ein bisschen daran gearbeitet, damit die IR- und 433-MHz-Sendungen mit den Funktionen „Schalter“ und „Licht“ kompatibel sind. Dadurch können wir jetzt im Gerät ON/OFF-Codes definieren, die gesendet werden, wenn sich der Wert des Geräts ändert :smile:

2. Der Empfangsteil

Also… Ich muss zugeben, dass mir dieser Teil ganz schön Kopfzerbrechen bereitet hat :sweat_smile: Aber nach ein bisschen Arbeit bin ich endlich zu etwas „halbwegs Korrektem“ gekommen! Ich habe die Erstellung der Arduino-Geräte so geändert, dass sie alle Minuten gepollt werden, wodurch die Nachrichten der Karte empfangen werden können. Anschließend liste ich die Geräte auf, die dieser Karte zugeordnet sind, und weise die Werte den Geräten zu, die die entsprechenden Funktionen haben.

3. Oberfläche

Konkrekt sieht die Oberfläche momentan so aus:

Geräte auf der Geräteseite:

Mein Dashboard:

4. Der Arduino-Code

Ich habe die Implementierung der DHT11 und einer Servomotor-Steuerungsfunktion hinzugefügt. Es bleibt noch Arbeit, um die Konfiguration in Gladys zu ermöglichen, aber die Basis ist da :slightly_smiling_face:

Zur Info: Da die Entwicklung noch lange nicht abgeschlossen ist, ist es sicher, dass sich der Code in den nächsten Tagen stark weiterentwickeln wird. Mein Ziel ist es jetzt, die bestehende Basis dank der Nutzungsrückmeldungen zu optimieren und so viele Funktionen wie möglich in den Arduino-Code zu implementieren :wink:

Nochmals vielen Dank an die Tester, dass ihr euch die Zeit nehmt, mir eure Erfahrungen mitzuteilen. Das hilft mir enorm und lässt den Dienst in die richtige Richtung wachsen :smile:

@billona Ich habe den ganzen Thread gelesen, tolle Arbeit! Es ist wirklich sehr sauber, und ich sehe, dass du den Service auf Basis des Feedbacks gut verbesserst, das ist cool :clap:

Mmm was heißt das? Was pollst du denn?

Beim Dashboard sollte der Nutzer die Wahl haben, den « RX » entweder in die Kategorie « MOTION_SENSOR » oder in die Kategorie « OPENING_SENSOR » einzustufen. Dadurch wird eine benutzerdefinierte Anzeige erstellt, die nur die Zeit seit der letzten Bewegung bzw. der letzten Türöffnung anzeigt.

Falls du sehen möchtest, wie die Anzeige dieser Box funktioniert, geht das hier =>

https://github.com/GladysAssistant/Gladys/blob/master/front/src/components/boxs/device-in-room/device-features/SensorDeviceFeature.jsx#L52

Kleine Frage noch: Was fehlt dir, damit wir eine erste Version des Dienstes veröffentlichen können?

Ich denke, es ist besser, eine erste Version zu veröffentlichen, auch wenn nicht alle Geräte, die du verwalten möchtest, bereits unterstützt werden, und dann iterativ am Code zu arbeiten. So könnte ich auch deine PR einer ersten Code-Review unterziehen. :slight_smile:

Danke @pierre-gilles, das freut mich zu hören :smile:

Ich verwende poll, um einen Parser zu erstellen. on(‹ data ›, async (data) => « … »); auf der Arduino-Karte:

https://github.com/billonalex/Gladys/blob/master/server/services/arduino/lib/poll.js

Mal sehen, ob das die ideale Lösung ist oder ob ein einfaches Initialisierungsskript für die Karte ausreicht… Aber es schien mir das Beste zu sein, da man sich auf diese Weise bei einer bestimmten Frequenz versichert, dass der Port immer in ‹ data › geöffnet ist.

Ja, das scheint mir eine interessantere Idee zu sein, ich werde daran arbeiten! Allerdings muss ich zugeben, dass die Art und Weise, wie ich es im Dashboard dargestellt habe, vor allem dazu diente, dem Gerät zu ermöglichen, zu sagen: « Ich bin hier! Schaut mal, ich habe einen Wert erfasst » :stuck_out_tongue:

Nun, wenn ich mich auf deine Kommentare stütze, habe ich das zu tun, und danach können wir tatsächlich eine erste Version veröffentlichen :smile:

Allerdings muss ich zugeben, dass der Code nicht vollständig optimiert ist, also wird es auf jeden Fall noch Arbeit geben, aber wenn wir uns auf alles, was bereits gemacht wurde, konzentrieren, dann ist die API vorhanden. Also sag du mir, was besser ist :wink:

Oh und könntest du mir auch bei den Schritten helfen, die ich für den PR durchführen muss? Da ich zum ersten Mal an einem so großen Projekt arbeite, habe ich noch nicht alle richtigen Reflexe :sweat_smile:

Oh jein, das ist ja schwer :smiley:

Ein einfaches Initialisierungsskript für die Karte reicht aus!

Du rufst parser.on('data', async (data) => nur einmal in einer Funktion « init.js » auf, die du beim Start des Dienstes aufrufst :slight_smile:

Wenn du die Sache mit dem Poll + dem Dashboard des RX erledigen kannst, dann scheint mir das Featuremäßig in Ordnung zu sein.

Kein Problem :slight_smile:

Oben in deiner PR hast du sicherlich bemerkt, dass es eine Checkliste mit Dingen gibt, die vor dem Einreichen deiner PR zur Überprüfung überprüft werden müssen:

Jeder Punkt muss überprüft und validiert werden. Ich lasse dich die Punkte einen nach dem anderen anschauen :slight_smile:

Die wichtigsten:

Die Tests

Du musst die Tests für deine Integration geschrieben haben, ich habe den Eindruck, dass das bei dir nicht der Fall ist? Du kannst dich an den Tests der anderen Dienste inspirieren (Gladys/server/test/services at master · GladysAssistant/Gladys · GitHub)

Um die Tests zu starten, kannst du npm test im Ordner server ausführen. Wenn du nur einen einzigen Test ausführen möchtest, kannst du den Test in it.only setzen (siehe die Dokumentation von mocha: Mocha | Classic, reliable, trusted. ☕)

Das Ziel der Tests ist, dass absolut alles, was du codiert hast, durch Tests abgedeckt ist und dass wir in der Zukunft sicher sein können, dass keine Regressionen durch einen anderen PR eingeführt werden.

Das Linting

Wir verwenden eslint und prettier in Gladys 4, um sicherzustellen, dass der Code genau gleich formatiert ist, egal welcher Entwickler am Projekt arbeitet.

Wenn du Fragen hast, zögere nicht! Und wenn du wirklich Schwierigkeiten hast, können wir uns immer anrufen, um dir eine schnelle Schulung zu geben :slight_smile:

Ja, das dachte ich mir auch :joy: Aber bevor ich den Poll für die Arduino-Karten programmiert habe… Habe ich einen Poll für JEDES Gerät programmiert, das etwas empfangen sollte! Noch schwerer, aber wenigstens war man sich sicher :joy:
Daher werde ich heute versuchen, das zu korrigieren.

Kein Problem, ich mache das und sobald es fertig ist, lasse ich dich wissen!

Kein Problem, ich kümmere mich darum, nachdem ich die paar Änderungen vorgenommen habe :slightly_smiling_face:

Nein, ich habe noch keine Tests geschrieben, ich werde mich darum kümmern.
Was Prettier betrifft, habe ich VS Code so angepasst, dass die gewünschten Anforderungen berücksichtigt werden, ich werde Datei für Datei überprüfen.

Wie in der Checkliste angegeben, hast du 2 CLI-Tools, mit denen du das Linting automatisch überprüfen kannst:

npm run eslint
npm run prettier

In (server und front)

Kann ich die Versionsänderungen in package.json und package-lock.json nach einem npm run prettier auch behalten? Oder sollte ich diese Änderungen rückgängig machen? Typischerweise hat die Installation und der Prettier diese Änderungen vorgenommen:

"prettier": "^1.17.1"

en das hier:

"prettier": "^1.19.1"

Du kannst das entfernen! Das hat nichts mit deinem PR zu tun, wir machen die Abhängigkeitsaktualisierungen separat :slight_smile:

C’est retiré :slightly_smiling_face:

Für die Funktion init() ist alles gut! Das einzige Problem… Wenn ich nach dem Erstellen des Arduino Geräte erstelle, muss der Server neu gestartet werden, damit sie berücksichtigt werden. Andernfalls, wenn ich einen 433-Empfänger einrichte und den Server nicht neu starte, wird diesem keine Wert zugewiesen…

Gleiches gilt, wenn ich einen Arduino erstelle, ich mache eine API-Anfrage, um die Funktion init() aufzurufen, andernfalls müsste der Server neu gestartet werden, damit der Arduino berücksichtigt wird…

Hier ist mein Skript:

Hast du einen Tipp, wie man das vermeiden kann? Wenn das geregelt ist, sollte das Dashboard des RX von alleine funktionieren ^^

Ah, das ist ein echtes Problem ^^ Gladys sollte nicht häufig neu gestartet werden. Du musst einen Listener hinzufügen: Wenn ein Gerät hinzugefügt/geändert wird, musst du das Abhören des Ports hinzufügen!

Was ich dir empfehle, ist, deine Init-Funktion in zwei Funktionen aufzuteilen:

  1. eine Funktion namens « listen », die den Arduino als Parameter nimmt, den du abhören möchtest
  2. die Init-Funktion, die die Liste der Arduinos abruft und für jeden Arduino die Funktion listen aufruft

Wenn ein Arduino hinzugefügt wird, muss diese Funktion listen aufgerufen werden. Genauso, wenn ein Arduino geändert wird (ich weiß nicht, ob es möglich ist, den Port zu ändern?), dann muss die Funktion aufgerufen werden.

Achte darauf, alle Zustände korrekt zu verwalten, damit ein Port nicht zweimal abgehört wird! Sonst riskieren wir, Nachrichten doppelt zu erhalten/oder es kann Probleme geben.

Ich denke, du musst deinen Port + Parser erstellen, indem du eine Referenz auf den Port und deinen Parser in deinem ArduinoManager-Objekt speicherst.

In deinem Code:

 const port = new SerialPort(arduinoPath, {
        baudRate: 9600,
        lock: false,
      });

 const parser = port.pipe(new Readline({ delimiter: '\n' }));

Deine Variablen sind « verloren », man kann sie nicht vom ArduinoManager-Objekt aus aufrufen

Du könntest sie zum Beispiel in einem Objekt speichern:

// index.js
this.arduinosPorts = {};
this.arduinoParsers = {};

// listen.js
this.arduinosPorts[arduinoPath] = new SerialPort(arduinoPath, {
            baudRate: 9600,
            lock: false,
          });
this.arduinoParsers[arduinoPath] = = port.pipe(new Readline({ delimiter: '\n' }));

(das ist ein Vorschlag, der in 30 Sekunden geschrieben wurde, aber du siehst die Idee? :slight_smile:

Dank dessen kannst du in deiner « listen »-Funktion testen, ob ein Arduino bereits abgehört wird und ein doppeltes Abhören vermeiden. Bei einem zweiten Abhören kannst du den Serialport schließen und dann wieder öffnen, verstehst du die Idee?

Ich habe deinen Rat so gut wie möglich befolgt!

Hier ist mein Script listen.js:

Was die Überprüfung der bereits geöffneten Ports angeht, entspricht das in etwa dem, was du im Sinn hattest?

Warum hast du nicht in 2 Funktionen aufgeteilt? So wie ich das sehe, schließt du jedes Mal, wenn jemand einen Arduino hinzufügt, alle Ports und öffnest sie dann wieder. Das ist nicht sehr optimiert, oder? Man könnte Nachrichten verpassen.

Ich empfehle dir wirklich, es in 2 Funktionen aufzuteilen, damit es einfacher und klarer wird :slight_smile:

Ich glaube, ich habe dich nicht ganz verstanden :sweat_smile:

In meinem Kopf waren die beiden Funktionen init() und listen(arduino), vielleicht habe ich da einen Schritt übersehen ^^

das ist richtig! Entschuldigung, ich habe deinen Code etwas schnell gelesen, aber es scheint mir eigentlich gut zu sein :smiley:

Ah, das ist ja gut :smile:

Allerdings bekomme ich manchmal diesen Fehler in meinen Logs:

2020-05-25T15:06:45+0200 <warn> listen.js:78 (listen) Unable to listen to device
2020-05-25T15:06:45+0200 <debug> listen.js:79 (listen) TypeError: Cannot read property 'device' of undefined
at listen (/home/pi/Gladys/server/services/arduino/lib/listen.js:26:31)
at arduinoList.forEach.arduino (/home/pi/Gladys/server/services/arduino/lib/init.js:41:7)
at Array.forEach (<anonymous>)
at ArduinoManager.init (/home/pi/Gladys/server/services/arduino/lib/init.js:40:17)

Manchmal, wenn ich this.gladys aufrufe, um meine Geräte abzurufen, zeigt logger.warn(this.gladys) « undefined » an.

Edit: Soweit ich das aktuell sehe, tritt der Fehler beim ersten Aufruf des Dienstes auf, wenn listen(arduino) in init.js aufgerufen wird. Nach einer Änderung des Arduinos (und damit einem erneuten Aufruf von listen) funktioniert es…

Booooon !! Nach einem harten Kampf habe ich endlich den Fehler gefunden :smile:

Also normalerweise sind wir bei der Initialisierung und beim Hören auf dem richtigen Weg, jetzt muss ich nur noch meine nutzlosen Skripte loswerden :wink:

Allerdings @pierre-gilles, ich habe an den Sensoren gearbeitet, wie du es mir gesagt hast. Beim Bewegungssensor sind wir gut, er zeigt mir an, wie viel Zeit seit der letzten Wertänderung vergangen ist. Allerdings habe ich ein bisschen Schwierigkeiten zu sehen, wie ich mich mit dem Öffnungssensor zurechtfinden soll… Da es sich um einen binären Typ handelt, lohnt es sich wirklich, den empfangenen Code in einem Öffnungssensor zu speichern?

Was meinst du? Als Wert, denke ich, speicherst du « 1 », wenn die Tür offen ist, und « 0 », wenn die Tür geschlossen ist, oder?

Gleiches gilt für den Bewegungssensor, außer dass du dort « 1 » speicherst, wenn eine Bewegung erkannt wird, das ist alles :slight_smile:

Speichere den empfangenen Code nicht im Wert, wenn das deine Frage ist