雨降り監視サーバーの制御用アプリ(android)

我が家の「アメだす(雨降り警報機)」のPCソフトの「監視」機能のON/OFF制御やPCのリブート、電源OFFをスマホから制御できるようにしました。
作ったアプリはその名も「Amefuri001」。

ただし、ローカルネットワーク内でのみ有効です。
外部からはVPN接続で使用します。

雨降り監視サーバーの制御用アプリ(android)

概要


■アプリが表示されると、UDPブロードキャスト(VPN時はDevice Scan)で制御対象のデバイスに「点呼」をとります。
■デバイスは応答として、自身のIPアドレスとホスト名を返します。
■アプリは、これを受けて、画面に CheckBox を生成し表示します。(上記画像)

■制御の対象とするデバイスをChaecBoxで選択し、
■制御したい動作に対応するButtonをタッチします。

以下は、ezのデバイスを選択し、「Ame Kansi GO」(雨降り監視開始)Buttonをタッチしたところ。
雨降り監視サーバーの制御用アプリ(android)

■選択されたデバイスにUDPで「司令」を送信します。
■デバイスは「司令」の動作を行い、結果をUDPで返します。



他にデバイスのスリープ、再起動、電源OFFの司令があります。

PCを立ち上げたまま離席した時に出先で「スリープ」させ「パリ協定」に協力します。


プログラム概要


■Class
ソケット用の「ListenerThread」と「SendThread」のClassを作成。
ListenerThreadをrunさせて於いて、SendThreadでコマンドを発行する為のもの。

■動作(デバイスに「点呼」)

(1)ListenerThread

UIスレッド(MainActivity)で「デバイスに「点呼」」用のHandlerを生成し、
そのハンドルをListenerThreadの生成時に引き渡す。

ListenerThreadは複数のデバイスから応答を待ち、応答UDPを受信したら、
受信した内容を、引き渡されたハンドルにsendMessageで送る。

UIスレッドで定義したHandlerのhandlMessageがこれを受けて、
そのデバイス用のCheckBoxを生成し、
画面に追加表示する。


上記の「ListenerThread」は終了指示があるまで、複数の端末からの応答を待ち続ける。

(2)SendThread

上記のListenerThreadを生成し、start()させた後、
SendThreadを生成しstart()させる。

SendThreadはDatagramSocketで「お~ぃ」とブロードキャストで呼びかけて、終了する。


■動作(デバイス側)

ブロードキャストで「お~ぃ」を受けたデバイは、
自身のIPアドレスとホスト名を応答UDPで返信する。


■動作(操作者)

画面に表示された、デバイス対応のチェックボックで制御したいデバイスを選択し、
動作指示のボタンを選んでタッチする。


■動作(デバイスに「司令」)

「点呼」用のListenerThreadを終了させる。

UIスレッド(MainActivity)で「デバイスに「司令」」用のHandlerを生成し、
そのハンドルをListenerThreadの生成時に引き渡す。

ListenerThreadは複数のデバイスから応答を待ち、応答UDPを受信したら、
受信した内容を、引き渡されたハンドルにsendMessageで送る。

UIスレッドで定義したHandlerのhandlMessageがこれを受けて、
そのデバイスから送られてきた「動作結果」をTextViewに表示する。


妥協点


UDPのリモート、ローカル共に固定のポート番号にしています。
当初はローカルポートは固定ではなくシステムにお任せして、
同一アプリまたは端末で複数の通信が同時に可能なようにしたかったのですが、
受信、送信各1スレッドで対応するいい方法が見つからなかった。
仕方なく、固定ポートにしました。

ソース


