Desenvolvimento

29 jun, 2017

Crie um app móvel de home assistant com serviços Watson e IoT Platform da IBM – Parte 02

Publicidade

Na primeira parte deste artigo, fizemos toda a preparação do nosso cenário. Preparamos o Watson no Bluemix, importamos o Watson Conversation Service, preparamos os serviços de Object Storage no Bluemix, o serviço Watson IoT Platform e o Raspberry Pi. Também desenvolvemos o Node-RED no Raspberry Pi.

Agora, na sequência, faremos o desenvolvimento final do aplicativo móvel. Veremos como combinar o poder do Watson com a simplicidade da plataforma IBM IoT para criar um home assistant, que controlará alguns dispositivos eletrônicos básicos. Depois, você pode usar este tutorial como um guia para construir seu próprio aplicativo.

7. Desenvolva o aplicativo móvel

Como este aplicativo é semelhante a uma interface de mensagens de bate-papo, usei um widget de interface de usuário iOS popular chamado JSQMessagesViewController. Existe um código de exemplo semelhante que eu poderia usar, mas está escrito no Objective C. No entanto, ainda é uma boa referência para usar.

Desenvolver a interface do usuário

Prepare o projeto Xcode

No Xcode, crie um Aplicativo de Visualização Única clicando em File > Project. Selecione Single View Application e especifique o Nome do Produto: Home Assistant.

Vá para a pasta do projeto e inicialize o CocoaPods. Um Podfile é gerado.

pod init

Adicione esta linha ao Podfile para instalar o widget JSQMessagesViewController.

pod 'JSQMessagesViewController'

Execute o comando para instalar as dependências JSQMessagesViewController. UmA ÁREA de trabalho Xcode é geradA. Você deve reabrir o Xcode usando a Área de Trabalho (*.wcworkspace) em vez do Projeto (*.xcodeproj).

pod install

No arquivo ViewController.swift, importe o módulo JSQMessagesViewController e altere a classe ViewController para herdar da classe JSQMessagesViewController. Declare a série de mensagens para manter as mensagens de bate-papo.

import JSQMessagesViewController
class ViewController: JSQMessagesViewController {
  var messages = [JSQMessage]()
}

Finalmente, digite o seguinte código em Info.plist para ativar o microfone.

<key>NSMicrophoneUsageDescription</key>
  <string>Need microphone to talk to Watson</string><

Configure a interface do usuário e crie uma extensão

Para configurar a interface do usuário e criar uma extensão:

Crie um arquivo chamado UIExt.swift para conter toda a lógica relacionada à UI como uma extensão Swift. Então, declare uma extensão.

extension ViewController {
}

Crie uma função SetupUI() que:

  • Inicializa o title, senderId e senderDisplayName.
  • Cria um botão de microfone e registra eventos touchDown e touchUpInside para funções de retorno callback.
  • Registra um item de menu (sintetiza para a função callback).
