HomeKit und ioBroker Lass uns zu Hause Freunde finden


Ohne Zweifel bleibt Apple iOS eines der beliebtesten mobilen Betriebssysteme, was bedeutet, dass moderne Automatisierungssysteme in der Lage sein mĂŒssen, sich in dieses Ökosystem zu integrieren und die FĂ€higkeit zur Interaktion bereitzustellen. Genau dafĂŒr wurde das Homekit-Framework entwickelt, mit dem Sie mit intelligenten GerĂ€ten vom iPhone / iPad / iWatch-Bildschirm und in jĂŒngerer Zeit vom Mac (macOS Mojave) aus arbeiten können.


Die meisten Automatisierungssysteme (ich mag den Marketingnamen "Smart Home" nicht) enthalten seit langem Module fĂŒr die Integration in Homekit, aber selbst ein geschulter Benutzer kann nicht immer nur herausfinden, wie sein GerĂ€t in der Home- (oder Eve-) Anwendung verfĂŒgbar gemacht werden kann.


Heute werde ich Ihnen erklĂ€ren, wie diese Manipulationen im ioBroker-System durchgefĂŒhrt werden (dies ist ein offenes und freies Automatisierungssystem). Aber um nicht dumm all die vielen Beispiele fĂŒr GerĂ€te zu nennen, möchte ich einige Prinzipien erklĂ€ren und AnsĂ€tze aufzeigen, wobei ich weiß, welche Sie andere Beispiele leicht implementieren können.


"Die Kenntnis einiger Prinzipien gleicht leicht die Unkenntnis einiger Fakten aus."
Claude Adrian Helvetius


ioBroker. Treiber, GerÀte und Status


ZunÀchst möchte ich erklÀren, was ein GerÀt im ioBroker-System ist und wie es dargestellt wird.


Ich möchte Sie daran erinnern, dass das ioBroker-System modular aufgebaut ist und die Erweiterungsmodule als Treiber (oder Adapter) bezeichnet werden. Ein Treiber ist ein Integrationsmodul mit einem GerÀt oder einer Gruppe von GerÀten, die durch eine gemeinsame FunktionalitÀt, ein gemeinsames Protokoll oder einen gemeinsamen Hersteller verbunden sind, und kann daher ein oder mehrere GerÀte in das ioBroker-System "ziehen". Eine weitere Funktion ist die Möglichkeit, mehrere Instanzen desselben Treibers zu erstellen, die sich in den Einstellungen unterscheiden.


Aber jedes GerĂ€t ist einzigartig und unnachahmlich, hat unterschiedliche Eigenschaften und FĂ€higkeiten. Auf dieser Grundlage konzentriert sich ioBroker in erster Linie nicht auf das GerĂ€t selbst, sondern auf seine Eigenschaften, die durch ZustĂ€nde dargestellt werden. Ein Status ist ein internes ioBroker-Objekt, das einen Wert akzeptiert und speichert. Synonyme des Staates können berĂŒcksichtigt werden: Zeichen, Attribute, Eigenschaften, Eigenschaften, Ereignisse. Beispiele fĂŒr Bedingungen: "Temperatur", "Helligkeitsstufe", "Batteriestand", "Einschaltflag", "Fehlerflag", "Druckflag", "Doppelpressflag" usw. Somit wird jedes GerĂ€t durch viele verschiedene ZustĂ€nde dargestellt.


Objektstruktur


ZustĂ€nde können in informative ZustĂ€nde unterteilt werden - sie zeigen Informationen vom GerĂ€t an und verĂ€nderbare - sie können vom Benutzer oder Skript geĂ€ndert und diese Änderungen an das GerĂ€t gesendet werden. Wenn sich auf dem GerĂ€t etwas Ă€ndert - diese Daten werden in den Status angezeigt und wenn sich der Status von ioBroker Ă€ndert (vom Benutzer oder vom Skript) -, erhĂ€lt das GerĂ€t ein Signal ĂŒber die Änderung und muss entsprechend reagieren (dies hĂ€ngt vom GerĂ€t selbst und dem Treiber ab funktioniert).


Alle GerÀtezustÀnde werden in einem einzigen Baum (Registrierung) von ZustÀnden zusammengefasst. Sie werden zuerst nach GerÀt (in einigen FÀllen wird immer noch Channeling verwendet) und dann nach Treiberinstanzen gruppiert.


Das Konzept der MQTT-Protokollthemen passt problemlos in einen solchen Statusbaum. Auf diese Weise können Sie zusĂ€tzliche GerĂ€te oder Systeme von Drittanbietern anschließen, die das MQTT-Protokoll unterstĂŒtzen. Es reicht aus, den MQTT-Treiber zu installieren - der entsprechende Zweig wird im Statusbaum angezeigt.


