iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 17
1
Modern Web

Webduino 不只是物聯網,智慧插座也可以很 Modern~系列 第 17

實戰智慧插座 17 - 用網頁聊天室開關燈 ( 聊天室原理篇 )

接下來的兩篇文章,要來談談「網路聊天室」,說也奇怪,網路聊天室和智慧插座能有什麼交集呢?因為智慧插座既然可以透過 Webduino 變成一個網頁裏的變數,那麼我們理所當然就可以在網路聊天室和這個插座聊天,舉例來說,當我們輸入「開燈」訊息,燈泡就會打開,接著燈泡還會回傳「我打開囉」的訊息,或是你也可以輸入「開電扇」,電扇就會打開,如果再加個溫濕度感應器,也會透過聊天室回報溫度。

這篇將會介紹如何時做一個網路聊天室,下一篇再繼續介紹將聊天室與我們的智慧插座串接。

要實作一個聊天室,Google 的 Firebase 服務大概是現在市面上最簡便的做法了,先來看一下 Friebase 的故事,Firebase 原本是 2011 年的一家提供雲端服務公司,在 2012 年他們開發的即時雲端資料庫,提供 許多相對應的 API ,讓開發人員能儲存或同步不同平台之間的資料,而 Firebase 在 2014 年被 Google 收購,被收購之後,Firebase 更推出了多項新的功能。



註冊 Firebase

