non vorrei lavorare

昔はおもにプログラミングやガジェット系、今は?

WindowsだけでAmazon DashボタンのMACアドレスを取得できるjsを書いた

こんばんは。先日、保育園から習い事の教室に向かう際、以前、習い事の教室のトイレが故障しており、難儀したことがあり、かなり強く長男に、保育園で事前にトイレに行っておけと言ったのをすっかり忘れてましたが、長男はちゃんとそれを実践しており、感心した@kjunichiです。

背景

LinuxMacだと、npmモジュールのnode-dash-buttonに付属のfindbuttonで Amazon DashボタンのMACアドレスが簡単に取得できる。

しかし、Windowsだと、このモジュールがWindowsに対応していないようで、 ビルドエラーとなり使えません。

代わりに当ブログでも何度か紹介しているwin-node-dash-button が使えるのですが、MACアドレスの検出のプログラムは付属していません。

MacLinuxが使えない環境だとこのMACアドレスを取得するところでつまずいて残念なことになる。

http://tanbonomannaka.hatenablog.com/entry/2016/12/18/204217tanbonomannaka.hatenablog.com

なんて記事をたまたま見つけたから、ちょっと記事にしてみた。

つくった

パケットキャプチャー関連の知識がなかったので、勉強になった。 Cだとこの手のプログラムは面倒な印象を持っているが、Node.jsだと思った通り、わかりやすかった。 結局、win-node-dash-buttonのコードを少し手を入れるだけでよかった。

"use strict";

const Cap = require('cap').Cap,
    decoders = require('cap').decoders,
    _ = require("lodash"),

    Server =  function() {
        const self = this,
            savedMacAddr={},
            lastUpdate ="",
            cap = new Cap(),
            buffer = new Buffer(65536);

        this.packetReceived = (nbytes, trunc) => {
            const ret = decoders.Ethernet(buffer);
            if (ret.info.type === 2054) { // arp
                if(! savedMacAddr[ret.info.srcmac]) {
                    console.log(ret.info.srcmac);
                    savedMacAddr[ret.info.srcmac] = true;
                }
            }
        };

        this.start = (ip) => {
            const device = Cap.findDevice(ip),
                linkType = cap.open(device, "", 10 * 1024 * 1024, buffer);

            try {
                cap.setMinBytes(0);
            } 
            catch (e) {
                console.log(e);
            }

            cap.on("packet", self.packetReceived);

            process.on("SIGINT", self.stop);
        };

        this.stop = () => {
            console.log("Shutting down");
            cap.removeListener("packet", self.packetReceived);
            cap.close();
        };
    };

const srv = new Server();
srv.start('192.168.0.2'); // 192.168.0.2は動かすホストのIPアドレス

CapというnpmモジュールがUnix系だとlibpcapを使い、Windowsだと別途インストールしたwinpcap同梱しており、利用することで マルチプラットフォームでパケットキャプチャーを実現している。

自分のPCのIPアドレスもわからない場合

以下のような感じで、コンソールにIPアドレスを出力できたが、どんな環境でも動かくは不明。 osというNode.js標準モジュールで実装することで、環境依存を抑えたつもり。

const os = require('os');
nifs = os.networkInterfaces();
for(let nif in nifs) {
    if(nif.startsWith("Virtual")|| nif.startsWith("Loopback")) {
        continue;
    }
    //console.log(nif);
    for(let ad in nifs[nif]) {
        if(nifs[nif][ad].family == 'IPv4') {
            //console.log(nifs[nif][ad]);
            console.log(nifs[nif][ad].address);
        }        
    }
}

まとめ

  • Windows環境だとnode-dash-buttonではなく、win-node-dash-buttonを使う。
  • MACアドレスの取得は、win-node-dash-button付属のコードをちょっと変更するか、この記事の検出用のコードを利用する。

関連記事

7年前の記事

Node.jsとAmazon Dashボタンで鬼が来たボタンを作った

おはようございます。去年のこの時期から次男の便秘で小児科通ったりしてましたが、最近では定期的にうんちが出るようになりました。@kjunichiです。

背景

昨年末、自作PCを一新をきっかけにMacよりWindowsに触れる機会が多くなり、sayコマンドのWindows版が ないか調べていたら、Windowsでもいくつかの方法で似たようなものは作れることが分かりました。

Edge.jsでNode.jsからC#のコードを呼び出せることを知っていたので、これとWindows向けに使える win-node-dash-buttonを使ってやってみました。

今回はIOTで子育てを解決してみます(嘘)!

子供が夜なかなか寝ないを解決したい!

子供がよる遊んでなかなか寝ないので、ボタン一つで解決を目指します。

つくるもの

Dashボタンを押すと、「おにがきたぞー」とCortanaと音字声でパソコンに脅かしてもらいます。

Edge.jsからSystem.Speech.dllを参照させる

今回のポイントはここだけです。32ビットの話が多く64ビットだと、\Windows配下で検索して 見つかったdllがいくつか見つかり、この中から当たりを見つけて、.jsと同一のディレクトリに 置きます。

ほんとは、このEdge.jsのdll参照時のパスの設定方法を調べたかったのですが。。。

また、UWPでもやってみましたが、参照設定が必要なのか、使用したい名前空間のクラスが見つかりませんでした。

成果物

const Server = require('win-node-dash-button').Server
const DashButton = require('win-node-dash-button').DashButton
const edge = require('edge');