Und es gibt alle Arten von Online-Diensten, die nĂŒtzliche Informationen liefern und / oder die Steuerung anderer GerĂ€te (z. B. Autoalarme) ermöglichen. Das Ergebnis der Interaktion mit diesen Diensten wird auch als eine Reihe von ZustĂ€nden dargestellt.


Staatsbaum


Insgesamt scheint ein GerÀt in ioBroker eine Reihe von ZustÀnden zu sein, die das GerÀt charakterisieren und die Interaktion mit ihm ermöglichen.


Homekit Zubehör, Dienstleistungen und Spezifikationen


Wenden Sie sich nun an Homekit. Hier wird die Klassifizierung von GerÀten, deren FunktionalitÀt und Eigenschaften angewendet.


Homekit-GerÀtekategorien


Zubehör entspricht einem physischen GerĂ€t. Das Zubehör verfĂŒgt ĂŒber eine Kategorie , mit der es einer bestimmten Gruppe zugeordnet werden kann.


Services entsprechen der FunktionalitÀt eines Zubehörs. Ein Zubehör kann mehrere Dienste haben.


Die Dienste zeigen die Funktionen des GerĂ€ts an: Lampe, Batterie, Taste, LuftqualitĂ€tssensor, TĂŒr, Luftfilter, Kamera.


Es ist der Dienst, der die Anzeige, das Verhalten des GerÀts und die Eigenschaften bestimmt.


Merkmal ist das Äquivalent der Attribute / Eigenschaften, die einen Dienst charakterisieren. Diese Eigenschaften bestimmen, ob das GerĂ€t eingeschaltet ist, wie hell die Lampe ist oder wie oft die Taste gedrĂŒckt wird. Ein einzelner Dienst kann viele Eigenschaften haben.


Objektstruktur


Anwendungen, die mit Homekit arbeiten, lesen die Dienste und Eigenschaften von Zubehör und zeigen die Werte in den Eigenschaften ĂŒber die BenutzeroberflĂ€che an und können sie Ă€ndern. Die geĂ€nderten Werte werden an die Homekit-GerĂ€te gesendet, um sie anzuwenden, und von den Homekit-GerĂ€ten werden auch die Werte der Merkmale mit einigen Änderungen von der Seite des GerĂ€ts gesendet.


Insgesamt scheint das GerÀt in HomeKit ein Zubehör mit einer Reihe von Diensten und Funktionen zu sein.


Yahka. Wir schließen uns dem Konzept an


FĂŒr die Arbeit mit Homekit verwendet ioBroker den Yahka- Treiber ( zusĂ€tzliche Module mĂŒssen vor der Installation installiert werden ) - ein Add-On zu einer bekannten Bibliothek https://github.com/KhaosT/HAP-NodeJS , das auch das beliebte HomeBridge-Projekt erstellt. Diese Bibliothek dient zum Erstellen eines virtuellen Gateways / einer virtuellen BrĂŒcke, die eine Reihe virtueller GerĂ€te in HomeKit bereitstellt. Wenn Sie die virtuellen GerĂ€te und Dienste entsprechend konfigurieren und die Werte der Merkmale festlegen, erhalten Sie das fertige GerĂ€t in Homekit und der Home-Anwendung, und wir können Siri auch bitten, es zu verwalten.


Der Yahka-Treiber dient lediglich zum Konfigurieren von Zubehör, zum HinzufĂŒgen von Diensten und zum Anzeigen der Entsprechung von Merkmalen (HomeKit) und Status (ioBroker).


Nach der Installation mĂŒssen Sie jedoch zuerst das Gateway konfigurieren und in die Home-Anwendung integrieren. Nach der Konfiguration werden alle zum Gateway hinzugefĂŒgten GerĂ€te automatisch zur Startseite hinzugefĂŒgt. Geben Sie dazu "GerĂ€tename" an (es ist wĂŒnschenswert, nur lateinische Buchstaben anzugeben) und merken Sie sich den PIN-Code (oder legen Sie Ihren eigenen fest).


Gateway-Setup


Wir gehen zur Home-Anwendung und fĂŒgen ein neues Zubehör hinzu.



Kommen wir nun zu den GerĂ€ten. Alles wĂ€re in Ordnung, wenn die Status fĂŒr das GerĂ€t in ioBroker eindeutig mit den Diensten und Funktionen in HomeKit ĂŒbereinstimmen wĂŒrden. Und es wĂ€re noch besser, wenn die Werte in den ZustĂ€nden fĂŒr die Werte der Merkmale geeignet wĂ€ren. Aber oft ist dies nicht der Fall, und Sie mĂŒssen sich ungewöhnliche Möglichkeiten zum Andocken einfallen lassen. Ich werde im Folgenden auf einige davon eingehen, und Sie mĂŒssen alle anderen Optionen selbst implementieren, "im Bild und in der Ähnlichkeit".


