「php」タグアーカイブ

[PHP] サーバのフォルダに有る画像をファイル名の降順で指定数だけ表示

サーバの非公開ディレクトリにある画像を取得し
ファイル名の降順で最新9件表示するPHPです。

<?php
$path = "ディレクトリを設定";

if (is_dir($path)) {
  if(is_readable($path)) { 
    $dir = dir($path); 
    $file_list = array();

    while (false !== ($file_name = $dir -> read())) {
      $file = $path . "/" .$file_name;
      if (@getimagesize($file)) { 
        $encoded_file = base64_encode( file_get_contents($file) );
        array_push($file_list,$encoded_file);
        $file_list[$file_name] = $encoded_file;
      }
    }

    // 並び替え
    krsort($file_list);
    // 表示
    $counter = 0;
    foreach($file_list as $value){
      if ($counter < 9) {
        echo '<img src="data:image/jpg;base64,'.$value.'">';
      }
      $counter++;
    }

    $dir -> close();
  } else {
    echo "<p>" .htmlspecialchars($path)." は読み込みが許可されていません。";
  }
} else {
  echo 'DIR 画像がないよ';
}
?>

こちらを参考にさせて頂きました

PHP で サーバ自身の IP のみ実行可能にする

簡易的に…

とりあえずサーバ自身と同じIPアドレスである場合のみ許可する形。

もっと上手い形が他にあるとは思うんだけど、基本的に自宅使用なのでとりあえずは…。

サーバ自身のアドレスを取得

うちのグローバルIPは動的なので、サーバ自身のIPアドレスを取得出来るよう、IPアドレスを表示するPHPを置く。

任意の場所にIPアドレスを表示するPHPを作成
/php/ip-address.php

<?php echo $_SERVER['REMOTE_ADDR'];

アクセスすると自分のIPアドレスが表示される
https://homemadegarbage.com/php/ip-address.php

制限したいPHPファイルに追加

/php/ip-address-test.php

<?php
$url = 'https://homemadegarbage.com/php/ip-address.php';
$server_ip = file_get_contents($url);
$access_ip = $_SERVER['REMOTE_ADDR'];

if ( $server_ip != $access_ip ) {
  echo 'IPが許可されていません';
  return;
}

〜〜〜〜〜〜〜〜
この後に実行したい処理

自宅以外のアクセスだと「IPが許可されていません」と表示されるhttps://homemadegarbage.com/php/ip-address-test.php

原因は調べきれていないけど URL は ドメインからのURL じゃないとダメだった。
Nginx プロキシサーバのため、 $_SERVER[‘REMOTE_HOST’] だと backend のURLを拾ってしまう(これもおいおい直さなきゃ)ので URL 直書き。

wordpress 投稿画面にカスタムボタン追加(プラグインなし): Nginx キャッシュ削除ボタン

キャッシュの削除をコマンドでやっていたけど、面倒なので wordpress の投稿画面に付けたいと思い立ち、勉強がてら自前でやってみました。

コマンド実行用PHP

サーバの任意の場所にphpファイルを作成。

/php/delete-nginx-cache.php

<?php
$result = `rm -r /var/cache/nginx/cache/* 2>&1`;
echo $result, PHP_EOL;

ブラウザからアクセスしてみて実行される事を確認。

最初、sudo 付けないでなんで実行出来るんだろ?と疑問に思いしばらく調査してしまった。
cache ファイルの所有者とphp実行のユーザが同じ www-data だからだった。

投稿画面にボタン追加&PHP実行用JS作成

サーバの任意の場所にJSファイルを作成。

/js/wp-settings.js

jQuery(function($) {
  // 投稿画面でない場合終了
  if(!($('#post').length)){
    return;
  }
  // ボタン追加
  var delCacheBtn = '<span id="delCacheBtn" class="button">キャッシュ削除</span>'
  $('#wp-content-media-buttons').append(delCacheBtn);

  // クリックでPHP実行
  $('#delCacheBtn').on('click',function(){
    $.get("/php/delete-nginx-cache.php", function(data, status){
      //通信に失敗 
      if (status != 'success') {
        alert('通信に失敗しました。');
      }
      // PHPからレスポンス有り
      data = data.replace(/\r?\n/g,''); //改行コード削除
      if (data) {
        console.log('delCacheBtn: ' + data.charCodeAt(0));
      }
    });
  });
});

追加する位置は、「メディアを追加」ボタンの並びの最後尾にしました。

今後はこのJSに色々追加すれば管理画面カスタマイズが可能!

管理画面にJSを追加する

wordpress の function.php に追加

/*------------ 管理画面にJS追加 -------------*/
function admin_func() {
echo '<script type="text/javascript" src="/js/wp-settings.js"></script>';
}
add_action('admin_head', 'admin_func');

