
Dieser Artikel konzentriert sich auf die Implementierung des mobilen Flutter-Clients.
Welcher mobile Client?
In der vorherigen Veröffentlichung wurde das System des Softwarezubehörs beschrieben:
bobaoskit - Zubehör, dnssd und WebSocket .
Ein Analogon eines Software-Zubehörs ist ein echtes Objekt. Glühbirne, Schalter, CD- / Kassettenrekorder, Radioplayer, Thermostat, Temperatursensor, Bewegungssensor usw. ... Ein Satz Zubehör wird durch Vorstellungskraft und Programmcode bestimmt. Sie können mindestens ein Schachbrett implementieren. Für eine solche Karte muss ein Kontrollfeld ( control
) move
, das beispielsweise ein Objekt { from: "e2", to: "e4" }
und Servicefelder zum Zurücksetzen von Zahlen usw. enthält. Das Zubehörskript verarbeitet die Anforderung zum Steuern des move
, akzeptieren Die Entscheidung ist, ob es möglich ist, die Figur zu bewegen, und ob der Status mit der Position der Figuren im gesamten Feld zurückgegeben wird (oder nicht).
Derzeit unterstützte Zubehörarten mit minimaler Funktionalität sind: "Schalter", "Temperatursensor", "Thermostat", "Radio-Player".
Über Schach wird es keine weitere Diskussion geben. Wenn es interessant ist und in diesem Fall, willkommen bei Katze.
Also ist bobaoskit.worker
Betrieb. Im Speicher des Computers befinden sich Zubehörobjekte. Sie können Informationen dazu lesen, eine JSON
Anforderung manuell an den WebSocket
Port senden und eingehende Ereignisse anzeigen.
Für das Management habe ich die einfachste mobile Anwendung erstellt.
Warum flattern?
In den letzten Jahren lebte in meinem Kopf eine Idee, Programmierung für mobile Geräte zu studieren. Da ich in JavaScript
schreibe, habe ich Lösungen studiert, mit denen Sie keine neue Programmiersprache lernen können.
Appcelerator
Er begann das Studium mit ihm. Wenn sich der Speicher nicht ändert, ist das SDK geöffnet, aber die IDE mit verschiedenen Tarifen.
NativeScript
Hier habe ich bereits eine einfache Anwendung erstellt, die eine Liste mit Bildern zeigt. Es ging nicht weiter.
ReactNative
Der längste Angriff der bisher aufgeführten Frameworks. Die größte Herausforderung besteht darin, loszulegen. Ich habe mir den Kurs angesehen. Zunächst ist es klar, interessant, es stellt sich heraus. Aber redux
und nach Überwältigung scheiterten. Dann versuchte er regelmäßig zu schreiben, aber redux
erlaubte sich nicht, sich zu überwältigen.
Infolgedessen war ich damals (Ende 2016) an keine Entscheidung gebunden. Vielleicht, weil es keine bestimmte Aufgabe gab, vielleicht aus anderen Gründen.
Näher am Herbst der Vergangenheit (2018) wurde bereits an der SDK für Software-Zubehör gearbeitet. Natürlich benötigen Sie eine mobile Anwendung. Alles begann mit mdns. In meiner Freizeit habe ich ReactNative aktualisiert, das Plugin "react-native-zeroconf" gefunden und die Anwendung erstellt. Gemäß den Anweisungen installiert, einen link
, gestartet. Die Expo-Debugging-Anwendung wurde gestartet, die keine nativen Module unterstützt, und dementsprechend funktionierte das mdns-Plugin nicht. Zu diesem Zeitpunkt war nicht genügend Zeit vorhanden, um eine saubere (ohne Expo) reaktionsnative Anwendung zu erstellen und damit zu testen. Die Arbeit wurde um einige Monate verschoben.
Gleichzeitig erschienen immer mehr Materialien über das flutter
im Netzwerk. Ich habe mich installiert. Die Installation ist einfach: git clone
und zu PATH
hinzufügen. Der Rest richtet bereits das Android SDK / Xcode ein (in meinem Fall ist das Android SDK bereits seit langer Zeit eingerichtet. Ich kann es nicht für iOS entwickeln, da ich kein MacOS-Benutzer bin) und das Dart SDK (kann separat installiert werden, aber nicht unbedingt, da es Teil des Flatterns ist).
Prinzip / Arbeitsschema
- Beim Start sucht die Anwendung mithilfe des Plugins
_bobaoskit._tcp
im lokalen Netzwerk nach _bobaoskit._tcp
Diensten. Es gibt mehrere Versionen dieses Plugins, die alle ihre Wurzeln in der veröffentlichten Version haben. Es ist jedoch nicht mit neuen Versionen des Dart SDK kompatibel. Viele haben eine gegabelte und zusätzliche Kompatibilität. Ich habe diese Version gewählt, weil die anderen die Hosts mehrerer erkannter Dienste nicht gleichzeitig aufgelöst haben.
Bei Erkennung und Bestimmung (onResolve) wird der Host zur Liste hinzugefügt.
Die Seite mit der Liste der setState() {...}
Dienste lautet StatefulWidget
. Wenn Dienste setState() {...}
/ verloren werden, wird setState() {...}
aufgerufen - Bei der Auswahl eines Hosts aus der Liste wird eine neue Seite (auch
StatefulWidget
) erstellt, an die der host
und der port
ausgewählten Dienstes übertragen werden.
Das für die Kommunikation verantwortliche BobaosKit
Objekt wird erstellt. Antworten werden durch Rückrufe verarbeitet während ich nicht viel asynchronen Dart studierte. Nach der gescannten Dokumentation ist Futures
jedoch ein Analogon zu Promise
in JS.
Funktionen werden für eingehende Ereignisse aufgezeichnet (keine Antworten). EventEmitter
hier nach EventEmitter
für Dart EventEmitter
. Ich habe meine sehr einfache geschrieben.
void registerListener(String name, Function cb) { this._events.add(new BobaosKitCallback(name, cb)); } void removeAllListeners() { this._events = []; } void emitEvent(String name, dynamic params) { // call all listeners List<BobaosKitCallback> foundCallbacks = this._events.where((t) => t.name == name).toList(); foundCallbacks.forEach((f) => f.cb(params)); } ... ... void listen() { this.ws.listen((text) { var json = jsonDecode(text); if (json.containsKey('response_id')) { .... } else { // response_id - this.emitEvent(json['method'], json['payload']); } }); }
Eingehende Ereignisse - Wenn das Zubehör entfernt, hinzugefügt und der Status aktualisiert wurde. Oder wenn alle Zubehörteile entfernt sind ( clear accessories
).
Registrierte Funktionen - zum Aktualisieren von Listen, Widgets für diese Ereignisse.
- Es wird eine Anfrage für Informationen zu sämtlichem Zubehör gesendet.
Für jedes Zubehör wird ein AccessoryInfo-Objekt erstellt:
import 'package:scoped_model/scoped_model.dart'; // AccessoryInfo extends Model // so, when accessory value is updated it descends down to // all widgets inside ScopedModelDescendant class AccessoryInfo extends Model { dynamic id; dynamic type; String name; String job_channel; List control; List status; bool selected; Map<dynamic, dynamic> currentState; AccessoryInfo(Map<dynamic, dynamic> obj) { this.id = obj['id']; this.type = obj['type']; this.name = obj['name']; this.job_channel = obj['job_channel']; this.control = obj['control']; this.status = obj['status']; this.currentState = {}; } void updateCurrentState(key, value) { currentState[key] = value; notifyListeners(); } void notify() { notifyListeners(); } }
Dieses Objekt ist bereits ein Modell. Anfangs habe ich überall StatefulWidget
und setState() {}
, aber setState() {}
funktioniert nur für ein Widget, in dem Listener registriert sind. Für eine detaillierte Verwaltung der Zubehörteile habe ich zunächst neue Stateful
Seiten erstellt und festgestellt, dass der Status nicht aktualisiert wird. Als Lösung - verwendet ScopedModel
.
Nachdem die Liste der Zubehörteile eingegangen ist, senden wir für jedes Zubehör eine Statusanfrage und fügen sie der Liste List <AccessoryInfo>
. Rufen Sie setState() {}
und fügen Sie der Schnittstelle ein unterstütztes Zubehör hinzu. Unterstützte Zubehörtypen werden in ListView.builder
und in ./lib/widgets/*.dart
. Derzeit unterstützter switch/temperature sensor/radio player/thermostat
. Die Hauptarbeit besteht darin, neue hinzuzufügen und vorhandene Widgets zu verbessern.
- Nun erfahren Sie, wie Sie für jedes Zubehör separate Elemente erstellen. Betrachten Sie beispielsweise einen Schalter.
@override Widget build(BuildContext context) { return new ScopedModel<AccessoryInfo>( model: info, child: ScopedModelDescendant<AccessoryInfo>( builder: (context, child, model) { var cardColor = Theme.of(context).cardColor; dynamic switchState = model.currentState['state']; if (switchState is bool) { if (switchState) { cardColor = Colors.deepPurple; } else { cardColor = Theme.of(context).cardColor; } } return Card( color: cardColor, child: ListTile( selected: false, leading: new Icon(Icons.lightbulb_outline), title: new Text("${model.name}"), onTap: () { // to control accessory value // get status value at first bobaos.getStatusValue( model.id, "state", (bool err, Object payload) { if (err) { return print('error ocurred $payload'); } if (payload is Map) { dynamic currentValue = payload['status']['value']; bool newValue; if (currentValue is bool) { // invert newValue = !currentValue; } else { newValue = false; } // then send new value bobaos.controlAccessoryValue( model.id, {"state": newValue}, (bool err, Object payload) { if (err) { return print('error ocurred $payload'); } }); } }); }, onLongPress: () { // TODO: dialog with additional funcs }, )); })); }
Für ein Zubehör vom Typ switch
wird in der allgemeinen Zubehörliste ein Element erstellt, wenn bei der Interaktion (onTap) eine Anforderung zum Abrufen des aktuellen Werts gesendet wird, um diesen Wert dann zu wechseln. ScopedModel
können ScopedModel
das Widget für eingehende Statusaktualisierungen neu zeichnen.
Ein Long-Click-Handler ist für dieses Zubehör nicht implementiert.
Für einen Radioplayer sieht es so aus:
onLongPress: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => AccRadioPlayerControl( info: info, bobaos: bobaos, ))); },
Die Seite AccRadioPlayerControl
wird AccRadioPlayerControl
, auf der auch das ScopedModel
zum Verwalten des Status verwendet wird.
Damit ist die gesamte Beschreibung des Programmoperationsalgorithmus erschöpft. Es sind keine zusätzlichen Funktionen implementiert, z. B. das Speichern des letzten Hosts und das Sortieren von Zubehör in Kategorien / Räume. Im Moment ist alles einfach.
Die Probleme
Ich werde das Hauptproblem beschreiben, das jetzt ist. Ich verstehe immer noch nicht, wie eine unterbrochene WebSocket
Verbindung WebSocket
.
Ich benutze: WebSocket-Klasse .
Wenn sich die Anwendung / das Gerät längere Zeit im Ruhemodus befindet, wird die Verbindung getrennt. Sie müssen zur ersten Seite zurückkehren und den erkannten Dienst erneut öffnen.
Nachwort
Einerseits lernt und entwickelt Flutter ziemlich schnell. ScopedModel erwies sich für mich als verständlicher als Redux.
Dart ähnelte dem bekannten JavaScript. Wenn Sie + dynamische Typen eingeben, kann jeder so bequem schreiben.
Schwierigkeiten beim Schreiben von Code: große Verschachtelung von Widgets. Die bekannte Rückrufhölle nach dem Flattern sieht anders aus. Vim-Modus und %
sind nützlich.
Nun ein paar Gedanken zum IoT. In letzter Zeit immer mehr intelligente Geräte / Dienste, die eine Registrierung in der Cloud erfordern. Chinesische Verkaufsstellen, für deren Verwendung Sie die Anwendung installieren müssen, erstellen ein Konto, und erst danach können Sie es verwenden.
Sprachassistenten. Alice von Yandex benötigt ihre Cloud, in die der erkannte Text gesendet wird. Amazonas Alexa arbeitet auf ähnliche Weise.
Das erfolgreichste ist meiner Meinung nach Apple HomeKit in Zusammenarbeit mit Siri. Die Cloud wird zur Texterkennung verwendet. Interaktion mit Geräten - im lokalen Netzwerk.
Meiner Meinung nach muss die Cloud für ihren Zweck existieren: Fernsteuerung, Aktualisierung usw. ... Wenn das Gerät in einem lokalen Netzwerk gesteuert werden kann, müssen Sie dies tun.
Referenzen
- Anwendungs-Repository
- Bobaoskit-Dokumentation - beschreibt, wie Sie bobaoskit.worker installieren und das
radio player
Zubehör starten.