Der Einfachheit halber habe ich ein Dokument mit der Übersetzung von Diensten und Typen sowie den möglichen Werten der Merkmale erstellt. Alle verwendeten Typen und Dienste entsprechen der HAP-NodeJS-Bibliothek .


Temperatursensor


Dies ist das einfachste Beispiel. Sie mĂŒssen lediglich einen Zustand haben, der den numerischen Wert der Temperatur enthĂ€lt. Es kann von ĂŒberall bezogen werden: von Sensoren oder von Internetdiensten (Wetter).

Sie mĂŒssen ein GerĂ€t der Kategorie Sensor hinzufĂŒgen, dem GerĂ€t den TemperatureSensor-Dienst hinzufĂŒgen und diesem Dienst einen Namen geben. Es gibt 5 Merkmale in diesem Dienst, von denen das wichtigste fĂŒr uns die aktuelle Temperatur ist.


Zubehör-Thermometer


TemperatureSensor Service


Es reicht aus, den Namen des Zustands anzugeben, der der Temperatur in der CurrentTemperature-Kennlinie entspricht.


FĂŒgen Sie hier auch den HumiditySensor-Feuchtigkeitsservice hinzu, und in Homekit wird ein separates Zubehörsymbol erstellt.


HumiditySensor Service


Speichern und fertig. Jetzt können Sie sich an Siri wenden und sie nach Temperatur und Luftfeuchtigkeit fragen.



GesprÀch mit Siri



Batterie


Ein weiterer einfacher Service. Sein Trick ist, dass es zu fast jedem Zubehör hinzugefĂŒgt werden kann. FĂŒgen Sie den BatteryService-Dienst hinzu und geben Sie in der BatteryLevel-Kennlinie einen Zustand an, der den Prozentsatz der Batterieladung enthĂ€lt. Danach werden die Ladedaten in den zusĂ€tzlichen Daten zum GerĂ€t angezeigt.


BatteryService


Sie können sofort das Vorzeichen "Low Charge" (Merkmal StatusLowBattery) setzen. Wenn der Wert des angegebenen Status gleich 1 ist, wird das entsprechende Symbol auf dem GerÀtebild angezeigt.


Aber was ist, wenn Sie keinen solchen Status haben, aber das Symbol fĂŒr niedrigen Batteriestand sehen möchten? Sie mĂŒssen diesen Status manuell oder mithilfe eines Skripts erstellen und den erstellten Status in den Merkmalen angeben.


Jetzt bleibt nur noch die korrekte Einstellung des Wertes true in diesem Zustand. Zu diesem Zweck verwenden wir ein Skript, das auf true gesetzt wird, wenn der Akku 30 Prozent erreicht.


createState(""); on({id: "zigbee.0.00158d0001f41725.battery", change: "ne"}, function (obj) {   var value = obj.state.val; setState("javascript.0.", (value <= 30)); }); 

Nach dem ersten Durchlauf erstellt das Skript einen Status und kann in den Merkmalen ausgewÀhlt werden.



Dieses Zeichen wird auf den Bildern des Zubehörs angezeigt



und GerÀtedetails


Lampen


GlĂŒhbirnen sind unterschiedlich - hell, warm, rot. Es gibt 4 FĂ€lle:


  • Einfach - durch Ein- und Ausschalten gesteuert
  • Dimmbar - wird auch durch die Helligkeit gesteuert
  • Mit der Temperatur - ist es möglich, die Temperatur des GlĂŒhens zu steuern
  • Farbe - Sie können die Farbe des Lichts steuern

FĂŒr jeden dieser FĂ€lle gibt es im GlĂŒhbirnen-Service ein entsprechendes Merkmal:


  • Ein - Ein / Aus
  • Helligkeit - Helligkeitsstufe
  • Farbton - Schatten
  • SĂ€ttigung - SĂ€ttigung
  • Farbtemperatur - Farbtemperatur

Im einfachen Fall geben wir in der Eigenschaft "Ein" den Zustand an, der fĂŒr das Ein- und Ausschalten verantwortlich ist.



Wenn die Lampe dimmbar ist, geben wir zusÀtzlich den Status mit der Helligkeitsstufe an.



Neben der Zuweisung korrekter ZustÀnde ist es wichtig, das Intervall akzeptabler Werte zu beachten!


Beispiel: In einigen FĂ€llen kann der fĂŒr die Lampenhelligkeit verantwortliche Status Werte von 0 bis 255 annehmen. In Homekit sind diese Werte jedoch auf ein Intervall von 0 bis 100 begrenzt. In diesem Fall können Sie die Konvertierungsfunktionen des Yahka-Treibers verwenden . Die Funktion "level255" konvertiert nur das Intervall der Werte 0..255 in das Intervall 0..100 (und umgekehrt).