要使用 Firebase 前一定要有一個 Google 的帳號,用 Google 帳號登入就對了。( https://firebase.google.com/ )

Webduino聊天室和燈泡聊天

登入進去之後,如果你想瞭解更多的用法,可以點選上方的「Docs」,因為我要用 JavaScript 來操控,就往下拉看到「Installation & Setup in JavaScript」的部分,一些太細節的部分我在這邊就不多做描述,重點還是擺在如何打造一個網頁聊天室。
( 起手式:https://firebase.google.com/docs/database/web/start )
( 完整 API:https://firebase.google.com/docs/reference/js/index-all )

Webduino聊天室和燈泡聊天



開始使用

要使用 Firebase 首先你要先載入 Firebase JavaScript。

<script src="https://www.gstatic.com/firebasejs/3.6.2/firebase.js"></script>

先進入個人控制台,建立新專案,幫你的專案設定個好名字。

Webduino聊天室和燈泡聊天

左邊選單選擇 Database,就可以看到你專案專屬的資料庫 ( 雖然你可以自訂專案名稱,但實際上系統會在你自訂的名稱後方加上一串代碼避免和別人重複 )

Webduino聊天室和燈泡聊天

因為我現在要做的是一個公開的聊天室 ( 讓所有人都能聊 ),所以先把資料庫的安全性設定為公開 ( 當這麼做的時候會出現警告訊息 ),就可以開始使用這個資料庫了,當然如果你比較重視安全性,就可以加入 apiKey 作為一些保護。

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Webduino聊天室和燈泡聊天

資料庫建立之後,要在網頁端使用就是要先初始化 Firebase,在舊版的 Firebase 是用 new 一個 Firebase 物件 ( 現在還是可以用 ),新版則是要使用一個 config 設定,裡面就包含了像是 apiKey 之類的安全性設定。

舊版:

myFirebase = new Firebase("你的 Firebase 資料庫");

新版:

var config = {
  apiKey: "apiKey",
  authDomain: "projectId.firebaseapp.com",
  databaseURL: "你的 Firebase 資料庫",
  storageBucket: "bucket.appspot.com"
};
firebase.initializeApp(config);

這邊因為我是個公開的資料庫來讀寫,我的設定檔就只需要 databaseURL 即可。

var config = {
  databaseURL: "你的 Firebase 資料庫"
};
firebase.initializeApp(config);


更新、寫入資料

然後就可以來寫資料進資料庫了,如果是舊版的 Firebase,直接用 push 的方法就可以,而在新版的文件裡,反而沒有提到 push 的用法 ( 雖然還是可以用呦~ ),反而是先用 push 產生一組 key,接著透過 update 的方式寫入,如果沒有這組 key 或是 key 相同,update 基本上就是「複寫更新」的指令。

var firebase;
var config = {
  databaseURL: "你的資料庫名稱"
};
firebase.initializeApp(config);

//要寫入的資料物件
var postData = {
  id:'a',
  num:123
};
//每次寫入資料庫都產生一組 key
var newPostKey = firebase.database().ref().child('posts').push().key;
var updates = {};
updates[newPostKey] = postData;
//寫入資料
firebase.database().ref().update(updates);

如此一來當我們執行網頁的時候,就會順利寫入資料,當然如果覺得這樣比較麻煩,也可以直接使用 push 來實現。

var firebase;
var config = {
  databaseURL: "你的資料庫名稱"
};
firebase.initializeApp(config);

var postData = {
  id:'b',
  num:456
};

firebase.database().ref().push(postData);

再來看到 ref ,裡面是輸入資料庫的目錄結構,舉例來說,如果我寫成像下面這樣,則資料就會存到「路徑/test 」裡頭。

firebase.database().ref('/test/').push(postData);

能夠寫入資料之後,我們練習一下用兩個輸入框加上一個按鈕,在輸入文字後,按下按鈕,就把文字寫入資料庫,首先 HTML 放兩個輸入框,第一個是姓名,第二個是聊天內容,然後放一個送出的按鈕。

姓名:<input id="name"><br/>
聊天內容:<input id="content"><br/>
<button id="btn">送出資料</button>

再來就是 JavaScript 內容,先宣告網頁上輸入欄位與按鈕,在按下按鈕的當下,把欄位的值寫入資料庫,如果你把資料庫在旁邊開一個視窗,就會看到資料即時寫入。 ( 注意,如果像我一樣使用 jQuery,firebase 變數需為全域變數 )

var firebase;
$(function(){
  var $name = $('#name'),
      $content = $('#content'),
      $btn = $('#btn');
  var config = {
    databaseURL: "你的資料庫名稱"
  };
  firebase.initializeApp(config);
  var database = firebase.database().ref();

  $btn.on('click',function(){
    var postData = {
      name:$('#name').val(),
      content:$('#content').val()
    };
    database.push(postData);
  });

});

Webduino聊天室和燈泡聊天
( 程式:http://bin.webduino.io/qajen/1/edit?html,js,output )

這裡我們其實還可以y在送出的當下,加入下面這行,讓我們按下送出之後,就把內容欄位清空

$('#content').val('');


讀取資料

既然可以寫入資料,就一定要可以讀取資料,而「即時」讀取資料正是 Firebase 的強項,這也是為什麼現在許多聊天室都會選用 Firebase 做為資料庫的主因。

我這邊使用 .on 來讀取資料,這個方法可以在每次資料庫有變動的時候,就把資料回傳讓我們知道,已下面這段程式碼,當我們資料庫變動時,就在 console 裡頭印出來。

database.on('value', function(snapshot) {
 console.log(snapshot.val());
});

不過由於讀取到的資料是「物件」,所以這裡我透過 for 迴圈轉換成陣列,並將陣列的內容依序顯示在網頁上,就可以看到一個聊天室雛形誕生了,不過要注意的是一開始我先把畫面清空,避免產生的內容重複發生

database.on('value', function(snapshot) {
  $show.html('');
  for(var i in snapshot.val()){
     $show.append('<span>'+snapshot.val()[i].name+' 說:'+snapshot.val()[i].content+'</span><br/>');
  }
});

Webduino聊天室和燈泡聊天
( 程式:http://bin.webduino.io/fawe/3/edit?html,js,output )



實作聊天室

看完寫入和讀取之後,就真的要來做一個聊天室了,首先來看一下一個基礎的聊天室,需要有哪些功能:

  • 姓名
  • 發送時間
  • 對話內容
  • 可以區隔是自己發的內容還是別人發的內容
  • 打完字按下 enter 就送出 ( 不然每次還要用滑鼠點也是頗累人 )

由這五個功能可以發現我們的資料庫需要有四個欄位:姓名、時間、內容、id,其他的基本上都可以透過網頁前端來解決,我們先把寫入資料庫的事情搞定,因為需要「時間」,所以在每次按下按鈕時都會觸發 new Date(),為了避免個位數與十位數在排版上的差異,所以一律補零,最後就是在寫入的物件新增一個 time 的屬性記錄當下時間。

id 則是在一開始的時候先設定一個變數 ms = new Date().getTime(),目的是讓打開網頁的使用者知道自己是誰 ( getTime()的目的在獲取從 1970/1/1 到現在的毫秒數,很難重複 ),這樣子我們讀取資料的時候,才可以知道哪個帳號是自己說的話,就可以透過樣式標記出來。( 當然如果是使用帳號登入之類的方式就更簡單判斷 )

$btn.on('click',write);
//設定在對話框按下 enter 的事件 ( enter 預設 keyCode 為 13 )
$content.on('keydown', function(e){
  if(e.keyCode == 13){
    write();
  }
});

function write(){
  var date = new Date();
  var h = date.getHours();
  var m = date.getMinutes();
  var s = date.getSeconds();
  if(h<10){
    h = '0'+h;
  }
  if(m<10){
    m = '0' + m;
  }
  if(s<10){
    s = '0' + s;
  }
  var now = h+':'+m+':'+s; //獲取按下按鈕或 enter 的當下時間
  //記得一開始要先宣告 ms = new Date().getTime()
  var postData = {
    name:$('#name').val(),
    content:$('#content').val(),
    time:now,
    id:'id'+ms
  };
  database.push(postData);
  $content.val('');
}

完成後再來看看讀取的程式,這邊我將會用到 .once 以及 .limitToLast(1).on 這兩種方法,第一個 .once 的目的在於資料庫的「完整資料」在「第一次」全部載入,因為沒有必要在每次送出訊息的時候都重新載入一次 ( 如果純粹用 .on 就會全部重新載入 ),而 .once 只會載入一次,而 .limitToLast(1).on 則是在資料庫有變動的時候,載入最後一筆訊息,同樣的,這個步驟也是避免每次發送訊息都載入完整資料庫。

將資料顯示在畫面上則是用了 jQuery 的 prepend,這樣才會顯示在開頭,然後用一點點 CSS 做顏色的區隔。

//第一次載入資料庫時顯示所有內容
database.once('value', function(snapshot) {
  $show.html('');
  for(var i in snapshot.val()){
     $show.prepend('<div><div>'+snapshot.val()[i].time+'</div>'+snapshot.val()[i].name+' 說:'+snapshot.val()[i].content+'</div>');
  }
});

//每一次資料庫有變動時,獲取最新一筆內容呈現
database.limitToLast(1).on('value', function(snapshot) {
  for(var i in snapshot.val()){
     $show.prepend('<div class="'+snapshot.val()[i].id+'"><div>'+snapshot.val()[i].time+'</div>'+snapshot.val()[i].name+' 說:'+snapshot.val()[i].content+'</div>');
  }
  //如果是自己發出去的文字,就改變顏色
  $show.find('.id'+ms).css({
    color:'#f00'
  });
  $show.find('.id'+ms+' div').css({
    color:'#f00'
  });
});

最後結果:
Webduino聊天室和燈泡聊天
( 完整程式碼:http://bin.webduino.io/xato/1/edit?html,js,output )

在不同視窗下看到的樣子:
Webduino聊天室和燈泡聊天



小結

如果你覺得程式太多太複雜,其實 Webduino Blockly 線上編輯工具 ( https://blockly.webduino.io ) 也有提供 Firebase 的功能喔!透過積木的組合,我們依然可以寫入資料以及存取資料。

Webduino聊天室和燈泡聊天
( 解答:https://blockly.webduino.io/#-K_HWwj-kZTM6wS1iPvm )

打造簡易聊天室的原理就是這樣,只要熟練 Firebase 的 API 就可以輕鬆上手,明天會再繼續介紹如何跟智慧插座串接,真正來和燈泡聊聊天!


上一篇
實戰智慧插座 16 - 搖搖手機就開燈 ( 行動裝置陀螺儀、加速度計 )
下一篇
實戰智慧插座 18 - 用網頁聊天室開關燈 ( 前、後端實作篇 )
系列文
Webduino 不只是物聯網,智慧插座也可以很 Modern~30

尚未有邦友留言

立即登入留言