const hostIp = '192.168.0.123';
const dashButtonMAC = "88:71:e5:xx:xx:xx";

const say = edge.func(`
    #r "System.Speech.dll"
    using System.Speech.Synthesis;
    async (input) => {
         SpeechSynthesizer sz = new SpeechSynthesizer();
         sz.Volume = 100;
         sz.Rate = -1;
         sz.Speak(input.ToString());
         return "";
    }
`);

const svr = new Server(),
    tide   = new DashButton(dashButtonMAC,  () => {
      say('おにがきたぞー、はやくねろ!', (error, result)=> {
        if (error) throw error;
        console.log(result);
    });
     console.log("Tide pressed"); 
    });
 
svr.register(tide)
.start(hostIp);

まとめ

  • IOTを駆使することで、子供たちをボタン一つで寝かしつけに成功した。(嘘です)
  • Edge.jsすごいとは思っていたものの、適用箇所が思いつかなかったが、この記事で一例ができた。

参考資料

関連記事

12年前の記事

mrubyでWebカメラを黒い画面にそのまま表示できるようにした

おはようございます。切り替えの遅かった長男が最近ではすっかり、お気に入りのYoutubeを見ていても、お風呂の号令でさっと、お風呂に来るようになりました。しかし、次男の悪さが今度は目立ってしまってます。まぁ、長男もそうだったから、まぁ、いいかぁ。っとこれだから次男は。。。@kjunichiです。

背景

長編まとめ・Sixel Graphics復活への動き(1) - Togetter

libsixelの使い方

// gcc `pkg-config libsixel --cflags --libs` main.c
// ./a.out > test.sixel

#include <stdio.h>
#include <stdlib.h>
#include <sixel.h>

        static int
sixel_write(char *data, int size, void *priv)
{
        return fwrite(data, 1, size, (FILE *)priv);
}

        static SIXELSTATUS
output_sixel(unsigned char *pixbuf, int width, int height,
                int ncolors, int pixelformat)
{
        sixel_output_t *context;
        sixel_dither_t *dither;
        SIXELSTATUS status;

        context = sixel_output_create(sixel_write, stdout);
        dither = sixel_dither_create(ncolors);
        status = sixel_dither_initialize(dither, pixbuf,
                        width, height,
                        pixelformat,
                        SIXEL_LARGE_AUTO,
                        SIXEL_REP_AUTO,
                        SIXEL_QUALITY_AUTO);

        if (SIXEL_FAILED(status))
                return status;

        status = sixel_encode(pixbuf, width, height,
                        pixelformat, dither, context);
        if (SIXEL_FAILED(status))
                return status;

        sixel_output_unref(context);
        sixel_dither_unref(dither);

        return status;
}

int main(int argc,char **argv) {
        SIXELSTATUS status;
        int WIDTH = 128;
        int HEIGHT =  128;

        unsigned char *pixbuf;
        pixbuf = malloc(sizeof(char)*WIDTH*HEIGHT*3);

        for(int h=0; h < HEIGHT; h++) {
        for(int w=0; w<WIDTH;w++) {
                int index = 3*(h*WIDTH+w);
                int v = (w*h)%255;
                pixbuf[index] = 0;
                pixbuf[index+1]=v;
                pixbuf[index+2]=0;
        }
        }
        status =        output_sixel(pixbuf, WIDTH,HEIGHT,256,SIXEL_PIXELFORMAT_RGB888);
        if (SIXEL_FAILED(status)) {
                fprintf(stderr, "%s\n%s\n",
                                sixel_helper_format_error(status),
                                sixel_helper_get_additional_message());
                return status;
        }
        printf("test\n");
}

画像サイズと、RGBの順にunsigned charが並んだ配列を渡せば、良きに計らってくれることが分かった。

mruby-webcamの対応

JPEG出力一択だったところをWebcam#setFmtメソッドを新設して、 "jpg","ppm"等のファイル拡張子による指定を可能として、 libsixelで処理し易い、ppm形式での出力を実装した。

実装と言っても、

imencode(".jpg", mat, buff, param);

と固定だった拡張子の指定を以下の様に可変にしただけ。

imencode(type.c_str(), mat, buff, param); 

mruby-webcamでカメラの映像をppm形式で出力する

cam = Webcam.new
cam.setFmt "ppm"
cam.capture {|img|
  # img : PPMのP6形式で渡される。
  puts img.length
}
cam.start

mruby-sixelの作成

mrbgemの概要

libsixelにRubyのGemを参考に

encoder = SixelEncoder.new
encoder.encode_from_ppm ppm_image_buf

動かす

必要な物

OSX,Linuxではmltermを用意すれば、試せる。WindowsでもRLoginから試せる。

参考資料

yskwkzhr.blogspot.com

  • libsixel本体

github.com

スペースキーを押すと、端末に画像が表示される。

cam = Webcam.new
encoder = SixelEncoder.new

cam.setFmt("ppm")
cam.capture {|img|
  puts "\ec"
   encoder.encode_from_ppm img
 }
cam.start

f:id:kjw_junichi:20170225080802p:plain

成果物

github.com

github.com

macOSでやるには

まとめ

前々から、sixel表示を手元の環境で試したと思いながら数年経ってしまったが、 2017年にようやく実現でき、mruby-sixelなるmrbgemも作成することが出来た。

関連記事

mrubyつながり

黒い画面つながり

Webカメラつながり

9年前の記事

6年前の記事

3年前の記事