Die folgenden Schwierigkeiten können auftreten, wenn Ihre Lampe farbig ist, die verwendete Farbe jedoch RGB ist. Es können entweder drei verschiedene ZustĂ€nde oder eine Zahl (oder Zeichenfolge) sein. In diesem Fall mĂŒssen Sie von einem RGB-Farbraum in einen anderen XYB-Raum (dieser Raum wird von HomeKit verwendet) oder in die XY-Ebene konvertieren.


Dazu mĂŒssen Sie zwei neue ZustĂ€nde (Farbton und SĂ€ttigung) erstellen, in die wir die Werte aus dem RGB-Zustand konvertieren und umgekehrt.


Das resultierende Skript fĂŒr die Farbe ist
 //       createState("Hue"); createState("Sat"); //      RGB- on({id: "Hue", ack: false, change: 'any'}, function (obj) {  var hue = parseInt(obj.state.val);  var sat = parseInt(getState('Sat').val);  var res = hsvToRgb(hue, sat, 100);  setRGB(parseInt(res[0]), parseInt(res[1]), parseInt(res[2])); }); //    RGB- function setRGB(r, g, b){  var val = ('00'+r.toString(16)).slice(-2)+('00'+g.toString(16)).slice(-2)+('00'+b.toString(16)).slice(-2);  // RGB-   setState('zigbee.0.00124b0014d016ab.color', val, false); } //   HSV   RGB function hsvToRgb(h, s, v) {  var r, g, b;  var i;  var f, p, q, t;   h = Math.max(0, Math.min(360, h));  s = Math.max(0, Math.min(100, s));  v = Math.max(0, Math.min(100, v));  s /= 100;  v /= 100;   if(s == 0) {      r = g = b = v;      return [          Math.round(r * 255),          Math.round(g * 255),          Math.round(b * 255)      ];  }   h /= 60;  i = Math.floor(h);  f = h - i;  p = v * (1 - s);  q = v * (1 - s * f);  t = v * (1 - s * (1 - f));   switch(i) {      case 0:          r = v;          g = t;          b = p;          break;       case 1:          r = q;          g = v;          b = p;          break;       case 2:          r = p;          g = v;          b = t;          break;       case 3:          r = p;          g = q;          b = v;          break;       case 4:          r = t;          g = p;          b = v;          break;       default: // case 5:          r = v;          g = p;          b = q;  }   return [      Math.round(r * 255),      Math.round(g * 255),      Math.round(b * 255)  ]; } 

Die Farbtemperatur kann einfacher eingestellt werden. Wenn der Bereich der verfĂŒgbaren Werte fĂŒr Ihre Lampe bekannt ist, kann er (ĂŒber die Funktion scaleInt ) in das fĂŒr HomeKit verfĂŒgbare Intervall konvertiert werden.


GlĂŒhbirnen-Service



Tiefer in der Lampe



Thermostat


Thermostat - ein GerĂ€t zur Aufrechterhaltung der eingestellten Temperatur (Thermostat-Service). Dementsprechend ist das Hauptmerkmal des Thermostats die gewĂŒnschte Temperatur (TargetTemperature). ZusĂ€tzlich zur eingestellten Temperatur kann die aktuelle Temperatur (CurrentTemperature) angezeigt werden, die informativer Natur ist (da das GerĂ€t sie nur von den Sensoren liest).


In der Home-Anwendung wird die Zieltemperatur im Thermostat eingestellt und die aktuelle Temperatur verfolgt. In meinem Thermostat (Zont) gab es nur diese beiden ZustĂ€nde - sie waren ĂŒber die Service Cloud API verfĂŒgbar.


FĂŒr die Schönheit der Anzeige des GerĂ€ts in HomeKit musste ich einige Konstanten hinzufĂŒgen: Der aktuelle Heizzustand ist aktiv (1), der Zielheizzustand ist automatisch (3).


Thermostat-Service



Temperaturauswahl


Tore


Mit einem Garagentor (GarageDoorOpener-Service) ist alles schwieriger als mit einem Thermostat.


Von den verfĂŒgbaren Eigenschaften hat das Gate einen Zielzustand (TargetDoorState), der unseren Wunsch anzeigt, dass das Gate "offen" oder "geschlossen" ist. Sie mĂŒssen aber auch den aktuellen Status des Gates (CurrentDoorState) korrekt anzeigen: Sind sie geöffnet oder geschlossen oder öffnen oder schließen sie sich?


In meinem Fall wurden die Tore ĂŒber mqtt in ioBroker mit mehreren InformationszustĂ€nden geöffnet:


  • Zeichen der Toroffenheit (OB)
  • Zeichen der Bewegung des Tores (LW)

Garagentor-KontrollzustÀnde


Dank dieser ZustÀnde können Sie den aktuellen Status des Tors berechnen:


  • Wenn es keinen OB und keinen DV gibt, werden die Tore geschlossen
  • Wenn es keinen OB und einen DV gibt, öffnen sich die Tore
  • Wenn es einen OB und keinen DV gibt, sind die Tore offen
  • Wenn es OB und DV gibt, schließt sich das Tor

