Raspberry pi

8편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (서버편)

[혜안] 2017. 5. 3. 16:04
728x90

[Raspberry pi] - 1편 스마트 콘센트? Smart Plug!

[Raspberry pi] - 2편 스마트 콘센트? Smart Plug!

[Raspberry pi] - 3편 스마트 콘센트? Smart Plug!

[Raspberry pi] - 4편 스마트 콘센트? Smart Plug! + 앱위젯

[Raspberry pi] - 5편 스마트 콘센트? Smart Plug! + 터치스위치

[Raspberry pi] - 6편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (개요편)

[Raspberry pi] - 7편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (Firebase 등록)

[Raspberry pi] - 8편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (서버편)

[Raspberry pi] - 9편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (App편)



짧은 휴가를 다녀오는 동안 iptime NAS2를 종료시켜놓았는데, 다녀와서 전원을 켜니 부팅이 안되고 있다.

Push 서버의 DB로 MySQL을 사용하고 있고, NAS2에 저장하고 있었으므로 이놈을 살리지 않으면 Push도 안되는 셈.

전원조차 안들어오는게 하드웨어 문제인 듯 하여 연휴 끝나고 A/S를 보내기로 하고, DB는 라즈베리파이(Raspberry Pi)에 자체구축하기로 했다. (SD카드 용량도 늘었겠다.)


1. MySQL 서버 설치

패키지 설치

$ sudo apt-get install mysql-server


Database 생성

접속

$ mysql -u root -p


생성할 테이블은 Push 대상 스마트폰을 식별할 수 있는 user id와 키값인 reg id 만 저장하면 된다.

mysql> CREATE DATABASE MYHOME;

mysql> USE MYHOME

mysql> CREATE TABLE PUSH_INFO ( 

        -> USER_ID VARCHAR(255) NOT NULL, 

        -> REG_ID VARCHAR(255) NOT NULL, 

        -> PRIMARY KEY (USER_ID) 

        -> );

mysql> CREATE USER '********'@'localhost' IDENTIFIED BY '********';

mysql> GRANT ALL PRIVILEGES ON MYHOME.* TO 'viewise'@'localhost';



2. Node.js Push 서버 구축

GCM을 구현하려면 App Server, GCM Server, App 의 세가지 구성이 있어야 하고, 지금 App Server를 Node.js를 이용하여 구축하는 단계이다.


App Server가 GCM Server에 Push 요청을 할 때에는 Registration ID라는 인자를 넣어 요청해야 하며, GCM Server는 이 Registration ID를 보고 어느 클라이언트에 Push를 보낼지 식별하게 된다.

물론 Registration ID는 App이 GCM Server에 요청하여 받아온 값이며, 이 값을 App Server에 저장하는 절차가 필요하다.


따라서 App Server에 크게 두가지 기능을 추가해야 한다.

1. App 으로부터 Registration ID 를 받아 MySQL에 저장하는 기능

2. MySQL에 저장된 Registration ID 를 읽어서 GCM에 Push요청을 하는 기능


먼저, 필요한 node.js 패키지부터 설치하자.

fcm-node 라는 패키지가 있지만, node-gcm이 사용하기 편했다. 클라이언트도 gcm으로 만들었고...

$ npm install mysql

$ npm install node-gcm


기존 서버 코드에 mysql 클라이언트와 node-gcm을 추가한다.

var express = require('express'),
        http = require('http'),
app = express(),
server = http.createServer(app),
bodyParser = require('body-parser'),
lirc_node = require('lirc_node'),
GPIO = require('onoff').Gpio,
plug = new GPIO(2, 'high'),
touch = new GPIO(26, 'in', 'rising'),
gcm = require('node-gcm'),
winston = require('winston'),
mysql = require('mysql');


firebase에서 발급한 서버키를 저장한다.

var serverKey = 'AAAAusk2Pec:APA91bFlmFJj_CkY8MRhmd2hHPVW.....';
var sender = new gcm.Sender(serverKey);

※ 웹 API 키가 아니다. 아래 화면을 참고하자.



1. App 으로부터 Registration ID 를 받아 MySQL에 저장하는 기능

var pool = mysql.createPool({
host:'localhost',
port:3306,
user:'********',
password:'********',
database:'MYHOME'});

app.get('/regclient', function(req, res) {
regid = req.query.regid;
l("Recv regid : " + regid);

pool.getConnection(function(err,connection) {
var data = [req.query.user, req.query.regid];
var query = connection.query('REPLACE INTO MYHOME.PUSH_INFO VALUES (?,?)'
, data
, function(err, result) {
if(err) {
e(err);
connection.release();
return;
}
l("Successfully inserted");
connection.release();
});
});
responsePlugStat(res); });

mysql과의 connection을 관리해 줄 pool을 정의하고,

/regclient 경로로 userid와 regid가 들어오면 DB에 저장하는 function이다.

쿼리문에 insert 대신 replace를 사용한 이유는, 이미 등록된 동일한 클라이언트(user id)에서 호출이 들어오면 insert 가 아닌 update를 해야 하기 때문이다.


2. MySQL에 저장된 Registration ID 를 읽어서 GCM에 Push요청을 하는 기능

function sendPush() {
pool.getConnection(function(err,connection) {
var onoff = 'on';
if(plug.readSync() == 1)
onoff = 'off';
var message = new gcm.Message({
collapse_key:'plug state',
data:{state:onoff}
});
var query = connection.query('SELECT * FROM MYHOME.PUSH_INFO'
, function(err, rows) {
if(err) {
e(err);
connection.release();
return;
}
var registrationIds = [];
for(var i=0; i<rows.length; i++) {
registrationIds.push(rows[i].REG_ID);
}
sender.send(message, registrationIds, 3, function(err, result) {
l(result);
});
connection.release();
});
}); }

현재의 plug 상태를 읽어서, GCM message를 생성한다.

그리고, DB 에 저장된 클라이언트 들의 Registration ID를 읽어서 registratonIds 배열에 저장한다.

Sender 객체의 send 함수는 한번만 호출하면, 이 registratonIds 배열을 읽어서 모든 대상에게 push를 발송한다.

첫번째 인자는 보낼 메시지,

두번째 인자는 받을 대상,

세번째 인자는 재시도 횟수,

그리고 마지막 인자에서는 에러 발생 시 에러코드와 발송결과를 리턴한다.

발송결과(result)에는 push를 발송한 모든 대상에 대한 결과가 들어 있다.

Push 발송 실패 시 result 로그를 통해 오류를 짐작할 수 있다.

2017-05-03 23:16:05 - info:  multicast_id=4725486217357751000, success=0, failure=2, canonical_ids=0, results=[error=InvalidRegistration, error=InvalidRegistration]


728x90