func setupUI() {
    self.title = "Watson Chat"
    self.senderId = UIDevice.current.identifierForVendor?.uuidString
    self.senderDisplayName = UIDevice.current.identifierForVendor?.uuidString
     
    JSQMessagesCollectionViewCell.registerMenuAction(#selector(synthesize(sender:)))
     
    // Create mic button
    let microphoneImage = UIImage(named:"microphone")!
    let microphoneButton = UIButton(type: .custom)
    microphoneButton.setImage(microphoneImage, for: .normal)
    microphoneButton.imageView?.contentMode = UIViewContentMode.scaleAspectFit
    self.inputToolbar.contentView.leftBarButtonItem = microphoneButton
     
    // Add press and release mic button
    microphoneButton.addTarget(self, action:#selector(didPressMicrophoneButton), for: .touchDown)
    microphoneButton.addTarget(self, action:#selector(didReleaseMicrophoneButton), for: .touchUpInside)
     
    setAudioPortToSpeaker()
  }

Adicione o ícone do microfone como Ativo/Assets. Clique em Assets.xcassets e arraste os arquivos de ícone para a área de trabalho para criar os conjuntos de imagens.

Implementar funções de gerenciamento para a interface do usuário

No arquivo UIExt.swift:

Adicione as seguintes funções para lidar com o evento de pressionamento do botão do microfone. Quando você pressiona este botão para falar um comando, ele começa a transmitir para o serviço Speech to Text do Watson.

func didPressMicrophoneButton(sender: UIButton) {
    let microphonePressedImage = UIImage(named:"microphone_pressed")!
    sender.setImage(microphonePressedImage, for: .normal)
    AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
    // Clear the input text
    self.inputToolbar.contentView.textView.text = ""
    // speech-to-text startStreaming
    sttStartStreaming()
  }

Adicione as seguintes funções para lidar com o evento de liberação do botão do microfone, que interrompe a transmissão para o serviço Speech to Text do Watson.

func didReleaseMicrophoneButton(sender: UIButton){
    let microphoneImage = UIImage(named:"microphone")!
    sender.setImage(microphoneImage, for: .normal)
    // speech-to-text stop streaming
    self.sttStopStreaming()
  }

Faça um override da função didPressSend() para manipular quando o botão enviar é pressionado. O código adiciona a mensagem à matriz de mensagens, envia uma requisição e recebe uma resposta do serviço Conversation do Watson.

override func didPressSend(
_ button: UIButton!, 
withMessageText text: String!, 
senderId: String!, 
senderDisplayName: String!, 
date: Date!) {
    send(text)
  }

Substituir as funções de retorno de chamada/callback da UI

No arquivo UIExt.swift, adicione as seguintes funções:

Faça um override do callback collectionView| Função numberOfItemsInSection para retornar o número de contagem de mensagens na matriz.

override func collectionView(_ collectionView: UICollectionView, 
    numberOfItemsInSection section: Int) -> Int {
    return self.messages.count
  }

Faça um override do callback collectionView | Função cellForItemAt que define o texto e processa a cor de uma célula específica no caminho do índice.

  override func collectionView(_ collectionView: UICollectionView, 
    cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
    let message = self.messages[indexPath.item]
    if !message.isMediaMessage {
      if message.senderId == self.senderId {
        cell.textView.textColor = UIColor(R: 0x72, G: 0x9B, B: 0x79)
      } else {
        cell.textView.textColor = UIColor(R: 0x47, G: 0x5B, B: 0x63)
      }
      let attributes : [String:AnyObject] = 
          [NSForegroundColorAttributeName:cell.textView.textColor!, NSUnderlineStyleAttributeName: 1 as AnyObject]
      cell.textView.linkTextAttributes = attributes
    }
    return cell
  }

Faça um override do callback collectionView | Função messageBubbleImageDataForItemAt para retornar se é uma bolha de entrada ou de saída para uma célula específica no caminho do índice.

override func collectionView(_ collectionView: JSQMessagesCollectionView!, 
    messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
    let data = messages[indexPath.row]
    switch(data.senderId) {
    case self.senderId:
      return self.outgoingBubble
    default:
      return self.incomingBubble
    }
  }

Faça um override do callback collectionView  | Função atributedTextForMessageBubbleTopLabelAt para definir e retornar o nome do remetente como o rótulo em cima da bolha da mensagem, ao lado dos atributos de estilo.

override func collectionView(_ collectionView: JSQMessagesCollectionView!, 
    attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath!) -> NSAttributedString! {
    if let message = firstMessage(at: indexPath) {
      let paragraphStyle = NSMutableParagraphStyle()
      paragraphStyle.alignment = NSTextAlignment.left
      let attrs = [
        NSParagraphStyleAttributeName: paragraphStyle,
        NSBaselineOffsetAttributeName: NSNumber(value: 0),
        NSForegroundColorAttributeName: UIColor(R: 0x1e, G: 0x90, B: 0xff)
      ]
      return NSAttributedString(string: message.senderDisplayName, attributes: attrs)
    } else {
      return nil
    }
  }

Faça um override do callback collectionView | Função HeightForMessageBubbleTopLabelAt para retornar a altura do rótulo do texto.

override func collectionView(_ collectionView: JSQMessagesCollectionView!, 
  layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, 
  heightForMessageBubbleTopLabelAt indexPath: IndexPath!) -> CGFloat {
    if let _ = firstMessage(at: indexPath) {
      return kJSQMessagesCollectionViewCellLabelHeightDefault
    } else {
      return 0.0
    }
  }

Funções de utilidade

Finalmente, também no arquivo UIExt.swift, adicione as seguintes funções:

A função send() reproduz um som, acrescenta a mensagem enviada à matriz de mensagens, chama a função finishSendingMessage() e chama a função conversationRequestResponse() que lida com a interação com o serviço Conversation do Watson.

func send(_ text: String) {
    setAudioPortToSpeaker()
    JSQSystemSoundPlayer.jsq_playMessageSentSound()
    let message = JSQMessage(senderId: self.senderId, senderDisplayName: self.senderDisplayName, date: Date(), text: text)    
    self.messages.append(message!)
    self.finishSendingMessage(animated: true)
    self.conversationRequestResponse(text)
  }

A função firstMessage() retorna a primeira mensagem que não é anterior ao mesmo remetente para indicar o remetente na parte superior da bolha de texto.

func firstMessage(at: IndexPath) -> JSQMessage! {
    let message = self.messages[at.item]
    if message.senderId == self.senderId {
      return nil
    }
    if at.item - 1 > 0 {
      let previousMessage = self.messages[at.item-1]
      if previousMessage.senderId == message.senderId {
        return nil
      }
    }
    return message
  }

A função didReceiveConversationResponse() é chamada quando o Conversation do Watson retorna uma resposta. A função reproduz um som, acrescenta a mensagem à matriz de mensagens e envia a resposta ao serviço Text to Speech do Watson para sintetizar a frase. Finalmente, ele chama a função finishReceiveMessage().

func didReceiveConversationResponse(_ response: [String]) {
    let sentence = re,sponse.joined(separator: " ")
    if sentence == "" { return }
    setAudioPortToSpeaker()
    JSQSystemSoundPlayer.jsq_playMessageReceivedSound()
    let message = JSQMessage(senderId: "Home Assistant", senderDisplayName: "Home Assistant", date: Date(), text: sentence)
    self.messages.append(message!)
     
    DispatchQueue.main.async {
      // text-to-speech synthesize
      self.ttsSynthesize(sentence)
      self.reloadMessagesView()
      self.finishReceivingMessage(animated: true)
    }
  }

Veja o arquivo UIExt.swift para a implementação completa (incluído nos arquivos em Obter o código/Get the code)

Interface com os Serviços Watson

Agora é hora de implementar a interface com o Serviço do Watson/Watson Service.

Prepare o SDK do Watson

Instale o SDK do Watson Swift. Você deve usar o Carthage para fazer isso e uma maneira de instalar o Carthage é usando Homebrew (um gerenciador de pacotes para MacOS).

Instale o Homebrew.

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Instale Carthage.

brew update
brew install carthage

Crie um Cartfile na pasta do projeto e adicione a seguinte linha.

github "https://github.com/watson-developer-cloud/swift-sdk"

Instale o framework do Swift SDK.

carthage update --platform iOS

Se você receber um erro semelhante ao:

Module compiled with Swift 3.1 cannot be imported in Swift 3.0.2

Em seguida, recompile os binários com este comando:

carthage update --platform iOS --no-use-binaries

Adicione o framework do Swift SDK ao projeto, navegando para General > Linked Frameworks > Libraries. Clique no ícone +.

Clique em Add Other e navegue até a pasta Carthage/Build/iOS. Selecione os frameworks: TextToSpeechV1.framework, SpeechToTextV1.framework, ConversationV1.framework e RestKit.framework.

Quando você terminar, você deve ter os seguintes frameworks adicionados.

Copie os frameworks em seu aplicativo para torná-los acessíveis em tempo de execução. Vá para a guia Build Phases, clique no ícone + e selecione New Run Script Phase.

No Script de Execução, especifique:

/usr/local/bin/carthage copy-frameworks

Especifique os seguintes Arquivos de Entrada clicando no ícone +.

$(SRCROOT)/Carthage/Build/iOS/TextToSpeechV1.framework
$(SRCROOT)/Carthage/Build/iOS/SpeechToTextV1.framework
$(SRCROOT)/Carthage/Build/iOS/ConversationV1.framework
$(SRCROOT)/Carthage/Build/iOS/RestKit.framew

Finalmente, insira as seguintes linhas no arquivo Info.plist como código fonte para se conectar à IoT Plataform do Watson.

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>watsonplatform.net</key>
    <dict>
      <key>NSTemporaryExceptionRequiresForwardSecrecy</key>
      <false/>
      <key>NSIncludesSubdomains</key>
      <true/>
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <key>NSTemporaryExceptionMinimumTLSVersion</key>
       <string>TLSv1.0</string>
    </dict>
  </dict>
</dict>

Inicialização

Crie um arquivo chamado WatsonExt.swift para conter o código que interage com os serviços Text to Speech, Speech to Text e Conversation do Watson. Declare uma extensão para o ViewController.

Para inicializar o serviço Text to Speech do Watson, obtenha o nome de usuário e a senha das Credenciais de Serviço do Bluemix.

textToSpeech = TextToSpeech(username: Credentials.TextToSpeechUsername,
                            password: Credentials.TextToSpeechPassword)

Para inicializar o serviço Speech to Text do Watson, obtenha o nome de usuário e a senha das Credenciais do Serviço Bluemix.

speechToTextSession = SpeechToTextSession(username: Credentials.SpeechToTextUsername,
                                          password: Credentials.SpeechToTextPassword)

Adicione um callback para lidar com os resultados de speech-to-text.

speechToTextSession?.onResults = onResults

Finalmente, para inicializar o serviço Conversation do Watson, obtenha o nome de usuário e a senha das Credenciais do Serviço Bluemix. Além disso, você precisa do WORKSPACE_ID. Na primeira conexão, o Serviço Conversation define um diálogo conversation_start que retorna a resposta inicial (geralmente configurada como saudação). Portanto, a função didReceiveConversationResponse() é chamada para anexar a mensagem e usar o serviço Text to Speech para sintetizar o texto. Isso salva o contexto, que será usado para uma interação subsequente.

conversation = Conversation(username: Credentials.ConversationUsername,
                            password: Credentials.ConversationPassword,
                            version: "2017-03-12")
    let failure = { (error: Error) in print(error) }
    conversation?.message(withWorkspace: Credentials.ConversationWorkspaceID, failure: failure) {
      response in
      print("output.text: \(response.output.text)")
      self.didReceiveConversationResponse(response.output.text)
      self.context = response.context
    }

Serviço Speech to Text do Watson

A função sttStartStreaming() é usada para se conectar ao serviço Speech to Text e pode iniciar a requisição de transmissão. Ela também inicia a entrada do microfone. A função é chamada quando um usuário pressiona o botão do microfone.

func sttStartStreaming() {
    // define settings
    var settings = RecognitionSettings(contentType: .opus)
    settings.continuous = true
    settings.interimResults = true
 
    self.speechToTextSession?.connect()
    self.speechToTextSession?.startRequest(settings: settings)
    self.speechToTextSession?.startMicrophone()
  }

A função sttStopStreaming() é usada para parar o serviço Speech-to-Text. Ela também para a entrada do microfone. A função é chamada quando um usuário libera o botão do microfone.

func sttStopStreaming() {
    self.speechToTextSession?.stopMicrophone()
    self.speechToTextSession?.stopRequest()
    // No need to disconnect -- the connection will timeout if the microphone
    // is not used again within 30 seconds. This avoids the overhead of
    // connecting and disconnecting the session with every press of the
    // microphone button.
    //self.speechToTextSession?.disconnect()
    //self.speechToText?.stopRecognizeMicrophone()
  }

A função callback onResults() atualiza o widget de visualização de texto com base no melhor resultado de transcrição do serviço Speech to Text.

func onResults(results: SpeechRecognitionResults) {
    self.inputToolbar.contentView.textView.text = results.bestTranscript
    self.inputToolbar.toggleSendButtonEnabled()
  }

Text to Speech do Watson

A função ttsSynthesize() é usada para sintetizar uma fala baseada em uma frase, usando o serviço Text to Speech. Ela reproduz os dados de áudio sintetizados.

func ttsSynthesize(_ sentence: String) {
    // Synthesize the text
    let failure = { (error: Error) in print(error) }
    self.textToSpeech?.synthesize(sentence, voice: SynthesisVoice.gb_Kate.rawValue, failure: failure) { data in
      self.audioPlayer = try! AVAudioPlayer(data: data)
      self.audioPlayer.prepareToPlay()
      self.audioPlayer.play()
    }
  }

Conversação de Watson

A função conversationRequestResponse() lida com a interação com o serviço Conversation. Ela envia a requisição (em texto) e recebe uma resposta do serviço Conversation. Ela, então, chama a função didReceiveConversationResponse() para lidar com o texto da resposta. Finalmente, ela chama a função issueCommand().

func conversationRequestResponse(_ text: String) {
    let failure = { (error: Error) in print(error) }
    let request = MessageRequest(text: text, context: self.context)
    self.conversation?.message(withWorkspace: Credentials.ConversationWorkspaceID,
                               request: request,
                               failure: failure) {
    response in
      print(response.output.text)
      self.didReceiveConversationResponse(response.output.text)
      self.context = response.context
      // issue command based on intents and entities
      print("appl_action: \(response.context.json["appl_action"])")
      self.issueCommand(intents: response.intents, entities: response.entities)
    }
  }

A função issueCommand() decifra essa intenção do serviço Conversation e envia o comando para o Raspberry Pi através do serviço IoT Platform do Watson.

func issueCommand(intents: [Intent], entities: [Entity]) {
     
    for intent in intents {
      print("intent: \(intent.intent), confidence: \(intent.confidence) ")
    }
    for entity in entities {
      print("entity: \(entity.entity), value: \(entity.value)")
    }
     
    for intent in intents {
      if intent.confidence > 0.9 {
        switch intent.intent {
        case "OnLight":
          let command = Command(action: "On", object: "Light", intent: intent.intent)
          sendToDevice(command, subtopic: "light")
        case "OffLight":
          let command = Command(action: "Off", object: "Light", intent: intent.intent)
            sendToDevice(command, subtopic: "light")
        case "TakePicture":
          let command = Command(action: "Take", object: "Picture", intent: intent.intent)
          sendToDevice(command, subtopic: "camera")
        default:
          print("No such command")
          return
        }
      }
    }
  }

Interface com os serviços da IoT Plataform do Watson

Esta seção explica como você configura a interface com os serviços da IoT Platform do Watson.

Prepare o projeto Xcode

Adicione o seguinte código ao Podfile para instalar o CocoaMQTT, que usa um protocolo chamado MQTT e fornece um bom Swift SDK para se comunicar com os serviços da IoT Platform do Watson. Além disso, adicione SwiftyJSON para analisar e formatar mensagens JSON.

pod 'CocoaMQTT'
pod 'SwiftyJSON'

Execute o comando para instalar as dependências CocoaMQTT e SwiftyJSON.

pod install

Inicialização

Declare uma variável global no arquivo AppDelegate.swift porque a conexão MQTT deve ser tratada no nível do aplicativo e não no nível de exibição. O cliente MQTT é inicializado com o nome do host, a porta e o ID do cliente.

let mqttClient = CocoaMQTT(clientID: Credentials.WatsonIOTClientID, 
host: Credentials.WatsonIOTHost, port: UInt16(Credentials.WatsonIOTPort))
  • nome do host: [ORG_ID] .messaging.internetofthings.ibmcloud.com
  • porta: 1883
  • ID do cliente: a: [ORG_ID]: [API_KEY]

Crie um arquivo chamado MqttExt.swift para conter o código que interage com o serviço IoT Platform do Watson. Declare uma extensão para o ViewController e implemente o CocoaMQTTDelegate.

extension ViewController : CocoaMQTTDelegate {
}

Inicialize o nome do usuário com [API_KEY], a senha com [AUTH_TOKEN] e configure-se como o delegado/delegate.

mqttClient.username = Credentials.WatsonIOTUsername
mqttClient.password = Credentials.WatsonIOTPassword
mqttClient.keepAlive = 60
mqttClient.delegate = self

Conecte-se quando o aplicativo tornar-se ativo.

func applicationDidBecomeActive(_ application: UIApplication) {
    mqttClient.connect()
  }

Desconecte quando o aplicativo entrar no plano de fundo.

func applicationDidEnterBackground(_ application: UIApplication) {
    mqttClient.disconnect()
   }

Implemente callbacks de MQTT

Implemente a seguinte callback para receber um evento bem-sucedido de conexão. Quando conectado, ligue para se inscrever no tópico do evento, que está no formato 2/type/[DEV_TYPE]/id/[DEV_ID]/evt/+/fmt/json, onde + é um curinga.

func mqtt(_ mqtt: CocoaMQTT, didConnect host: String, port: Int) {
    mqttClient.subscribe(eventTopic)
}

Implemente a função de callback didReceiveMessage() para lidar com eventos do dispositivo. Se a função recebe um status da câmera, baixe a imagem do serviço de Armazenamento de Objetos/ Object Storage. Caso contrário, se a função recebe um status da luz, anexe uma mensagem à conversa.

func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) {
    var json : JSON = JSON.null
    if let message = message.string {
      json = JSON.init(parseJSON: message)
    } else {
      return  // do nothing
    }
    let cameraTopic = "iot-2/type/\(Credentials.DevType)/id/\(Credentials.DevId)/evt/camera/fmt/json"
    let lightTopic = "iot-2/type/\(Credentials.DevType)/id/\(Credentials.DevId)/evt/light/fmt/json"
    switch message.topic {
    case cameraTopic:
      //ObjectStorage
      if let objectname = json["d"]["objectname"].string {
        if let containername = json["d"]["containername"].string {
          self.downloadPictureFromObjectStorage(containername: containername, objectname: objectname)
        }
      }
    case lightTopic:
      if let status = json["d"]["status"].string {
        switch status {
        case "on":
          self.didReceiveConversationResponse(["Light is on"])
        case "off":
          self.didReceiveConversationResponse(["Light is off"])
        default:
          break
        }
      }
    default:
      break
    }
  }

Enviar um comando para o dispositivo

Implemente esta função para enviar um comando para o dispositivo. O formato do tópico é iot-2/type/[DEV_TYPE]/id/[DEV_ID]/cmd/[light|camera]/fmt/json.

func sendToDevice(_ command: Command, subtopic: String) {
    if let json = command.toJSON() {
      let topic = "iot-2/type/\(Credentials.DevType)/id/\(Credentials.DevId)/cmd/\(subtopic)/fmt/json"
      let message = CocoaMQTTMessage(topic: topic, string: json)
      print("publish message \(json)")
      mqttClient.publish(message)
    }
  }

Recupere imagem dos serviços de Armazenamento de Objetos/ Object Storage

Para recuperar a imagem do serviço de Armazenamento de Objetos, você deve preparar seu projeto Xcode e, então, inicializá-lo.

Prepare o projeto Xcode

Adicione o seguinte código ao Podfile para instalar o framework BluemixObjectStorage.

pod 'BluemixObjectStorage'

Execute o comando para instalar as dependências BluemixObjectStorage.

pod install

Inicialização

Crie um arquivo chamado ObjectStorageExt.swift para conter o código que interage com o serviço de Object Storage e declare uma extensão para ViewController.

extension ViewController {
}

Inicialize o serviço com [OS_PROJECTID], [OS_USERNAME] e [OS_PASSWORD], que você salvou anteriormente.

self.objectStorage = ObjectStorage(projectId: Credentials.ObjectStorageProjectId)
    objectStorage.connect(userId: Credentials.ObjectStorageUserId,
                       password: Credentials.ObjectStoragePassword,
                       region: ObjectStorage.Region.Dallas) {
                          error in
                          if let error = error {
                            print("objectstorage connect error :: \(error)")
                          } else {
                            print("objectstorage connect success")
                          }
    }

Implemente a função downloadPictureFromObjectStorage() para baixar a foto, que é tirada e carregada pelo aplicativo Node-RED.

func downloadPictureFromObjectStorage(containername: String, objectname: String) {
    self.objectStorage.retrieve(container: containername) {
      error, container in
      if let error = error {
        print("retrieve container error :: \(error)")
      } else if let container = container {
        container.retrieve(object: objectname) {
          error, object in
          if let error = error {
            print("retrieve object error :: \(error)")
          } else if let object = object {
            print("retrieve object success :: \(object.name)")
            guard let data = object.data else {
              return
            }
            if let image = UIImage(data: data) {
              self.addPicture(image)
              self.didReceiveConversationResponse(["Picture taken"])
            }
          } else {
            print("retrieve object exception")
          }
        }
      } else {
        print("retrieve container exception")
      }
    }
  }

Conclusão

O tutorial mostrou como um aplicativo móvel pode usar os serviços Conversation, Text to Speech e Speech to Text do Watson para entender os comandos do usuário. Esses comandos decifrados são, então, usados para controlar dispositivos através do serviço IoT Platform do Watson. O tutorial também explicou como integrar um Raspberry Pi como um gateway doméstico para receber comandos e enviar eventos do e para o aplicativo móvel. Finalmente, ele mostra como armazenar imagens usando o serviço Object Storage.

Agradeço a Chung Kit Chan por sua ajuda na revisão deste tutorial.

 

***

Khong Kok Sing faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela Redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://www.ibm.com/developerworks/mobile/library/cc-build-home-assistant-watson-iot-platform/