Um ein Signal zum Öffnen und Schließen des Gates zu senden, habe ich zwei separate ZustĂ€nde (es wĂ€re möglich, mit einem Zustand zu verwalten, aber ich habe zwei), die eine Nachricht ĂŒber mqtt an den Gate-Steuercontroller senden:


  • Eröffnungssignal
  • Schließsignal

Um ein Signal zu senden, mĂŒssen Sie eine "Klick" -SchaltflĂ€che simulieren: Setzen Sie den Wert auf true und setzen Sie ihn nach einer Weile auf false zurĂŒck. In diesem Zusammenhang war es fĂŒr die Integration in HomeKit erforderlich, einen anderen Status zu erstellen - den „Zielstatus des Gates“. Wenn dieser geĂ€ndert wird, wird das entsprechende Signal gesendet.


Das Zeichen der Offenheit des Tors kann durch den Zielzustand ersetzt werden (d. H. Was das Ziel anstrebt):


  • Wenn die CA „geschlossen“ ist und kein DV vorhanden ist, ist das Gate geschlossen
  • Wenn die CA „geschlossen“ ist und ein DV vorhanden ist, öffnen sich die Tore
  • Wenn die Zertifizierungsstelle „offen“ ist und kein DV vorhanden ist, ist das Gate geöffnet
  • Wenn die Zertifizierungsstelle „offen“ ist und ein DV vorhanden ist, wird das Gate geschlossen

Wir werden auch einen separaten Status "Aktueller Gate-Status" erstellen und diesen abhĂ€ngig vom Wert der Zeichen und vom Zielstatus in das Skript einfĂŒgen.


ZustandsĂ€nderungsskript fĂŒr Garagentore
 createState("gate_0.current"); //   createState("gate_0.target"); //   //    0,   300 on({id: "mqtt.0.gate.gpio.13", ack: false, val: 1}, function (obj) {  setStateDelayed("mqtt.0.gate.gpio.13", 0,  300); }); on({id: "mqtt.0.gate.gpio.12", ack: false, val: 1}, function (obj) {  setStateDelayed("mqtt.0.gate.gpio.12", 0,  300); }); //     on({id: "mqtt.0.gate.is_open", ack: false, val: 1}, function (obj) {  // ""  setState("javascript.0.gate_0.current", 0, true); }); on({id: "mqtt.0.gate.is_open", ack: false, val: 0}, function (obj) {  // ""  setState("javascript.0.gate_0.current", 1, true); }); //    - ,      on({id: "javascript.0.gate_0.target", ack: false, val: 0}, function (obj) {  setState("mqtt.0.gate.gpio.12", 1); }); //    - ,      on({id: "javascript.0.gate_0.target", ack: false, val: 1}, function (obj) {  setState("mqtt.0.gate.gpio.13", 1); }); on({id: "mqtt.0.gate.in_progress", ack: true, change: 'any'}, function (obj) {  //    " ",      if (obj.state.val === 1) {      //    "",         const target = getState("javascript.0.gate_0.target");      if (target.val === 0) {          // ""          setState("javascript.0.gate_0.current", 2, true);      } else {          // ""          setState("javascript.0.gate_0.current", 3, true);      }  }  //    " ",      if (obj.state.val === 0) {      //    "",         const target = getState("javascript.0.gate_0.target");      if (target.val === 0) {          // ""          setState("javascript.0.gate_0.current", 0, true);      } else {          // ""          setState("javascript.0.gate_0.current", 1, true);      }  } }); 

Nach dem AusfĂŒhren des Skripts können Sie die Eigenschaften des Garagentor-Service konfigurieren:


GarageDoorOpener Service



Kamera


Um HomeKit eine Kamera hinzuzufĂŒgen, wird die "klassische" Methode verwendet. Die Übertragung des Bildes von der Kamera ĂŒber das ffmpeg-Modul ist organisiert. Dadurch wird der Eingabestream verschlĂŒsselt, verschlĂŒsselt und an Homekit ĂŒbergeben.


ZunĂ€chst mĂŒssen Sie ffmpeg auf dem Server installieren, auf dem sich ioBroker befindet.


FĂŒr jede Plattform wird sie auf unterschiedliche Weise installiert. Sie können sie aus der Quelle zusammenstellen oder nach einer fertigen Baugruppe suchen, z. B.: https://www.johnvansickle.com/ffmpeg/ Muss einen libx264-Encoder haben. Sie können den Encoder nach der Installation von ffmpeg mit dem folgenden Befehl ĂŒberprĂŒfen:


 ffmpeg -codecs | grep 264 

Die Ergebnisse sollten eine Zeile des Formulars enthalten:


 DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (decoders: h264 h264_v4l2m2m h264_vdpau ) (encoders: libx264 libx264rgb h264_v4l2m2m ) 

