カイワレスタイル

ゲーム、アニメ、プログラム、興味のあることをツラツラと。

Raspberry Pi + hubot + slackでPhilips Hueを制御する

自宅の室内灯は色が自由に変えられるLED電球、Philips Hueを使っている。
Philips HueはAPIが公開されており、同一ネットワーク内ならAPIを叩くことで制御することが出来る。
使い方は公式のGetting startedを参照。
かいつまんで説明すると、

  1. www.meethue.com/api/nupnp にアクセスしてHueのブリッジのIPアドレスを取得する
  2. http://<ブリッジのIPアドレス>/debug/clip.html にアクセスする
  3. ブリッジからユーザーIDを発行してもらう。ブリッジのボタンを押してからAPIを叩かないとエラーになる。
  4. http://<ブリッジのIPアドレス>/api/<ユーザーID>/lights/<ライトの番号>/state にJSON形式で状態を設定して PUT して制御する

これを、slack経由でhubotにHueを簡単に制御できるようにしてみた。

hubotからHueを制御する

Getting startedの通りにユーザーIDを発行したら、次はhubotから制御するようにスクリプトを書く。
下記のスクリプトを作成した。
hubotのCoffeeScriptから呼び出せるようにライブラリ化している。
CoffeeScript同士でexportする方法がよくわからなかったのでjavascriptにしているが正しいかどうかはわからない。

var request = require('request')
var async = require('async')

var developer = 'ユーザーID';

// ブリッジのIPアドレスを取得する(たまに変わることがあるため)
function search_hue_bridge(callback) {
    var url = 'https://www.meethue.com/api/nupnp'
    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var dict = JSON.parse(response.body);
            console.dir(dict);
            console.log('bridge IP: ' + dict[0]["internalipaddress"]);
            if(callback !== null) {
                callback(null, dict[0]["internalipaddress"]);
            }
        } else {
            console.log('error: ' + error);
            if(callback !== null) {
                callback(error, "");
            }
        }
    });
}

// Hueの状態を変える
function set_state(lampno, json, bridge, callback) {
    var url = "http://" + bridge + "/api/" + developer + "/lights/" + lampno + "/state";
    console.log('URL: ' + url);

    var options = {
        url: url,
        form: json,
        json: true,
    };

    request.put(options, function(error, response, body){
        if (!error && response.statusCode == 200) {
            console.log(body.name);
            if(callback !== null ) {
                callback(null);
            }
        } else {
            console.log('error: ' + error);
            if(callback !== null) {
                callback(error);
            }
        }
    });
}

// 全てのHueの状態を変える(我が家では4つ使っているので4回ループさせる)
function set_all_state(json) {
    search_hue_bridge( function(error, bridge) {
        if (!error && bridge !== "") {
            var i = 1;
            async.whilst(function test() {
                return i < 5;
            }, function func(done) {
                set_state(i, json, bridge, null);
                i++;
                done();
            }, function result(err) {
                if(err)
                    console.error(err);
                else
                    console.log('finished');
            });
        } else {
            console.error("hue bridge not found");
        }
    });
}

// Hueの色を変える(彩度、明度、色相)
function set_hue_color(sat, bri, hue) {
    var json = JSON.stringify({on: true, sat: sat, bri: bri, hue: hue});
    set_all_state(json);
}

// Hueを点ける
function set_hue_on() {
    var json = JSON.stringify({on: true});
    set_all_state(json);
}

// Hueを消す
function set_hue_off() {
    var json = JSON.stringify({on: false});
    set_all_state(json);
}

module.exports = {
    set_hue_color : set_hue_color,
    set_hue_on : set_hue_on,
    set_hue_off : set_hue_off
};

slack経由でHueを制御するCoffeeScript

slack経由でHueを制御するCoffeeScriptは下記の通り

# Description:
#   自宅の室内灯を操作する
#
# Commands:
# hubot lampon でんきつける
# hubot lampoff でんきけす
# hubot lampwhite でんき白くする
# hubot lampred でんき赤くする
# hubot lampblue でんき青くする
# hubot lampgreen でんき緑にする
# hubot lampyellow でんき黄色くする
# hubot lamppink でんき桃色にする

uc = require('../libs/usercheck') #特定のslackのユーザーIDかどうかチェックする
tmp = require('../libs/temperature')
hue = require('../libs/hue')

request = require('request')
exec = require('child_process').exec
envelope = room: "#general"

module.exports = (robot) ->
    
    # Hueを点ける
    robot.respond /lampon/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_on()
            msg.reply '点灯したと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # Hueを消す
    robot.respond /lampoff/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_off()
            msg.reply '消灯したと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # Hueの色を白くする
    robot.respond /lampwhite/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_color(0, 255, 46920)
            msg.reply '電灯を白色モードに切り替えたと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # Hueの色を赤くする
    robot.respond /lampred/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_color(255, 255, 0)
            msg.reply '電灯を赤色モードに切り替えたと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # Hueの色を青くする
    robot.respond /lampblue/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_color(255, 255, 46920)
            msg.reply '電灯を青色モードに切り替えたと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # Hueの色を緑にする
    robot.respond /lampgreen/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_color(255, 255, 25500)
            msg.reply '電灯を緑色モードに切り替えたと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # Hueの色を黄色くする
    robot.respond /lampyellow/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_color(255, 255, 12750)
            msg.reply '電灯を黄色モードに切り替えたと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # Hueの色をピンクにする
    robot.respond /lamppink/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            hue.set_hue_color(255, 255, 56100)
            msg.reply '電灯を桃色モードに切り替えたと思います'
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

    # 温湿度をチェックする関数
    tempCheck = () =>
        tmp.get_temperature((err, temp, hudi) ->
            if !err
                robot.send envelope, "現在の室温は#{temp}℃、湿度は#{hudi}%です"
                if hudi < 45
                    hue.set_hue_color(128, 255, 6375)   # Hueを赤くする
                    robot.send envelope, "室内が乾燥し過ぎのようです。加湿したほうがよろしいかと思います"
                else if hudi > 65
                    hue.set_hue_color(128, 255, 47000)  # Hueを青くする
                    robot.send envelope, "室内の湿度が高すぎるようです。除湿したほうがよろしいかと思います"
            else
                robot.send envelope, "#{err}"
        )

    # 湿度をチェックする命令
    robot.respond /湿度チェック/i, (msg) ->
        if uc.user_check(msg.message.user.id)
            tempCheck()
        else
            msg.reply 'ご主人様以外のお方からのご命令はお受けできません'

これでslackからHueの色を変えることが出来るようになった。

slackから家のhueの色変えるのできた

使ってみて

slackでコマンドを打つことで点けたり消したり色を変えたりできるようになったが、ぶっちゃけ、slackでコマンド打つより壁のスイッチを押したほうが早いのでほとんど使わない事がわかった…。
実用性を考えるならslack経由ではなく、Amazon Dash Buttonみたいなネットワーク接続型の物理ボタンのほうが使いやすそうだ。
その場合、Raspberry Pi を経由しなくてもESP8266で直接制御できるかもしれない。
いずれ作ってみたい。