owntracksをセルフホスト

Dawarichでトライ

 この記事を見て、そのとおりdockerでやってみた。
qiita.com

もしリバースプロキシ or Cloudflare Tunnelなどで外部公開する場合、ホスト名を明示的に許可しないと接続を拒否されます。

 と書いてあったが、普通のポート開放では不要なのかと思ってスルーしてたら引っかかってしまった。

 で、動くようになったんだが、不満点は2つ。

  • クライアントのタイムゾーンがGMT

x.com
x.com
 設定をいろいろみてもそれらしき項目はなし。原点に戻ってdock-composer.ymlを見たらタイム設定があった。 x.com

  • 動作が遅い

 なにがボトルネックかわからんので、対応しようがない。

対策

 データは家でしか見ないので、ローカルファイルでええんちゃうか。だとしたらgpxファイルにしてgpspruneで見たらええんちゃうか。
 ということで、webサーバでデータを受けて、1日1回データをgpxに書き出せばええやん。

できたもの

 公式サイトにphp+mysqlのスクリプトサンプルがある。
owntracks.org

 これをsqliteで書き換えてみた。シンプルそうなので興味があった。RDBを扱うのは初めて。できあいのスクリプトもあるけど、多機能すぎた(friend機能とは不要)のでパス。

<?php
header("Content-type: application/json");
$payload = file_get_contents("php://input");
$logfile = __DIR__ . "/log/" . date("YmdHis") . ".json";
file_put_contents($logfile,$payload);
$data =  @json_decode($payload, true);
$db = new SQLite3('../../database/tracks.sqlite');
$db->exec('CREATE TABLE IF NOT EXISTS entries(time INTEGER,tid TEXT,lat REAL,lon REAL,alt INTEGER, posted INTEGER,received INTEGER)');

if ($data['_type'] == 'location') {
	$stmt = $db->prepare('INSERT INTO entries VALUES(:time,:tid,:lat,:lon,:alt,:posted,:received)');
	$stmt->bindValue(':time',$data['tst'],SQLITE3_INTEGER);
	$stmt->bindValue(':tid',$data['tid'],SQLITE3_TEXT);
	$stmt->bindValue(':lat',$data['lat'],SQLITE3_FLOAT);
	$stmt->bindValue(':lon',$data['lon'],SQLITE3_FLOAT);
	$stmt->bindValue(':alt',$data['alt'],SQLITE3_INTEGER);
	$stmt->bindValue(':posted',$data['created_at'],SQLITE3_INTEGER);
 	$stmt->bindValue(':received',time(),SQLITE3_INTEGER);
  
	$result = $stmt->execute();
	$stmt->close();

	$response = array();
	print json_encode($response);
}else{
	echo "This is not location\n";
}
?>

 データを見ると、測位時刻、送信時刻を持っていたので、さらに受信時刻も加えてみた。これをBASIC認証を掛けたディレクトリに置いて、owntracksアプリを設定。
 これをgpxに書き換えるスクリプト。引数なしで実行すると全日の記録を、引数があるとその日(strtotime関数に依存)のデータを書き出す。

<?php
error_reporting(E_ALL);
chdir(__DIR__);

if(isset($argv[1])){
	if(!($epoc_yesterday = strtotime($argv[1]))){
		die("format error! : $argv[1]\n");
	}
}else{
	$epoc_yesterday = strtotime("yesterday");
}
$epoc_today = $epoc_yesterday + 60*60*24;


$db = new SQLite3('/home/kazz/web/default/database/tracks.sqlite');
$result_list = $db->query("SELECT * FROM entries WHERE tid = 'AS' AND posted >= $epoc_yesterday AND  posted < $epoc_today");

$template_track = file_get_contents("trkpt.txt");
$array_template = array("%%lat%%","%%lon%%","%%alt%%","%%time%%");
$data_gpx = "";
while ($row = $result_list->fetchArray(SQLITE3_ASSOC)){
	$time_gpx = str_replace("+00:00","Z",gmdate(DATE_ATOM,$row['posted']));
	$array_data = array($row['lat'],$row['lon'],$row['alt'],$time_gpx);
	$data_gpx .= str_replace($array_template,$array_data,$template_track);
}
if($data_gpx){
	$body = str_replace(array("%%time%%","%%data%%\n"),array($time_gpx,$data_gpx),file_get_contents("gpx.txt"));
	file_put_contents("gpx/" . date("Ymd",$epoc_yesterday).".gpx",$body);
}else{
	die("No data\n");
}
?>

 これをcronで深夜に実行。
 できてる。

 あとは古いデータを削除するスクリプトを仕込むことになるけど、データが溜まってからやる。しかしこの程度ならcsvファイルでも十分行けたかもしれない。なんならこの状態でログとして保存しているJSONファイルでもいける。