FĂŒr Raspberry Pi 3 können Sie die vorgefertigte Assembly verwenden , die einen Codec mit UnterstĂŒtzung fĂŒr die GPU-Hardwarecodierung enthĂ€lt (h264_omx verbraucht weniger Ressourcen). Sagen Sie es so:


 wget https://github.com/legotheboss/YouTube-files/raw/master/ffmpeg_3.1.4-1_armhf.deb sudo dpkg -i ffmpeg_3.1.4-1_armhf.deb 

Beide Codecs sind in dieser Assembly vorhanden: libx264 und h264_omx


Als NĂ€chstes mĂŒssen Sie die Adresse des Kamerastreams abrufen, der gesendet werden soll (dieser Schritt geht ĂŒber den Rahmen dieses Artikels hinaus). Sie können beispielsweise einen vorgefertigten öffentlichen Stream verwenden .


FĂŒgen Sie nun die Kamera zu Yahka hinzu, geben Sie die Adresse des Streams an und Ă€ndern Sie gegebenenfalls die Parameter des Codecs, der BildgrĂ¶ĂŸe und der Bildrate.


Wichtig: Kombinationen von Parametern sind fĂŒr die korrekte Anzeige der Kamera in Homekit sehr wichtig und hĂ€ngen von der Kamera und dem Stream ab. Dies wirkt sich auch auf die Systemleistung aus Der laufende Prozess von ffmpeg verbraucht viele Ressourcen.


HinzufĂŒgen einer Kamera


Stream-Setup


Kameras werden als separate GerĂ€te außerhalb des Gateways hinzugefĂŒgt und mĂŒssen genau wie das Gateway hinzugefĂŒgt werden


Miniaturansicht der Kamera


Ausstrahlung von der Kamera


Bonus


Als Bonus werde ich ĂŒber die ungewöhnliche Verwendung von Kamerasendungen sprechen.


Mit demselben ffmpeg können Sie anstelle der Kamera versuchen, das Bild und jedes Bild zu ĂŒbertragen. Diese Bilder können auch mit dem Videostream kombiniert werden. Sie können Text, Grafiken und andere Informationen auf dem Bild anzeigen.


Text Overlay Cast


Als Ergebnis erhalten Sie ein interessantes Dashboard. Wenn Sie das Bild regelmĂ€ĂŸig aktualisieren, erhalten Sie dynamische Daten.


Als Beispiel habe ich eine grafische Darstellung der Änderungen einiger Indikatoren in Form eines Bildes (Datei auf der Festplatte) erstellt. Dieses Diagramm wird einmal pro Minute aktualisiert und ĂŒberschreibt das Bild in der Datei.


(Die Funktionen createImage1, createImage2, die Bildung eines Diagramms und das Auferlegen von Text auf ein Bild gehen ĂŒber den Rahmen dieses Artikels hinaus, aber ich werde einen Hinweis geben).

Ich werde Ihnen sagen, wie Sie ein Diagramm in Form eines Bildes erhalten können.


IoBroker bietet eine Standardmethode zum Erstellen von Diagrammen - den Flot-Treiber. Dieser Treiber ist mit einem Webtreiber gekoppelt und zeigt das Ergebnis in einem Browser an. Um das erstellte Diagramm auf dem Server (im Skript) als Bild zu erhalten, wird jedoch ein zusĂ€tzlicher PhantomJS-Treiber benötigt, der einen „Screenshot“ der Seite erstellt (auf der ein Flot-Diagramm gezeichnet wird).


Ich werde jedoch ĂŒber eine alternative Methode zum Erstellen von Diagrammen auf dem Server in einem Skript sprechen.


Es gibt eine solche Chart.js-Bibliothek http://www.chartjs.org/ , mit der Sie gut aussehende Grafiken im Browser zeichnen können (Beispiele http://www.chartjs.org/samples/latest/ ).