■MainActivity
package jp.no4393.amefuri001;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ListenerThread listenerThread;
    private LinearLayout deviceListLayout;
    private ArrayList<CheckBox> deviceCBList;
    private MainActivity ma;
    private TextView ReplyText;
    private SendThread sendThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listenerThread = null;
        deviceListLayout = (LinearLayout)findViewById(R.id.deviceListLayout);
        ReplyText = (TextView)findViewById(R.id.ReplyText);
    }

    public void yoohooButtonOnClick(View view){
        DeviceListInit();
    }

    public void commandOnClick(View view){
        String command;
        switch (view.getId()) {
            case R.id.ameKansiGOButton:
                command = "amekansigo";
                break;
            case R.id.ameKansiStopButton:
                command = "amekansistop";
                break;
            case R.id.sleepButton:
                command = "pcsuspend";
                break;
            case R.id.rebootButton:
                command = "pcreboot";
                break;
            case R.id.powerOffButton:
                command = "pcpoweroff";
                break;
            default:
                return;
        }

        CommandSend(command);
    }

    @Override
    protected void onStart(){
        super.onStart();
        DeviceListInit();
    }

    @Override
    protected void onDestroy() {
        if (listenerThread != null) {
            listenerThread.endrun();
        }
        super.onDestroy();
    }

    public Handler getHandlerSetDeviceList(){
        return new Handler(Looper.myLooper()){
            public void handleMessage(Message msg){
                CheckBox deviceCheckBox = new CheckBox(ma);
                deviceCheckBox.setText((String)msg.obj);
                deviceListLayout.addView(deviceCheckBox);
                deviceCBList.add(deviceCheckBox);
            }
        };
    }

    public Handler getHandlerReplyText(){
        return new Handler(Looper.myLooper()){
            public void handleMessage(Message msg){
                ReplyText.setText(ReplyText.getText()+"\n"+(String)msg.obj);
            }
        };
    }

    private void DeviceListInit(){
        ma = this;

        if (listenerThread != null) {
            listenerThread.endrun();
        }
        deviceListLayout.removeAllViews();
        deviceCBList = null;
        deviceCBList = new ArrayList<CheckBox>();

        CheckBox deviceCheckBox = new CheckBox(this);
        deviceCheckBox.setText("255.255.255.255;ALL");
        deviceListLayout.addView(deviceCheckBox);
        deviceCBList.add(deviceCheckBox);

        listenerThread = new ListenerThread(getHandlerSetDeviceList());
        listenerThread.start();

        sendThread = new SendThread("255.255.255.255","whois");
        sendThread.start();
    }

    private void CommandSend(String sendData){
        ma = this;
        if (listenerThread != null) {
            listenerThread.endrun();
        }

        listenerThread = new ListenerThread(getHandlerReplyText());
        listenerThread.start();
        ReplyText.setText("Reply from a device.");

        for (CheckBox cb:deviceCBList){
            if (cb.isChecked()) {
                String ips = cb.getText().toString();
                int i = ips.indexOf(";");
                ips = ips.substring(0, i);

                sendThread = new SendThread(ips, sendData);
                sendThread.start();
            }
        }
    }

    public void deviceScanButtonOnClick(View view){
        if (listenerThread != null) {
            listenerThread.endrun();
        }
        deviceListLayout.removeAllViews();
        deviceCBList = null;
        deviceCBList = new ArrayList<CheckBox>();

        CheckBox deviceCheckBox = new CheckBox(this);
        deviceCheckBox.setText("255.255.255.255;ALL");
        deviceListLayout.addView(deviceCheckBox);
        deviceCBList.add(deviceCheckBox);

        listenerThread = new ListenerThread(getHandlerSetDeviceList());
        listenerThread.start();

        for (int i=0;i<255;i++){
            sendThread = new SendThread("192.168.0."+String.valueOf(i),"whois"+";192.168.0."+String.valueOf(i));
            sendThread.start();
        }
    }

    private void deviceScan(){

    }

}




■ListenerThread
package jp.no4393.amefuri001;

import android.os.Handler;
import android.os.Message;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.channels.IllegalBlockingModeException;

public class ListenerThread extends Thread {

    private boolean run;
    private DatagramSocket socket;
    private Handler toHandler;
    private int localPort;

    public ListenerThread(Handler toHandler){
        super();
        run = true;
        socket = null;
        this.toHandler = toHandler;
    }


    @Override
    public void start(){
        super.start();
        run = true;
    }

    @Override
    public void run(){
        if (socket != null) {
            socket.close();
        }
        socket = null;
        try {
            socket = new DatagramSocket(10021);
            byte[] rbuf = new byte[1024];
            DatagramPacket rpacket =
                    new DatagramPacket(rbuf, rbuf.length);
            while (run) {
                socket.receive(rpacket);
                int len = rpacket.getLength();
                String receivestr = new String(rbuf,0,len);
                Message msg = new Message();
                msg.what = 1;
                msg.obj = receivestr;
                toHandler.sendMessage(msg);
            }

        } catch (IOException e) {
        } catch (IllegalBlockingModeException e) {
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
    }


    public void endrun(){
        run = false;
        if (socket != null) {
            socket.close();
        }
        socket = null;
    }
}




■SendThread
package jp.no4393.amefuri001;


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.channels.IllegalBlockingModeException;

public class SendThread extends Thread {

