bobaflu - acessórios de programação no flutter


Este artigo se concentrará na implementação do cliente móvel Flutter.


Qual cliente móvel?


A publicação anterior descreveu o sistema de acessórios de software:
bobaoskit - acessórios, dnssd e WebSocket .


Um análogo de um acessório de software é um objeto real. Lâmpada, interruptor, CD / toca-fitas, rádio, termostato, sensor de temperatura, sensor de movimento, etc. ... Um conjunto de acessórios é determinado pela imaginação e pelo código do programa. Você pode implementar pelo menos um tabuleiro de xadrez. Para esse quadro, você precisa ter um campo de controle ( control ) move , que leva um objeto { from: "e2", to: "e4" } por exemplo, e campos de serviço para redefinir figuras, etc. ... O script acessório processará a solicitação de controle do campo de move , aceite a decisão é se é possível mover a figura e retornará (ou não) o status com a posição das figuras em todo o campo.


Os tipos de acessórios atualmente suportados com funcionalidade mínima são os seguintes: "switch", "sensor de temperatura", "termostato", "radio player".


Sobre o xadrez, não haverá mais discussões. Se for interessante e, nesse caso, bem-vindo ao gato.


Portanto, o bobaoskit.worker funcionamento. Os objetos acessórios existem na memória do computador, você pode ler informações sobre eles, enviar manualmente uma solicitação JSON para a porta WebSocket e ver os eventos recebidos.


Para gerenciamento, criei o aplicativo móvel mais simples.


Por que vibrar?


Nos últimos dois anos, uma ideia vive ativamente em minha mente para estudar programação para dispositivos móveis. Desde que escrevi em JavaScript , estudei soluções que permitem que você não aprenda uma nova linguagem de programação.


Appcelerator Ele começou o estudo com ele. Se a memória não mudar, o SDK está aberto, mas o IDE com várias tarifas.
NativeScript Aqui eu já criei um aplicativo simples que mostra uma lista com fotos. Não foi mais longe.
ReactNative O ataque mais longo dos quadros listados até agora. O maior desafio está sendo iniciado. Eu olhei para o curso. No começo, é claro, interessante, ao que parece. Mas o redux e o overpower falharam. Então ele regularmente tentava começar a escrever, mas o redux teimosamente não se permitia dominar.


Como resultado, não me apeguei a nenhuma decisão (final de 2016). Talvez porque não houvesse tarefa específica, talvez por outras razões.


Mais próximo do outono do passado (2018), o trabalho já estava em andamento no sdk para acessórios de software. Naturalmente, você precisa de um aplicativo móvel. Tudo começou com mdns. Uma vez no meu tempo livre, atualizei o ReactNative, encontrei o plugin react-native-zeroconf e criei o aplicativo. De acordo com as instruções, instalado, fez um link , lançado. O aplicativo de depuração da Expo foi lançado, que não suporta módulos nativos e, portanto, o plug-in mdns não funcionou. Nesse ponto, não havia tempo livre suficiente para criar um aplicativo nativo de reação limpo (sem expo) e testar com ele. O trabalho foi adiado por alguns meses.


Ao mesmo tempo, mais e mais materiais apareceram sobre a flutter na rede. Eu me instalei. A instalação é simples: git clone e adicione ao PATH . O restante já está configurando o Android SDK / Xcode (no meu caso, o Android SDK foi configurado por um longo tempo. Não consigo desenvolver para iOS, porque não sou usuário do macOS) e o Dart SDK (você pode instalá-lo separadamente, mas não necessariamente, pois faz parte da vibração).


Princípio / esquema de trabalho


  • Quando iniciado, o aplicativo procura serviços _bobaoskit._tcp na rede local usando o plug- in flutter_mdns . Existem várias versões deste plug-in, todas com raízes no publicado , mas ele não é compatível com as novas versões do Dart SDK, respectivamente, com muitas bifurcações e compatibilidade adicional. Eu escolhi esta versão porque os outros não resolveram os hosts de vários serviços descobertos de uma só vez.
    Após a detecção e determinação (onResolve), o host é adicionado à lista.
    A página com a lista de serviços descobertos é StatefulWidget , respectivamente, quando detecta / perda de serviços, setState() {...} é chamado
  • Ao escolher um host da lista, uma nova página é criada (também StatefulWidget ), para a qual o host e a port serviço selecionado são transmitidos.
    O objeto BobaosKit responsável pelas comunicações é criado. As respostas são processadas por meio de retornos de chamada, como enquanto eu não estudei muito dardo assíncrono. Mas, a julgar pela documentação digitalizada, o Futures é um análogo da Promise em JS.
    As funções são gravadas para eventos recebidos (sem respostas). EventEmitter para Dart aqui. Eu escrevi o meu muito simples.

  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 recebidos - se o acessório foi removido, adicionado, status atualizado. Ou se todos os acessórios forem removidos ( clear accessories ).