Zum Zeichnen wird die „Leinwand“ (Leinwand, Leinwand) des Browsers verwendet. Um mit dieser Bibliothek auf dem Server zu zeichnen, mĂŒssen Sie daher die "Server" -Version der "Canvas" - und DOM-Objekte verwenden. Dies ist, was das chartjs-node-Paket tut ( https://github.com/vmpowerio/chartjs-node ).


Die HauptabhĂ€ngigkeit fĂŒr dieses Paket ist das Canvas-Paket ( https://github.com/Automattic/node-canvas ), das global (oder im iobroker-Ordner) installiert werden sollte. Es ist wichtig, alle AbhĂ€ngigkeiten fĂŒr die Plattform zu installieren, auf der Sie https://github.com/Automattic/node-canvas#compiling ablegen .


Danach können Sie in den Javascript-Treibereinstellungen die Module chart.js und chartjs-node hinzufĂŒgen. Sie sollten korrekt und fehlerfrei installiert werden. Andernfalls behandeln Sie Fehler und beheben Sie sie.


Und dann können Sie ein Skript schreiben.


Unten finden Sie ein Skript fĂŒr ein Beispiel wie Es enthĂ€lt die Verwendung des Verlaufstreibers und verwendet bestimmte Statusnamen.


Achtung! Das Skript hat komplizierte Konstruktionen fĂŒr AnfĂ€nger - Versprechen. Dies ist eine bequeme Möglichkeit, keine Funktionen mit RĂŒckruf zu schreiben, sondern Ketten von Schritten zu erstellen. So ist es beispielsweise zweckmĂ€ĂŸig, Daten aus der Zustandsgeschichte zu erhalten.


 'use strict'; const ChartjsNode = require('chartjs-node'); /** *  sendTo  Promise,      */ function sendToPromise(adapter, cmd, params) { return new Promise((resolve, reject) => { sendTo(adapter, cmd, params, (result) => { resolve(result); }); }); } //    const chartColors = { black: 'rgb(0, 0, 0)', red: 'rgb(255, 99, 132)', orange: 'rgb(255, 159, 64)', yellow: 'rgb(255, 205, 86)', green: 'rgb(75, 192, 192)', blue: 'rgb(54, 162, 235)', purple: 'rgb(153, 102, 255)', grey: 'rgb(201, 203, 207)' }; /** *        * : * @param config -     * @param filename -     * : * @param Promise -    */ function doDraw(config, filename) { //     640x480  var chartNode = new ChartjsNode(640, 480); return chartNode.drawChart(config) .then(() => { //     return chartNode.writeImageToFile('image/png', filename); }); } /** *     ChartJS. * : * @param Promise -    */ function prepareDraw0(){ // ,    var ; //  Promise     return new Promise((resolve, reject)=>{resolve()}) //       ,      .then(()=>{ //  ,   ,      = [ {"val":3,"ack":1,"ts":1539063874301}, {"val":5,"ack":1,"ts":1539063884299}, {"val":5.3,"ack":1,"ts":1539063894299}, {"val":3.39,"ack":1,"ts":1539063904301}, {"val":5.6,"ack":1,"ts":1539063914300}, {"val":-1.3,"ack":1,"ts":1539063924300}, {"val":-6.3,"ack":1,"ts":1539063934302}, {"val":1.23,"ack":1,"ts":1539063944301}, ]; }) //   -    .then(()=>{ const chartJsOptions = { //   -  type: 'line', data: { //    datasets: [ { //   label: '', //  backgroundColor: chartColors.black, borderColor: chartColors.black, //   pointRadius: 3, //    borderWidth: 3, //     ''        data: .map((item) => { return {y: item.val, t: new Date(item.ts)} }), //   -  fill: false, } ] }, options: { //   legend: { labels: { //   fontSize: 20, }, }, //   scales: { //  X xAxes: [{ //  -   type: 'time', display: true, //   scaleLabel: { display: true, labelString: '' }, }], //  Y yAxes: [{ //  -  type: 'linear', display: true, //   scaleLabel: { display: true, labelString: '' }, }] } } }; return chartJsOptions; }); } /** *     ChartJS. *         , *     . * * : * @param hours -  ,     * : * @param Promise -    */ function prepareDraw1(hours){ //   ,      const end = new Date().getTime(), start = end - 3600000*(hours || 1); // 1 =   //  ,       //   var , 2, 1, 2, 2; //  Promise     return new Promise((resolve, reject)=>{resolve()}) //       'mqtt.0.ESP_Easy..Temperature' .then(() => { return sendToPromise('history.0', 'getHistory', { id: 'mqtt.0.ESP_Easy..Temperature', options: { start: start, end: end, aggregate: 'onchange' } } ).then((result) => { //     ''  = result.result; }); }) //       'sonoff.0.chicken2.DS18B20_Temperature' .then(() => { return sendToPromise('history.0', 'getHistory', { id: 'sonoff.0.chicken2.DS18B20_Temperature', options: { start: start, end: end, aggregate: 'onchange' } }).then((result)=>{ //     '2' 2 = result.result; }); }) .then(() => { return sendToPromise('history.0', 'getHistory', { id: 'sonoff.0.sonoff_chicken_vent.DS18B20_Temperature', options: { start: start, end: end, aggregate: 'onchange' } }).then((result)=>{ 1 = result.result; }); }) .then(() => { return sendToPromise('history.0', 'getHistory', { id: 'sonoff.0.chicken2.POWER1', options: { start: start, end: end, aggregate: 'onchange' } }).then((result)=>{ 2 = result.result; }); }) .then(() => { return sendToPromise('history.0', 'getHistory', { id: 'sonoff.0.chicken2.POWER2', options: { start: start, end: end, aggregate: 'onchange' } }).then((result)=>{ 2 = result.result; }); }) //   -    .then(()=>{ const chartJsOptions = { //   -  type: 'line', data: { //    datasets: [ { //           label: ' ('+[.length - 1].val+')', //  backgroundColor: chartColors.blue, borderColor: chartColors.blue, //  . 0 -   pointRadius: 0, //    borderWidth: 3, //     ''        data: .map((item) => { return {y: item.val, t: new Date(item.ts)} }), //   -  fill: false, //   Y yAxisID: 'y-axis-1', },{ label: ' 1 ('+1[1.length - 1].val+')', backgroundColor: chartColors.green, borderColor: chartColors.green, pointRadius: 0, borderWidth: 3, data: 1.map((item) => { return {y: item.val, t: new Date(item.ts)} }), fill: false, yAxisID: 'y-axis-1', },{ label: ' 2 ('+2[2.length - 1].val+')', backgroundColor: chartColors.red, borderColor: chartColors.red, pointRadius: 0, borderWidth: 3, data: 2.map((item) => { return {y: item.val, t: new Date(item.ts)} }), fill: false, yAxisID: 'y-axis-1', },{ label: ' 2  ('+2[2.length - 1].val+')', backgroundColor: chartColors.yellow, borderColor: chartColors.yellow, pointRadius: 0, borderWidth: 1, data: 2.map((item) => { return {y: (item.val) ? 1 : 0, t: new Date(item.ts)} }), fill: true, lineTension: 0, steppedLine: true, yAxisID: 'y-axis-2', },{ label: ' 2  ('+2[2.length - 1].val+')', backgroundColor: chartColors.grey, borderColor: chartColors.grey, pointRadius: 0, borderWidth: 1, data: 2.map((item) => { return {y: (item.val) ? -1 : 0, t: new Date(item.ts)} }), fill: true, lineTension: 0, steppedLine: true, yAxisID: 'y-axis-2', } ] }, options: { //   legend: { labels: { //   fontSize: 20, }, }, //   scales: { //  X xAxes: [{ //  -   type: 'time', display: true, //   scaleLabel: { display: true, labelString: '' }, //    () time: { unit: 'minute', displayFormats: { minute: 'HH:mm' } }, }], //  Y yAxes: [{ //  -  type: 'linear', display: true, //   scaleLabel: { display: true, labelString: '' }, //   -  position: 'left', //   id: 'y-axis-1', },{ type: 'linear', display: true, scaleLabel: { display: true, labelString: '  ' }, ticks: { min: -4, max: 2 }, //   -  position: 'right', id: 'y-axis-2', }] } } }; return chartJsOptions; }); } function createImage(filename, callback){ // filename -  ,       //    prepareDraw1(2) //     .then((result) => { //        return doDraw(result, filename); }) .then(()=>{ if (callback) callback(); }) .catch((err)=>{ console.error(err); }); } 

Broadcast-Bild statt Stream


Das Miniaturbild wird ungefÀhr einmal pro Minute aktualisiert, daher werden wir festlegen, dass das Bild alle 10 Sekunden aktualisiert wird:


 var fs = require('fs'); //  10    schedule("*/10 * * * * *", () => {  createImage1('/tmp/1_new.jpg', ()=> {      fs.renameSync('/tmp/1_new.jpg', '/tmp/1.jpg');  });  createImage2('/tmp/2_new.jpg', ()=> {      fs.renameSync('/tmp/2_new.jpg', '/tmp/2.jpg');  }); }); 

Die Besonderheit ist, dass beim Senden des Bildes das Bild schnell genug ersetzt werden muss, damit ffmpeg nicht abstĂŒrzt :) Daher wird das Bild zuerst in eine Datei umgewandelt und dann in die fĂŒr die Übersetzung verwendete Datei umbenannt.


Geben Sie nun in den Kameraeinstellungen den Namen der generierten Datei anstelle der Stream-Adresse an und fĂŒgen Sie die Einstellungen hinzu, dass das Bild "aktualisiert" wird (Parameter "-loop 1"). Dies wird in den erweiterten Eigenschaften der Kamera konfiguriert. Diese Eigenschaften sind nichts anderes als Befehlszeilenoptionen zum AusfĂŒhren von ffmpeg. Daher sollten Kombinationen von Parametern in der ffmpeg-Dokumentation und in den Beispielen gefunden werden.


Die Eigenschaften sind in zwei Typen unterteilt: zum Erhalten einer „Vorschau“ (ein kleines Kamerabild) und zum Senden. Daher können Sie verschiedene Bildquelldateien angeben, beispielsweise mit unterschiedlichen Details.


Ffmpeg-Startoptionen


Live-Übertragung Bild


Fazit


ioBroker . , . , , .


, Yahka , Material. , HomeKit.


Yahka, HomeKit — Ham, HomeBridge . .

Source: https://habr.com/ru/post/de433798/


All Articles