bobaflu - accesorios de programación en flutter


Este artículo se centrará en la implementación del cliente móvil Flutter.


¿Qué cliente móvil?


La publicación anterior describió el sistema de accesorios de software:
bobaoskit: accesorios, dnssd y WebSocket .


Un análogo de un accesorio de software es un objeto real. Bombilla, interruptor, reproductor de cd / cassette, reproductor de radio, termostato, sensor de temperatura, sensor de movimiento, etc. ... Un conjunto de accesorios está determinado por la imaginación y el código del programa. Puede implementar al menos un tablero de ajedrez. Para tal tablero, debe tener un move campo de control ( control ), que toma un objeto { from: "e2", to: "e4" } por ejemplo, y campos de servicio para restablecer figuras, etc. ... El script accesorio procesará la solicitud para controlar el campo de move , aceptar la decisión es si es posible mover la figura y devolverá (o no) el estado con la posición de las figuras en todo el campo.


Los tipos de accesorios admitidos actualmente con una funcionalidad mínima son los siguientes: "interruptor", "sensor de temperatura", "termostato", "reproductor de radio".


Sobre el ajedrez, no habrá más discusión. Si es interesante y en ese caso, bienvenido a cat.


Entonces bobaoskit.worker funcionamiento. Existen objetos accesorios en la memoria de la computadora, puede leer información sobre ellos, puede enviar manualmente una solicitud JSON al puerto WebSocket y ver los eventos entrantes.


Para la administración, hice la aplicación móvil más simple.


¿Por qué revolotear?


Durante los últimos años, una idea ha estado viviendo activamente en mi cabeza para estudiar programación para dispositivos móviles. Como escribo en JavaScript , estudié soluciones que le permiten no aprender un nuevo lenguaje de programación.


Appcelerator Comenzó el estudio con él. Si la memoria no cambia, el SDK está abierto, pero el IDE con varias tarifas.
NativeScript Aquí ya he creado una aplicación simple que muestra una lista con imágenes. No fue más lejos.
ReactNative El asalto más largo de los marcos enumerados hasta ahora. El mayor desafío es comenzar. Miré el curso. Al principio es claro, interesante, resulta. Pero redux y después de la dominación fallaron. Luego intentaba comenzar a escribir regularmente, pero redux tercamente no se permitía dominar.


Como resultado, no estaba apegado a ninguna decisión en ese momento (finales de 2016). Quizás porque no había una tarea específica, quizás por otras razones.


Más cerca de la caída del pasado (2018), ya se estaba trabajando en el SDK para accesorios de software. Naturalmente, necesitas una aplicación móvil. Todo comenzó con mdns. Una vez en mi tiempo libre, actualicé ReactNative, encontré el complemento react-native-zeroconf y creé la aplicación. De acuerdo con las instrucciones, instalado, hizo un link , lanzado. Se lanzó la aplicación de depuración de Expo, que no admite módulos nativos y, en consecuencia, el complemento mdns no funcionó. En este punto, no había suficiente tiempo libre para crear una aplicación nativa de reacción limpia (sin expo) y probar con ella. El trabajo fue pospuesto por un par de meses.


Al mismo tiempo, aparecieron más y más materiales sobre el flutter en la red. Me instalé yo mismo. La instalación es simple: git clone y agregue a PATH . El resto ya está configurando el SDK / Xcode de Android (en mi caso, el SDK de Android se ha configurado durante mucho tiempo. No puedo desarrollar para iOS, porque no soy un usuario de macOS) y el SDK de Dart (puede instalarlo por separado, pero no necesariamente, ya que es parte del flutter).


Principio / esquema de trabajo


  • Cuando se inicia, la aplicación busca los servicios _bobaoskit._tcp en la red local utilizando el complemento flutter_mdns . Hay varias versiones de este complemento, todas tienen su origen en la publicada , pero no es compatible con las nuevas versiones del Dart SDK, respectivamente, muchas bifurcaciones y compatibilidad añadida. Elegí esta versión porque las otras no resolvieron los hosts de varios servicios descubiertos a la vez.
    Tras la detección y determinación (onResolve), el host se agrega a la lista.
    La página con la lista de servicios descubiertos es StatefulWidget , respectivamente, cuando detecta / pierde servicios, se setState() {...}
  • Al elegir un host de la lista, se crea una nueva página (también StatefulWidget ), a la que se transmiten el host y el port servicio seleccionado.
    Se BobaosKit objeto BobaosKit responsable de las comunicaciones. Las respuestas se procesan mediante devoluciones de llamada, como mientras que no estudié mucho dardo asíncrono. Pero a juzgar por la documentación escaneada, Futures es un análogo de Promise en JS.
    Las funciones se registran para eventos entrantes (sin respuestas). EventEmitter para Dart aquí. Escribí mi muy simple.

  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']); } }); } 