ここで、「add_action(‘admin_head-post.php’」などと指定したらそのページだけに適用出来る。

今回は管理画面全体。

完成!

????????????

IP制限

IPアドレスをサーバ自身と同じ場合のみに制限してみた。

PHP で サーバ自身の IP のみ実行可能にする

プロキシキャッシュ 設定の記事はこちら

ラズパイサーバー ★ Nginx 追加設定

 

WordPress で「もっと見る」を実装(プラグイン無し)②

前回の作業に追加しました。

WordPress で「もっと見る」を実装(プラグイン無し)

対応状況

対応済

  • TOPページ
  • カテゴリアーカイブ
  • 検索結果(日本語結果が微妙な気がする、要検証)

未対応

  • 月別アーカイブ

いずれ対応したい

  • 自動スクロール

jQuery

function moreDisp() {
  // more
  $(document).on("click","#more_disp a", function() {
    var now_post_num = $('.large-8.medium-8.columns .card.callout').length; 
    var get_post_num = 10; // 一度に取得する数 
    var dir = location.href.split("/");
    var get_post_str = dir[dir.length -1];
    var more_disp = $("#more_disp");
        $.ajax({
            type: 'post',
            url: '/PATH/more-disp.php',
            data: {
                'now_post_num': now_post_num,
                'get_post_num': get_post_num,
                'get_post_str': get_post_str
            },
            success: function(data) {
                now_post_num = now_post_num + get_post_num;
                data = JSON.parse(data);
                $(".large-8.medium-8.columns").append(data['html']);
                $(more_disp).remove();
                if (!data['noDataFlg']) {
                    $(".large-8.medium-8.columns").append(more_disp);
                }
            }
        });
        return false;
    });
}

PHP

<?php

require_once("/WP PATH/wp-config.php");

$now_post_num = $_POST['now_post_num'];
$get_post_num = $_POST['get_post_num']+1;
$get_post_str = $_POST['get_post_str'];
 
if (empty($get_post_str)){
    $results = $wpdb->get_results($wpdb->prepare("
        SELECT
            $wpdb->posts.ID,
            $wpdb->posts.post_author,
            $wpdb->posts.post_title,
            $wpdb->posts.post_content
        FROM $wpdb->posts
        WHERE $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish' 
        ORDER BY 
            $wpdb->posts.post_date DESC 
        LIMIT $now_post_num, $get_post_num
        "));
} elseif (preg_match('/^\?s=/',$get_post_str)) {
    $keyword = str_replace('?s=','', $get_post_str);
    $keyword = urldecode($keyword);
    $keyword = '%'.like_escape( $keyword ).'%';
    $results = $wpdb->get_results($wpdb->prepare("
        SELECT
            $wpdb->posts.ID,
            $wpdb->posts.post_author,
            $wpdb->posts.post_title,
            $wpdb->posts.post_content
        FROM $wpdb->posts
        WHERE $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish' AND $wpdb->posts.post_title LIKE %s
        ORDER BY 
            $wpdb->posts.post_date DESC 
        LIMIT $now_post_num, $get_post_num
        ",$keyword));
} else {
    $results = $wpdb->get_results($wpdb->prepare("
        SELECT
            $wpdb->posts.ID,
            $wpdb->posts.post_author,
            $wpdb->posts.post_title,
            $wpdb->posts.post_content,
            $wpdb->terms.name
        FROM $wpdb->posts
        JOIN $wpdb->term_relationships ON $wpdb->term_relationships.object_id = $wpdb->posts.ID
        JOIN $wpdb->term_taxonomy ON $wpdb->term_taxonomy.term_taxonomy_id = $wpdb->term_relationships.term_taxonomy_id
        JOIN $wpdb->terms ON $wpdb->terms.term_id = $wpdb->term_taxonomy.term_id
        WHERE $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish' AND $wpdb->terms.slug = '$get_post_str'
        ORDER BY 
            $wpdb->posts.post_date DESC 
        LIMIT $now_post_num, $get_post_num
        "));
}

if ( count($results) < $get_post_num ) {
    $noDataFlg = 1;
} else {
    $noDataFlg = 0;
    array_pop($results);
}

$html = "";
foreach ($results as $value) {
    $html .= '<div class="card callout">';
    $html .= '<div class="row">';
    $html .= '<div class="large-3 medium-3 small-3 columns"><p>'.get_the_post_thumbnail($value->ID).'</p></div>';
    $html .= '<h5><a href="'.get_permalink($value->ID).'">'.apply_filters('the_title', $value->post_title).'</a></h5>';
    $html .= '<p class="excerpt">'.mb_substr(strip_tags($value-> post_content),0,100).'...</p>';
    $html .= '<p class="posted text-right">'.' '.get_the_date( 'Y.m.d', $value->ID ).' '.get_the_author_meta('display_name', $value->post_author ).' '.get_avatar($value->post_author,35).'</p>';
    $html .= '</div>';
    $html .= '</div>';
    $html .= '</div>';
}
 
$returnObj = array();
$returnObj = array(
    'noDataFlg' => $noDataFlg,
    'html' => $html,
);
$returnObj = json_encode($returnObj);
 
echo $returnObj; 
?>

もっと簡潔に記述したいものだけど…

 

PHP7.0にアップデート 中に気付いたけど、WPのデバッグモードがONになっていると動作しない。

MJPG-streamer カメラをブラウザから起動/終了させる

ブラウザからON/OFF出来るボタンを付けてみました。

C言語でラッパープログラム作成

プログラムを置く任意の場所に移動

cd tool

起動用プログラム

起動用ファイルを作成

vi mjpg-streamer_open.c

記述、保存

#include <stdlib.h>

int main(void){
  setuid(0);
  system("LD_LIBRARY_PATH=/usr/local/lib mjpg_streamer -i \"/usr/local/lib/input_uvc.so -y -r 640x480 -f 15 -d /dev/video0 -y\" -o \"/usr/local/lib/output_http.so -p 8081 -w www -c BASIC認証のID:BASIC認証のパスワード\" -b");
  return 0;
}

コンパイル

sudo gcc -o mjpg-streamer_open mjpg-streamer_open.c

SUID(Set User ID)

sudo chmod +s mjpg-streamer_open

起動出来るかテスト

./mjpg-streamer_open

終了用プログラム

起動用ファイルを作成

vi mjpg-streamer_close.c

記述、保存

#include <stdlib.h>

int main(void){
  setuid(0);
  system("kill -9 `pidof mjpg_streamer`");
  return 0;
}

SUID(Set User ID)

sudo chmod +s mjpg-streamer_close

終了出来るかテスト

./mjpg-streamer_close

実行用PHP

任意の場所に作成

mjpg-streamer.php

<?php
$action = $_GET['action'];
$result = `/パス/mjpg-streamer_$action 2>&1`;
echo $result, PHP_EOL;

ブラウザからPHPにアクセスして起動・終了のテスト

HTML 

ボタンを作成

<p>
  <span id="cameraOpen" class="button">ON</span>
  <span id="cameraClose" class="button">OFF</span>
</p>

JavaScript

<script>
  function postMjpgStreamer(action) {
    var url = '/パス/mjpg-streamer.php?action=' + action; // リクエスト先URL
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onreadystatechange = function () {
      if (xhr.readyState != 4) {
      // リクエスト中
      } else if (xhr.status != 200) {
      // 失敗
        alert('失敗しました。もう一回試してみてね!');
      } else {
      // 取得成功
      alert(xhr.response);
        window.location.reload() ;
      }
    };
    xhr.send(null);
  }

  document.getElementById('cameraOpen').addEventListener('click' ,function() {
    postMjpgStreamer('open');
  }
  ,false);

  document.getElementById('cameraClose').addEventListener('click' ,function() {
    postMjpgStreamer('close');
  }
  ,false);
</script>

HTML&JSの デモソースコード

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MJPG-streamer Button</title>
<meta charset="utf-8">
<meta name="description" content="MJPG-streamer カメラをブラウザから起動/終了させる デモ">
<meta name="author" content="Home Made Garbage">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="">
<!--[if lt IE 9]>
<script src="//cdn.jsdelivr.net/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<link rel="shortcut icon" href="">
</head>
<body>

<button type="button" id="cameraOpen">ON</button>
<button type="button" id="cameraClose">OFF</button>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
  function postMjpgStreamer(action) {
    var url = 'mjpg-streamer.php?action=' + action; // リクエスト先URL
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onreadystatechange = function () {
      if (xhr.readyState != 4) {
      // リクエスト中
      } else if (xhr.status != 200) {
      // 失敗
        alert('失敗しました。もう一回試してみてね!');
      } else {
      // 取得成功
      alert(xhr.response);
        window.location.reload() ;
      }
    };
    xhr.send(null);
  }
 
  document.getElementById('cameraOpen').addEventListener('click' ,function() {
    postMjpgStreamer('open');
  }
  ,false);
 
  document.getElementById('cameraClose').addEventListener('click' ,function() {
    postMjpgStreamer('close');
  }
  ,false);
</script>
</body>
</html>

GitHubにもあげてみました。

MJPG-streamer-Button/index.html at master · homemadegarbage/MJPG-streamer-Button
mjpg-streamer.php の /パス/ の部分を修正して頂ければ、アップロードしてそのまま動く形になっているかと思います。

完成

※ 上記スクリーンショットは、MJPG-Streamer の埋め込み&ボタンに当ブログ上のCSSが当たっている状態です。デモソースコードではMJPG-Streamerの埋め込みはしておらず、HTMLデフォルトのボタンのみ表示されている状態になります。

MJPG-Streamer の埋め込みについては、こちらの記事に記載しております。

MJPG-streamer カメラをブラウザから起動/終了させる

スマホからも・・・

参考にさせて頂きました

WordPress で「もっと見る」を実装(プラグイン無し)

プラグインを使えば簡単に出来そうですが、プラグイン無しでやってみたかったので

WordPress ページングはやめてAjaxローディングにする | hijiriworld Web

を参考にさせて頂き、アーカイブページに「もっと見る」を実装しました。

WordPress テンプレート

件数が10件より多いとき、ループの外にボタンを追加

		<?php 
			while ( have_posts() ) : the_post();
				get_template_part( 'template-parts/content', get_post_format() );
			endwhile;

			$term = get_queried_object();
			if ($term->count > 10) {
				echo '<div id="more_disp"><a href="#" class="button">Load More</a></div>';
			}
		?>

jQuery

URLからカテゴリのslugを取得してPHPに渡しています

	$(document).on("click","#more_disp a", function() {
		var now_post_num = $('main .card.callout').length; // 現在表示されている数
		var get_post_num = 10; // 一度に取得する数        
		var dir = location.href.split("/");
		var get_post_slug = dir[dir.length -1];
//        $("#more_disp").html('<img class="ajax_loading" src="http://localhost/wp/wp-content/themes/ajax_loading/images/ajax_loader.gif" />');
         console.log(get_post_slug);
        $.ajax({
            type: 'post',
            url: '/php/more-disp.php',
            data: {
                'now_post_num': now_post_num,
                'get_post_num': get_post_num,
                'get_post_slug': get_post_slug
            },
            success: function(data) {
                now_post_num = now_post_num + get_post_num;
                data = JSON.parse(data);
                $("main").append(data['html']);
                $(more_disp).remove();
                if (!data['noDataFlg']) {
                    $("main").append(more_disp);
                }
            }
        });
        return false;
    });

ローディング画像はまだ用意していない…

PHP

/php/more-disp.php

カテゴリを取得する為にSQLのJOINとか調べてやってみましたが、もっと良い書き方があるかもしれません…。何故かASが使えませんでした。(多分何か書き方が悪かったのかも…)

<?php

require_once("../wp/wp-config.php"); 

$now_post_num = $_POST['now_post_num'];
$get_post_num = $_POST['get_post_num']+1;
$get_post_slug = $_POST['get_post_slug'];
 
$results = $wpdb->get_results($wpdb->prepare("
    SELECT
        $wpdb->posts.ID,
        $wpdb->posts.post_author,
        $wpdb->posts.post_title,
        $wpdb->posts.post_content,
        $wpdb->terms.name
    FROM $wpdb->posts
    JOIN $wpdb->term_relationships ON $wpdb->term_relationships.object_id = $wpdb->posts.ID
    JOIN $wpdb->term_taxonomy ON $wpdb->term_taxonomy.term_taxonomy_id = $wpdb->term_relationships.term_taxonomy_id
    JOIN $wpdb->terms ON $wpdb->terms.term_id = $wpdb->term_taxonomy.term_id
    WHERE $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish' AND $wpdb->terms.slug = '$get_post_slug'
    ORDER BY 
        $wpdb->posts.post_date DESC 
    LIMIT $now_post_num, $get_post_num
        ", OBJECT));
if ( count($results) < $get_post_num ) {
    $noDataFlg = 1;
} else {
    $noDataFlg = 0;
    array_pop($results);
}
 
$html = "";
foreach ($results as $value) {
    $html .= '<div class="card callout">';
    $html .= '<div class="row">';
    $html .= '<div class="large-3 medium-3 small-3 columns"><p>'.get_the_post_thumbnail($value->ID).'</p></div>';
    $html .= '<h5><a href="'.get_permalink($value->ID).'">'.apply_filters('the_title', $value->post_title).'</a></h5>';
    $html .= '<p class="excerpt">'.mb_substr(strip_tags($value-> post_content),0,100).'...</p>';
    $html .= '<p class="posted text-right">'.$value->name.' '.get_the_date( 'Y.m.d', $value->ID ).' '.get_the_author_meta('display_name', $value->post_author ).' '.get_avatar($value->post_author,35).'</p>';
    $html .= '</div>';
    $html .= '</div>';
    $html .= '</div>';
}
 
$returnObj = array();
$returnObj = array(
    'noDataFlg' => $noDataFlg,
    'html' => $html,
);
$returnObj = json_encode($returnObj);
 
echo $returnObj; 
?>

追記

TOP、カテゴリ、検索結果を追加しました。勉強中・・・

WordPress で「もっと見る」を実装(プラグイン無し)②

 

よだん。
今日は長女ちゃんが冬休みの工作をしていました。

 

#工作 #冬休み #自由研究

Home Made Garbageさん(@homemadegarbage)が投稿した写真 –

さくらのIoTモジュールでGPSリアルタイムトラッキング(完成編)

さくらのIoTモジュールでGPSリアルタイムトラッキング を行いまして、長女ちゃんの登下で実用していたのですが、今回IFTTTで帰宅時に家に近づくとスマホに通知が来るようにしましたので完結編としてまとめます。

概要

さくらのIoTモジュールでGPS情報を送り、Web上のマップでリアルタイムに位置情報を表示します。
家に近づいたらIFTTTでスマホに通知を送ります。

さくらのIoTモジュール+GPSの設定

詳細は以下のとおりでマイコンにはArduino Nano互換機を使用しています。

さくらのIoT Platform α版 準備編その4

[amazonjs asin=”B01CZQANN0″ locale=”JP” title=”HiLetgo Mini USB Nano V3.0 ATmega328P CH340G 5V 16M マイクロコントローラーボード Arduinoと互換”]
安すぎるー!

Arduino IDE用コード

#include <SakuraAlpha.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

#define BUF_LEN 16

static const int RXPin = 10, TXPin = 11;
static const uint32_t GPSBaud = 9600;

float lat;
float lng;

//自宅の緯度経度
float latHome = 4x.xxxxxxx;
float lngHome = 14x.xxxxxxx;

float earthR = 6378137; //地球の長半径[m]
float distance;
uint32_t sateNo = 0;
uint32_t state = 0;

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

SakuraAlphaI2C sakura;

void setup()
{
  Serial.begin(9600);
  ss.begin(GPSBaud);
  
  for(;;){
    Serial.println("Waiting to come online...");
    if( sakura.getNetworkStatus() == 1 ) break;
    delay(1000);
  }
  Serial.println("online!");

}

void loop(){
  unsigned long start = millis();
  
  do {
    while (ss.available() > 0){
      gps.encode(ss.read());
    }
  } while (millis() - start < 5000);
  
  lat = gps.location.lat();
  lng = gps.location.lng();
  sateNo = gps.satellites.value();

  //2点の緯度経度から距離概算
  //http://qiita.com/fantm21/items/9f587d2526cf7231a0ba
  distance = sqrt(pow(earthR * (lat - latHome)/180.0*M_PI, 2) + pow(cos(lngHome/180.0*M_PI) * earthR * (lng - lngHome)/180.0*M_PI, 2));

  if(sateNo >= 4){
    //距離ステータス
    if(state == 0 && distance > 300.0){
      state = 1;
    }
    if(state == 1 && distance < 200.0){ 
      state = 2; //1st notification (200m以内)
    }
    if(state == 2 && distance < 100.0){
      state = 3; //2nd notification (100m以内)
    }
    if(state == 3 && distance < 50.0){
      state = 4; //3rd notification (50m以内)
    }

    //モジュール転送
    sakura.writeChannel(0, lat);      //ch0 緯度
    sakura.writeChannel(1, lng);      //ch1 経度
    sakura.writeChannel(2, sateNo);   //ch2 受信衛星数
    sakura.writeChannel(3, distance); //ch3 自宅からの距離[m]
    sakura.writeChannel(4, state);    //ch4 距離ステータス
  }
}

マイコンで5秒毎にGPSモジュールの情報(緯度、軽度、受信衛星数)を受けて、緯度経度から自宅からの距離を計算します。距離の概算には以下を参考にさせていただきました。
[bc url=”http://qiita.com/fantm21/items/9f587d2526cf7231a0ba”]

通学・帰宅を判断するために距離ステータスを設け、家から300m以上離れると1を代入し帰宅時に200m以内に入ると2、100m以内で3、50m以内で4としています。

以上のデータをGPS受信衛星数が4以上の時にIoT通信モジュールにI2Cで転送し、3G回線でプラットフォームに通信しています。

IFTTTの設定

IoT通信モジュールからの距離ステータスをうけて長女ちゃんが家に近づいたらスマホに通知が来るようにIFTTTを利用しました。

IFTTTとは

if this then thatの略で各種Webサービス同士を連携して自分好みのWebサービス(レシピ)を生成することができます。ここではthis(トリガ)にwebに値(3つまで)を送信(GETまたはPOST)できるMakerチャンネルを使用しthat(アクション)にスマホに通知を送るIF Notificationsチャンネルを使用しました。

レシピ生成

Makerチャンネルを使用して、スマホに通知を送る方法は以下に詳細が記載せれており非常に参考になります。
[bc url=”http://mag.switch-science.com/2015/06/25/ifttt-maker-channel/”]

  • Makerチャンネルを作成

シークレットキーは後で使用するので控えておく。

  • レシピを作成
    新しいレシピを作成してthisにMakerチャンネルをthatにIF Notificationsチャンネルを選択します。

イベント名は”sakuraGPS”としました。

通知内容は
長女ちゃん家まであと {{Value1}} m 
としValue1には家からの距離が代入されるようにします。

以下でサーバーからGET(またはPOST)すると通知が来ます。

http://maker.ifttt.com/trigger/sakuraGPS/with/key/シークレットキー?value1={家からの距離}

Webサーバーの設定

「さくらのIoT Platform α版」では、下記のサービスが提供されています。

  • Outgoing WebHook:モジュール→サーバー
  • WebSocket:双方向
  • Incoming Webhook:サーバー→モジュール

今回は、このブログを動かしている Raspberry Pi 2 サーバーに、「Outgoing WebHook」から値を受信し、処理しています。

Nginx の設定

/sakura/ ディレクトリに Basic 認証を付けています。

sudo vi /etc/nginx/sites-available/default

下記を追加

        location /sakura/ {
                auth_basic "Restricted";
                auth_basic_user_file /etc/nginx/.htpasswd;
                proxy_pass http://backend;
        }

※ 設定の詳細はこちらの記事に記載しております。
ラズパイサーバー ★ Nginx 追加設定

PHP

  1. Outgoing WebHook を受信し、/sakura/gps.txt に書き出す
  2. channel 4 の statusが2以上に切り替わった時、IFFTに距離を送信する

/sakura/index.php

<?php
    $filepath = "gps.txt";
    
    $json = file_get_contents("php://input");
    $data = json_decode($json,true);
       
    $place = array();
    for ($i = 0; $i <= 4; $i++) {
        array_push($place,$data["payload"]["channels"][$i]["value"]);
    }

    // 値が空の時がある為、nullチェック
    if ( in_array(null, $place, true) == false ) {
        // 前回のステータスを取得
        $rData = fopen($filepath,'r');
        $content = file_get_contents($filepath);
        $placeOld = explode(",", $content);
        // 前回のステータスと変化 且つ ステータスが2以上の時、IFFTに送信
        if ( $placeOld[4] != $place[4] && $place[4] >= 2 ) {
            // IFFT で設定したシークレットキーのURLに、距離をGETする
            file_get_contents('http://maker.ifttt.com/trigger/sakuraGPS/with/key/IFFTのシークレットキー?value1='.(string)round($place[3],1)) ;
        }
        // gps.txt に書き込む
        file_put_contents($filepath, implode(",",$place));
    }

何故か高頻度で空の値が入ってくるので、(PHP側の原因なのか?データなのか?原因究明できていません)null チェックをしています。

gps.txt

緯度、経度、衛星数、距離、ステータスが書き出されます。

43.00745,141.44801,4,7711.8975,4

HTML ( OpenStreetMap … JavaScript)

地図(OpenStreetMap)でリアルタイムトラッキングさせる記述です。

<div id="mapTestText" class="mapTestText">&nbsp;</div>
<div id="map" class="map">&nbsp;</div>
<p><script type="text/javascript">// <![CDATA[
    var opacity = 1;
    
    // 地図レイヤー作成
    var map1 = new ol.layer.Tile({
        opacity: opacity,
        source: new ol.source.OSM()
    });
    
    // 家アイコン
    var iconFeature = new ol.Feature({
        geometry: new ol.geom.Point(ol.proj.fromLonLat([経度, 緯度])),
    });
    var iconStyle = new ol.style.Style({
        image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
            src: '/wp/wp-content/uploads/map-icon-home.png',
        }))
    });
    iconFeature.setStyle(iconStyle);

    // 学校アイコン    
    var iconFeature2 = new ol.Feature({
        geometry: new ol.geom.Point(ol.proj.fromLonLat([経度, 緯度])),
    });
    var iconStyle2 = new ol.style.Style({
        image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
            src: '/wp/wp-content/uploads/map-icon-school.png',
        }))
    });
    iconFeature2.setStyle(iconStyle2);
    
    // 児童館アイコン    
    var iconFeature3 = new ol.Feature({
        geometry: new ol.geom.Point(ol.proj.fromLonLat([経度, 緯度])),
    });
    var iconStyle3 = new ol.style.Style({
        image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
            src: '/wp/wp-content/uploads/map-icon-jidokan.png'
        }))
    });
    iconFeature3.setStyle(iconStyle3);
    
    // アイコンレイヤー作成    
    var vectorSource = new ol.source.Vector({
        features: [iconFeature,iconFeature2,iconFeature3]
    });
    var iconLayer = new ol.layer.Vector({
        source: vectorSource,
    });
    
    // 地図表示
    var map = new ol.Map({
        layers: [map1,iconLayer],
        target: 'map',
        interactions: ol.interaction.defaults({mouseWheelZoom:false}),
        view: new ol.View({
            // 地図の中心地セット
            center: ol.proj.fromLonLat([経度, 緯度]),
            zoom: 17
        }),
    });
    
    // リアルタイムアイコン作成
    var imgElement = document.createElement('img');
    imgElement.setAttribute('src', '/wp/wp-content/uploads/icon-sister-150x150.png');
    var marker = new ol.Overlay({
        element: imgElement,
        position: '',
        positioning: 'bottom-center'
    });
    map.addOverlay(marker);

    // 現在地のセット
    function gpsTracking() {
        req = new XMLHttpRequest(); 
        req.open("GET", "/sakura/gps.txt", true, "BASIC認証のID", "BASIC認証のパスワード");
        req.send(null);
        req.onload = function(){
            var genzaiti = new Array();
            var source = req.responseText;
            genzaiti = source.split(",");
            // HTML上に書き出し(確認の為)
            document.getElementById('mapTestText').innerHTML = 'lat:' + genzaiti[0] + ' long:' + genzaiti[1] + ' satellites: ' + genzaiti[2] + ' 距離: ' + Number(genzaiti[3]).toFixed(1) + ' status: ' + genzaiti[4] + ' source: ' + source;
            // リアルタイムアイコンに現在地のセット
            marker.setPosition( ol.proj.fromLonLat([ Number(genzaiti[1]) , Number(genzaiti[0]) ]) );
            // 地図の中心地をアイコンの場所にセット
            map.getView().setCenter(ol.proj.transform([ Number(genzaiti[1]) , Number(genzaiti[0]) ], 'EPSG:4326', 'EPSG:3857'));
        }
    };
    
    gpsTracking();
    // 5秒ごとに実行
    setInterval('gpsTracking()',5000);

// ]]></script></p>

このような感じで、地図上に現在地のアイコンが表示されます。

※ 動作の様子はこちらの記事に記載しております。
さくらのIOTモジュールでGPSリアルタイムトラッキング

動作

無事に通知が来ることを確認できました 🙂 

これで家の鍵をもたせてない長女ちゃんがいつ帰ってきても安心だね!

WordPress に Instagram API を使って タグのギャラリー表示②

前回の記事↓から 少し改造しました。

WordPress に Instagram API を使って タグのギャラリー表示

改造ポイント

  • タグをHTML側で指定可能に
  • ページ内に複数のギャラリーを表示

コード

HTML

<div class="instagram">
<h3>タグ:<span class="tag">家族</span></h3>
</div>
<div class="instagram">
<h3>タグ:<span class="tag">空</span></h3>
</div>

「class=”instagram”」の中で、タグの文字列を「class=”tag”」で囲む。

タグ名を表示させたくない場合はstyleで消すと良いと思います。

<div class="instagram">
<span class="tag" style="display:none;">家族</span>
</div>

jQuery

/js/instagram.js

jQuery(function($) {
    $(function(){
        $.each($(".instagram"), function(i, val) {
            var $tag = $(val).find('.tag').text();
            var html = '';

            $.ajax({
                url: '/php/instagram.php',//PHPファイルのURL
                type:'POST',
                data: {
                    'tag': $tag
                },
                dataType: 'json'
            }).done(function(data){
                //通信成功時の処理
                html += '<ul>';
                $.each(data.data,function(i,item){
                    var imgurl = item.images.low_resolution.url; //低解像度の画像のURLを取得
                    var link = item.link; //リンクを取得
                    html += "<li><a href='" + link + "' target='_blank'><img src='" + imgurl + "'></a></li>";
                });
                html += '</ul>';
            }).fail(function(){
                //通信失敗時の処理
                html = "<li>画像を取得できません。</li>";
            }).always(function(){
                //通信完了時の処理
                $(val).append(html);
            });
        });
    });
});

HTMLで取得したタグをPHPにPOST

php

/php/instagram.php

<?php
//POSTリクエストの場合のみ受付
if($_SERVER['REQUEST_METHOD'] == 'POST'){
    //アクセストークン
    $access_token = "アクセストークン";
    $page = $_POST['tag'];
    //JSONデータを取得して出力
    echo @file_get_contents("https://api.instagram.com/v1/tags/$page/media/recent?access_token={$access_token}");
    //終了
    exit;
}
?>

POSTされたタグをセットしてGET 

css

/*--------------------------------------------------------
Instagram
--------------------------------------------------------*/
.instagram ul {
	padding: 0;
	margin-bottom: 20px;
}
.instagram li {
	list-style: none;
	display: inline-block;
	width: 30%;
	margin: 10px;
}

 

表示

タグ:日常

タグ:家族

捕捉

前回、ギャラリー表示したはずの「フクロウ」が最近表示されてないなーと調べてみたら、

    • Instagram の API は最初 Sandbox Mode となっている
    • Sandbox Mode では最新の20件しか取得出来ない
    • APIのフルアクセスには審査が必要で、個人のHPに表示させたいだけじゃだめ

ということで、最新20件よりも過去のデータは取ってこれないんですね、残念。

参考にさせて頂きました

WordPress に Instagram API を使って タグのギャラリー表示

先日書いたフクロウカフェの記事で、「インスタグラムのタグを拾ってギャラリー表示したい!」と試みたところめちゃくちゃ苦労したので、残しておきます。

まずはAPIの取得

登録方法などは色々なサイト様で解説して下さっているのでここではざっくり記載します。

Instagram Developer Documentation にアクセスして、「Regist Your Application」。

下記を入力します。

  • Application Name;適当に
  • Description:適当に
  • Website URL:使用するサイトのURL
  • Valid redirect URIs :私は上記と同じURLにしました
  • Disable implicit OAuth;チェックを外す

登録すると、「CLIENT ID」などが出てきます。

この「CLIENT ID」を使ってアクセストークンの取得をします。

アクセストークンの取得

此処でハマりました。

下記ではタグ取得ができない

https://instagram.com/oauth/authorize/?client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=token

色々なサイトに上記のパターンで書いてあったのですが、これだとタグの取得が出来ないんです。(ユーザーの取得はできます。)「 400 error scope=public_content」云々ってエラーが出ます。

APIにアクセスすると、

https://api.instagram.com/v1/tags/フクロウカフェ?access_token=取得したアクセストークン

エラー内容は確かこんな感じでした。

{"meta": {"error_type": "OAuthPermissionsException", "code": 400, "error_message": "This request requires scope=public_content, but this access token is not authorized with this scope. The user must re-authorize your application with scope=public_content to be granted this permissions."}}

「&scope=public_content」を追加する

謎過ぎてエラーメッセージコピペで検索して辿り着きました。アクセストークン取得時に「&scope=public_content」を追加する必要があるようです。

Can’t get Instagram app work with the new API policy and restrictions 

(英語が読めないから色々体当たり・・・)

再度アクセストークン取得

https://api.instagram.com/oauth/authorize/?client_id=xXxXx&redirect_uri=http://thewebapp.dev/&response_type=token&scope=public_content

 

APIにアクセスして確認

Instagram Developer Documentation を参考に適宜変更して、ブラウザのURLにアクセスして、なんか取れてるのを確認します。

例えばこれだと、

https://api.instagram.com/v1/tags/フクロウカフェ?access_token=取得したアクセストークン

こんな風に表示されます。

{"meta": {"code": 200}, "data": {"media_count": 13632, "name": "\u30d5\u30af\u30ed\u30a6\u30ab\u30d5\u30a7"}}

 

WordPressにGallery表示

ここも色々はまって・・・・例えば「 instafeed.js 」もタグ取得だけが何故か上手くいかず、他ももいくつか試してみてだめだったのでこちらを参考にさせて頂きました。

[bc url=”http://marimelody.net/web/jquery-ajax/”]

ほぼこちらのサイト様の通りにさせて頂いたので、詳細の説明は割愛致します。

php

echo @file_get_contents("https://api.instagram.com/v1/users/self/media/recent/?access_token={$access_token}");

のところを、タグ取得用に修正しました。

echo @file_get_contents("https://api.instagram.com/v1/tags/フクロウカフェ/media/recent?access_token={$access_token}");

※ 2016.10.01追記:タグを直書きだと汎用性が無い…ので、HTML側で指定するように改造しました。

WordPress に Instagram API を使って タグのギャラリー表示②

jQuery

そしてjQueryでもハマったのが・・・・・

WordPress の jQuery は、「$」が使えない!

下記で囲った中に上記の jQuery を 書いた所うまくいきました。

jQuery(function($) {
  
});

参考にさせて頂きました。

WordPress で jQuery を使う時の注意点

因みに私の環境では、functions.php に記述しなくても jQuery が使えたので、此処は記載していません。

css

スタイルシートに適当にこんな感じで追加しました。もうちょっと調整してもよいかもとは思っています。

/*--------------------------------------------------------
Instagram
--------------------------------------------------------*/
.instagram li {
	list-style: none;
	display: inline-block;
	width: 30%;
}

表示!

これでやっと表示ができましたー。

こちら のページに表示してあります。Instagramに「フクロウカフェ」タグが追加される度にこのページも自動的に更新されていきます。

さくらのIOTモジュールでGPSリアルタイムトラッキング(調整その1)

前回の微調整です。

さくらのIOTモジュールでGPSリアルタイムトラッキング

まずはお父ちゃんの方で、「衛生の数」も拾うようにしてもらいました。

衛星数も表示するように追加。

    function getPlace() {
        genzaiti = new Array();
        var source = req.responseText;
        genzaiti = source.split(",");
        document.getElementById('mapTestText').innerHTML = 'lat: ' + genzaiti[0] +
                                                            '<br />long: ' + genzaiti[1] +
                                                            '<br />satellites: ' + genzaiti[2] +
                                                            '<br />source: ' + source;
    }

あと、下記で、jsonデータが破損しているときがある。

    $json = file_get_contents("php://input");

元々のデータなのか、php側の問題なのか・・・原因はまだ切り分けられていないので課題。

そこで、緯度経度が取得できない時にアイコンが飛んでっちゃっていたので、それを修正。

適当に、緯度経度の値を40と100位にしておいて、それ以下だと動作させないようにしました。

            if ( genzaiti[0] > 40 && genzaiti[1] > 100 ) {
                marker.setPosition( ol.proj.fromLonLat([ Number(genzaiti[1]) , Number(genzaiti[0]) ]) );
                map.getView().setCenter(ol.proj.transform([ Number(genzaiti[1]) , Number(genzaiti[0]) ], 'EPSG:4326', 'EPSG:3857'));
            }

アイコンを中心に自動的に追従してほしいので、下記を追加。(上記のifの中に入ってます)

 map.getView().setCenter(ol.proj.transform([ Number(genzaiti[1]) , Number(genzaiti[0]) ], 'EPSG:4326', 'EPSG:3857'));

ところが、マウスでドラッグして移動した時も強制的に戻されちゃう。だめじゃん!ドラッグされたときは処理しないようにしなくちゃ。

現状はこんな感じです。

まだまだ課題がありつつの・・・。あとはWebsocketの方も試してみる予定です。双方向通信もしたいので!:grin:

さくらのIOTモジュールでGPSリアルタイムトラッキング

先日 (2016/6/1) 届いた「さくらのIoT Platform α版 通信モジュール」に、お父ちゃんGPSを搭載してくれました! 

そこで、とうとう 前回行った OpenStreetMap & OpenLayers3 で GPSのログから地図に経路の表示 で、リアルタイムGPSトラッキングを実装しました!

なにもかも分からないことだらけなのですべて苦戦しました  色々と至らないところが有るかと思いますが何卒ご容赦くださいませ。

1. PHPでOutgoing Webhooks を受信

まず、さくらのIoTプラットフォームでは現在、下記が用意されています。
(開発者向け利用マニュアルより抜粋させて頂きました。)

  • Outgoing WebHook;モジュールから届いたチャンネルのデータをユーザーのHTTPサーバにPOSTする機能です。
  • WebSocket (over SSL):プラットフォームとWebSocketを使って接続することで、モジュールと双方向にチャンネルデータのやりとりができる機能です。

 

今回は「Outgoing WebHook」にて、当ブログ 「Home Made Garbage」が動いている、Raspberry Pi 2 サーバーで受信を行うことにしました。

管理画面では、「名前」「Payload URL」「Secret」を登録します。

ここでは、殆ど未経験の私が試みるにあたって、「まずは最低限の動作を」という所まで先にやりたかったので、Secret の登録は行っていません。(後で追加予定)

まずは、設定した「Payload URL」に、受信用のphpを設置します。

index.php

<?php
    $json = file_get_contents("php://input");
    $data = json_decode($json,true);
    

取得した情報をブラウザで表示確認しようとあくせく苦戦したのですが、方法がさっぱり解らなかったので一旦 txt ファイルに出力して確認することにしました。

    file_put_contents("json.txt", serialize($json));

エラー処理やら何やらは一切行っておりません。めっちゃシンプルです。txtファイルは事前に作成しておく必要があります。

GPSは、5秒ごとに送信される設定になっているので、json.txt が 5秒ごとに更新されていきます。

json.txt の中身はこんな感じです。

s:217:"{"payload": {"channels": [{"channel": 0, "value": 43.NN4423, "type": "f"}, {"channel": 1, "value": 141.NN972, "type": "f"}]}, "module": "uttdPKFFQ6M7", "datetime": "2016-06-16T02:13:25.408958456Z", "type": "channels"}";

※緯度経度の小数点以下二桁は「NN」に変更しております。

2. jsonから緯度経度を取り出す

次に、json.txt から緯度経度を取り出します。(いずれログを残したいので、必要な情報だけ取り出しています。)

上記コードの下に下記を付け足します。

    $place = array($data["payload"]["channels"][0]["value"]);
    array_push($place,$data["payload"]["channels"][1]["value"]);
    array_push($place,$data["datetime"]);
    
    file_put_contents("gps.txt", implode(",",$place) );

ここでも、エラー処理など皆無で、txtファイルも事前に作成しております。

gps.txt には5秒ごとに下記が出力されています。

43.NN4423,141.NN972,2016-06-16T02:13:25.408958456Z

 

3. 地図上でリアルタイムトラッキング

前回行った OpenStreetMap & OpenLayers3 で GPSのログから地図に経路の表示 に、リアルタイムトラッキングを実装します。

ここも、辿り着くまでさんざん苦労したのですが、、、OpenLayers のコードの最後に下記を追加します。

    // リアルタイムアイコン
    var marker;
    var genzaiti;
    
    // 位置を配列に入れる
    function getPlace() {
        genzaiti = new Array();
        var source = req.responseText;
        genzaiti = source.split(",");
        // 
        document.getElementById('mapTestText').innerHTML = genzaiti[0] + ' ' + genzaiti[1];
    }
    
    // マーカー作成
    function makeMarkerOverlay(imgSrc, coordinate) {
        var imgElement = document.createElement('img');
        imgElement.setAttribute('src', imgSrc);
        var markerOverlay = new ol.Overlay({
            element: imgElement,
            position: coordinate,
            positioning: 'bottom-center'
        });
        return markerOverlay;
    }

    // マーカーに画像セット
    marker = makeMarkerOverlay('/wp/wp-content/uploads/icon-sister-150x150.png');
    // 地図(map)に、マーカーをオーバーレイする
    map.addOverlay(marker);

    // gps.txt からデータを取得し、マーカーの位置をセットする
    function gpsTracking() {
        req = new XMLHttpRequest(); 
        req.open("GET", "/sakura/gps.txt", true);
        req.send(null); 
        req.onload = function(){
            getPlace();
            marker.setPosition( ol.proj.fromLonLat([ Number(genzaiti[1]) , Number(genzaiti[0]) ]) );
        };
    };
    
    gpsTracking();
    // 5秒ごとに繰り返す
    setInterval('gpsTracking()',5000);

ここでも、エラー処理などは・・・(以下略・・・

4. 試運転

本日お父ちゃんが出勤中に試運転をしております!

5秒おきなので、30倍に早送りしております。

 

動いてる〜!長女ちゃんアイコンが動いてます。第一段階としては、まずはここまで〜!

(因みにBGMは昔私が作った曲です〜

課題

  • 自動的に長女ちゃんアイコンが真ん中に来るようにする(地図の自動追従)
  • 緯度経度の取得が不安定なので改善(php側?)
  • 地図を移動させた時、アイコンも一緒に動いてしまう

追記:その後の進捗