    private DatagramSocket socket;
    private String destinationAddress;
    private String sendData;
    private int localPort;

    public SendThread(String destinationAddress,String sendData){
        super();
        socket = null;
        this.destinationAddress = destinationAddress;
        this.sendData = sendData;
    }

    @Override
    public void run(){
        if (socket != null) {
            socket.close();
        }
        socket = null;
        InetSocketAddress remoteAddress =
                new InetSocketAddress(destinationAddress, 10020);
        try {
            socket = new DatagramSocket(10022);
//            socket = new DatagramSocket(null);
            String message  = sendData;
            byte[] buf = message.getBytes();
            DatagramPacket packet =
                    new DatagramPacket(buf, buf.length, remoteAddress);
            socket.send(packet);

        } catch (IOException e) {
        } catch (IllegalBlockingModeException e) {
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
    }
}




■activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="jp.no4393.amefuri001.MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/baseLayout"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="110dp">

            <LinearLayout
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:id="@+id/deviceListLayout"
                android:layout_height="match_parent">

                <TextView
                    android:text="Device List"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/deviceListText"
                    android:background="@color/colorPrimary"
                    android:textColor="@android:color/background_light"
                    android:textSize="24sp"
                    android:paddingLeft="@dimen/activity_horizontal_margin" />

                <CheckBox
                    android:text="CheckBox"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/deviceCheckBox1" />
            </LinearLayout>
        </ScrollView>

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="80dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:orientation="vertical" >

                <TextView
                    android:text="Reply from a device."
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/ReplyText" />
            </LinearLayout>
        </ScrollView>

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <Button
                    android:text="Ame Kansi GO"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/ameKansiGOButton"
                    android:textAllCaps="false"
                    android:onClick="commandOnClick" />

                <Button
                    android:text="Ame Kansi Stop"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/ameKansiStopButton"
                    android:textAllCaps="false"
                    android:onClick="commandOnClick" />

                <Button
                    android:text="Sleep"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/sleepButton"
                    android:textAllCaps="false"
                    android:onClick="commandOnClick" />

                <Button
                    android:text="Reboot"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/rebootButton"
                    android:textAllCaps="false"
                    android:onClick="commandOnClick" />

                <Button
                    android:text="Power Off"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/powerOffButton"
                    android:textAllCaps="false"
                    android:onClick="commandOnClick" />

                <Button
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/yoohooButton"
                    android:text="YooHoo"
                    android:textAllCaps="false"
                    android:onClick="yoohooButtonOnClick" />

                <Button
                    android:text="Device Scan"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/deviceScanButton"
                    android:onClick="deviceScanButtonOnClick"
                    android:textAllCaps="false" />
            </LinearLayout>
        </ScrollView>

    </LinearLayout>
</RelativeLayout>

スポンサーサイト

コメントの投稿

非公開コメント

お知らせ
■注意:Google Chrome以外のブラウザでは動作しない事もあります。
お好みに応じて
画像だけの表示でいいよ
やっぱり全部表示して
背景は黒がいいな
背景を元に戻して
サブジャンル
メモリアルマップ
My Favorite Photo
検索フォーム
プロフィール

otsu.usiwakamaru

Author:otsu.usiwakamaru
五条の橋でひらりひらりと欄干を飛び渡る牛若丸のように、興の向くまま電子回路にプログラミングに写真にと飛び回っています。

プロフィール詳細

カレンダー
04 | 2017/05 | 06
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 - - -
キーワード
クリックしたキーワードの記事のみ表示します。

ダンサー ミニー ミッキー デイジー ドナルド グーフィー  キャラクター 3D 試み MFP 電池 ティンカーベル デザイン クラリス 全天球 パークテーマ チップデール プルート IXY キャスト マレフィセント スクルージ ダッフィー スモールプラネット SPOT 改造 

最新記事
月別アーカイブ
全記事表示リンク

全ての記事を表示する

おすすめプログ(新着記事)
リンク


にほんブログ村
カテゴリ
橋 (1)
PIC (1)
カウンタ
訪問者数(UU)

アクセス数

現在の閲覧者数

tael no013用