Eventos entrantes: si el accesorio se ha eliminado, agregado, estado actualizado. O si se quitan todos los accesorios ( clear accessories ).


Funciones registradas: para actualizar listas, widgets para estos eventos.


  • Se envía una solicitud de información sobre todos los accesorios.

Se crea un objeto AccessoryInfo para cada accesorio:


 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(); } } 

Este objeto ya es un modelo. Inicialmente, escribí setState() {} y setState() {} todas partes, pero setState() {} solo funciona para un widget dentro del cual los oyentes están registrados. Pero para la gestión detallada de accesorios, inicialmente creé nuevas páginas con Stateful y noté que el estado no se actualiza. Como solución, utilizó ScopedModel .


Después de recibir la lista de accesorios, para cada uno de ellos enviamos una solicitud de estado y la agregamos a la lista List <AccessoryInfo> . Llame a setState() {} , agregando así un accesorio compatible a la interfaz. Los tipos de accesorios compatibles se definen en ListView.builder y en ./lib/widgets/*.dart . switch/temperature sensor/radio player/thermostat admitidos actualmente. El principal trabajo por delante es agregar nuevos y mejorar los widgets existentes.


  • Ahora sobre cómo crear elementos separados para cada accesorio. Por ejemplo, considere un cambio.

  @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 }, )); })); } 

Para un accesorio de tipo switch , se crea un elemento en la lista general de accesorios, al interactuar con el cual (onTap) se envía una solicitud para obtener el valor actual, y luego para cambiar este valor. ScopedModel permite volver a dibujar el widget para las actualizaciones de estado entrantes.


No se implementa un controlador de clic largo para este accesorio.


Para un reproductor de radio, se ve así:


  onLongPress: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => AccRadioPlayerControl( info: info, bobaos: bobaos, ))); }, 

Se AccRadioPlayerControl página AccRadioPlayerControl , que también usa ScopedModel para administrar el estado.


En esto, se agota la descripción completa del algoritmo de operación del programa. No se implementan características adicionales, como recordar el último host, clasificar los accesorios en categorías / habitaciones. Por el momento, todo es simple.


Los problemas


Describiré el problema principal que es ahora. Todavía no entiendo cómo detectar una conexión WebSocket rota.


Yo uso: clase WebSocket .


Cuando la aplicación / dispositivo está en modo de suspensión durante mucho tiempo, la conexión se desconecta. Debe volver a la primera página y volver a abrir el servicio descubierto.


Epílogo


Por un lado, Flutter es bastante rápido en aprender y desarrollarse. ScopedModel para mí resultó ser redux más comprensible.
Dart resultó ser similar a JavaScript familiar. Escribir + tipos dinámicos permitirá que todos puedan escribir de la manera más conveniente.


Dificultades para escribir código: gran anidamiento de widgets. El conocido infierno de devolución de llamada después del aleteo se ve de manera diferente. Vim-mode y % serán útiles.


Ahora algunas reflexiones sobre IoT. Recientemente, cada vez más dispositivos / servicios inteligentes que requieren registrarse en la nube. Puntos de venta chinos, para los cuales necesita instalar la aplicación, crear una cuenta, y solo después de eso puede usarla.


Asistentes de voz. Alice de Yandex requiere su nube a la que se envía el texto reconocido. Alexa de Amazon funciona de manera similar.


El más exitoso, en mi opinión, es hecho por Apple HomeKit junto con Siri. La nube se usa para el reconocimiento de texto. Interacción con dispositivos: en la red local.


Mi opinión es que la nube debe existir para su propósito: control remoto, actualización, etc. ... Si el dispositivo se puede controlar en una red local, entonces debe hacerlo.


Referencias


  1. Repositorio de aplicaciones
  2. Documentación de Bobaoskit : describe cómo instalar bobaoskit.worker y ejecutar el accesorio del radio player .

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


All Articles