Funções registradas - para atualizar listas, widgets para esses eventos.


  • Uma solicitação é enviada para obter informações sobre todos os acessórios.

Um objeto AccessoryInfo é criado para cada acessório:


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

esse objeto já é um modelo. Inicialmente, escrevi StatefulWidget e setState() {} todos os lugares, mas setState() {} funciona apenas para um widget no qual os ouvintes são registrados. Porém, para o gerenciamento detalhado de acessórios, criei inicialmente novas páginas com Stateful e notei que o status não é atualizado. Como uma solução - ScopedModel usado.


Depois que a lista de acessórios é recebida, para cada um deles, enviamos uma solicitação de status e adicionamos à lista List <AccessoryInfo> . Chame setState() {} , adicionando assim um acessório suportado à interface. Os tipos de acessórios suportados são definidos em ListView.builder e em ./lib/widgets/*.dart . switch/temperature sensor/radio player/thermostat atualmente suportados. O principal trabalho a seguir é adicionar novos e melhorar os widgets existentes.


  • Agora, sobre como criar elementos separados para cada acessório. Por exemplo, considere uma opção.

  @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 um acessório do tipo switch , um elemento é criado na lista geral de acessórios, ao interagir com o qual (onTap) uma solicitação é enviada para obter o valor atual e depois mudar esse valor. ScopedModel permite redesenhar o widget para receber atualizações de status.


Um manipulador de cliques longos não está implementado para este acessório.


Para um player de rádio, é assim:


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

A página AccRadioPlayerControl , que também usa o ScopedModel para gerenciar o estado.


Nisso, toda a descrição do algoritmo de operação do programa está esgotada. Nenhum recurso adicional, como lembrar o último host, classificando acessórios em categorias / salas não é implementado. No momento, tudo é simples.


Os problemas


Vou descrever o principal problema que é agora. Ainda não entendo como detectar uma conexão WebSocket interrompida.


Eu uso: classe WebSocket .


Quando o aplicativo / dispositivo está no modo de suspensão por um longo tempo, a conexão é desconectada. Você precisa voltar para a primeira página e reabrir o serviço descoberto.


Posfácio


Por um lado, Flutter é muito rápido em aprender e desenvolver. O ScopedModel acabou sendo mais compreensível para mim do que o redux.
Dart acabou sendo semelhante ao JavaScript familiar. Digitar + tipos dinâmicos permitirá que todos escrevam da forma mais conveniente.


Dificuldades na escrita de código: aninhamento grande de widgets. O conhecido inferno de retorno de chamada após vibração parece diferente. O modo Vim e % serão úteis.


Agora, alguns pensamentos sobre a Internet das coisas. Recentemente, mais e mais dispositivos / serviços inteligentes que exigem registro na nuvem. Pontos de venda chineses, para os quais você precisa instalar o aplicativo, crie uma conta e somente depois poderá usá-lo.


Assistentes de voz. Alice da Yandex requer sua nuvem para a qual o texto reconhecido é enviado. O Alexa da Amazon funciona de maneira semelhante.


O mais bem sucedido, na minha opinião, é feito pelo Apple HomeKit em conjunto com o Siri. A nuvem é usada para reconhecimento de texto. Interação com dispositivos - na rede local.


Minha opinião é que a nuvem deve existir para sua finalidade: controle remoto, atualização, etc. ... Se o dispositivo puder ser controlado em uma rede local, você precisará fazer isso.


Referências


  1. Repositório de aplicativos
  2. Documentação do Bobaoskit - descreve como instalar o bobaoskit.worker e iniciar o acessório do radio player .

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


All Articles