
江端智一のホームページ
どなたか、良いアイデアを頂けませんか?
(1)最初にhttp://192.168.0.19(ローカル) でアクセスしたWebサーバのコンテンツに記載された"*.kobore.net"のIPアドレスは、無条件で"192.168.0.19"に変換されて、自分のサーバにループしてアクセスさせるようにする方法です。
(2)http://kobore.net でアクセスした場合は、従来通りの反応をする
「kobore.netや、wp.kobore.netが、事故でクラッシュした」時でも運用が続けられるように、先日、Raspberry Pi 4のセット一式を購入しました。
これです。
説明書が不親切なので、組立方の手順を簡単に残しておきます。
(1)基盤を一番下のプラ板に設置
(2)基盤に放熱版(3つを設置) # 虫めがねとピンセットがないと辛いかも
(3)ファンを設置する(方向は、#(多分)コードがプラ版につく方向だと思う

経験的に、ラズパイをサーバとして使うと、夏場に熱でダウンすることは知っていますので、今回は「冷却(といっても空冷だけど)」に投資しています。
私、通常はubuntuなのですが、今回は、SDカードにプレインストールされていたOSをそのまま使うことにしました(SDカード焼き込みとか、面倒なので)
ちなみに、HDMIのインターフェースが、Pi3から変っています(MiniHDMIになっている)ので、こんなのも追加で購入しました。
取り敢えず、部屋の中のWiFiに繋いで、apt-get install, update, upgradeを実施中です。
======
GUIから、SSHサーバの設定ができる


これだけ。これでTeraTerm IPアドレス(192.168.0.19)、ポート22 でアクセスできるようになりました。
さてWebサーバは、apache と nginx の2択がありますが、今回はnginxを使うことにしました。理由は、最近、これしか使っていないからです。その他、色々メリットもあるようです。
さて、次はngnixの構築だけど、ここで(私は)初めて、ufwなるもの設定する必要が出てきたので、メモを残しておきます(なんでもiptablesのコマンドライン設定ツールだとか)。
$ sudo apt-get install ufw
は、さくっとできたのですが、状態を調べたら
$ sudo ufw status
ERROR: Couldn't determine iptables version
てなものが出てきます。sudo rebootで、
$ sudo ufw status
Status: inactive
となりました。
では、全アクセスを禁止した上で、sshポート(22)だけをオープンしておきます。
$ sudo ufw default deny
Default incoming policy changed to 'deny'
(be sure to update your rules accordingly)
$ sudo ufw allow 22
Rules updated
Rules updated (v6)
次に、ufwを有効化します。
$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
再度状態を確認します
$sudo ufw status
Status: activeTo Action From
-- ------ ----
22 ALLOW Anywhere
22 (v6) ALLOW Anywhere (v6)
とりあえず、web用に80と443をオープンしておきます。
$ sudo ufw allow 80
Rule added
Rule added (v6)
$ sudo ufw allow 443
Rule added
Rule added (v6)
$ sudo ufw status
Status: activeTo Action From
-- ------ ----
22 ALLOW Anywhere
80 ALLOW Anywhere
443 ALLOW Anywhere
22 (v6) ALLOW Anywhere (v6)
80 (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
と、こんなところで準備完了かな?
ためしに、http://192.168.0.19をやってみたところ、成功しました。

https://192.168.0.19 は失敗しますね。

当然ですね。まだ、秘密鍵も仕込んでいませんし。まあ、とりあえずこちらは後回しにして、kobore.netからコンテンツのフルダウンロードをします。
sshを使ってシェルにログインした後(詳細は「ホームページのフルバックアップ」を参照
$cd /home/kobore
$cp -r www www2 // Don't touch "www" itself
$tar -czvf www2.tgz www2
$mv www2.tgz www.kobore.net.20210713.tgz
$rm -r www2 // Never delete directory "www"
でもって、
"www.kobore.net.20210713.tgz" をローカルにダウンロードする
そんでもって、これをさらに、teratermの「ファイル(F)→SSH SPC...」でもってラズパイに送り込みます。
ちなみにkoboreというアカントは予め作っておいて、そちらにSSHログインしておきます。

ちなみに、kobore.net → ローカルPCのダウンロードに4時間、さらにラズパイへの転送に4時間かかりました。
でもって、
tar zxvf www.kobore.net.20210731.tgz
で解凍します。

$mv www2 www
として、とりあえずコンテンツの配置は完了。さて、次は、ngnixに、この、/home/kobore/wwwをリンクさせます。
/etc/nginx/sites-available/default の、ここだけを変更

これで変更できて、http:/192.168.0.19 で表示を確認しましたが・・・
ここで「どエラい見落し」を見付けました。
ラズパイの中の、/home/kobore/wwwに記載された、"kobore.net"は、全部、本物のサーバに向かってしまい、ラズパイの中のコンテンツには向かない
考えてみれば当然です。これでは、ラズパイの中にindex.shtmlのリンクを張っただけのことで、サーバとしては全く役に立ちません。
江端家ネットワーク内に、DNSサーバを立てることもできるけど、そうしてしまったら、本家の、kobore.netにアクセスできなくなるし、"*.kobore.net"→"192.168.0.19"と強制的に書き換えすることもできるけど、かなり面倒な作業になるし、美しくない。
ラズパイの中のみで作業をする、という手もあるけど、江端家ネットワーク内のPCから、こちらのサーバも使いたい(特に、ローカルなWordPressに、守秘情報を保存したい)
DNSサーバに、"*.kobore.net"→"*.local.kobore.netとして、"192.168.0.19"と書き込むか? 先ずできるとは思えないし、モラルとしても論外だろうなぁ。
という訳で、
どなたか、良いアイデアを頂けませんか?
(1)最初にhttp://192.168.0.19(ローカル) でアクセスしたWebサーバのコンテンツに記載された"*.kobore.net"のIPアドレスは、無条件で"192.168.0.19"に変換されて、自分のサーバにループしてアクセスさせるようにする方法です。
(2)http://kobore.net でアクセスした場合は、従来通りの反応をする
ホスト名 http://raspberrypi/ でアクセスできることを確認した。ならば、ホスト名を変えみようか。"koborepi", "tech_koborepi", "wp_koborepi" とか複数の名前を設置できるか?試してみよう
(参考文献)
AmazonJSをインストールしたら、新規投稿画面に、こんなものが出てくるようになった。

さて、これに手を入れるべきか否か? 自動更新に任せて暫く放置するか。
色々手を入れて、wordpressが立ち上がらなくなった時は青冷めたしなぁ。
とりあえずターゲットのコードだけ書き出しておきます。
% pwd
/home/kobore/www/wp/wp/wp-content/plugins/amazonjs/js
% more tinymce-plugin.js
(function() {
tinymce.PluginManager.add( 'amazonjs', function( editor, url ) {
editor.addButton('amazonjs', {
title: amazonjsAdmin.mce.buttonTitle,
image: url + '/../images/amazon-icon.png',
onclick: function () {
editor.windowManager.open({
title: amazonjsAdmin.mce.dialogTitle,
url: amazonjsAdmin.mce.dialogUrl,
width: $(window).width() * 0.9,
height: $(window).height() * 0.9,
id: 'amazonjs-insert-dialog'
});
}
});
});
})();
%
どうも原因が見あたらないのですが、
https://postgresweb.com/err-wordpress-failed-to-load-plugin-url
を見て、思い当たることがありあした。私も、chromoに"uBlock origin"という広告表示ブロックプラグインを入れていました。
これを一時外してみたら、メッセージが消えて、

が出てきました。まだ、リンクには失敗していますが、とりあえず第一の問題点は越えたか、と。
私は普段、PostgreSQLを使っています。主な理由は、PostGISを使いたいからですが、サーバとして立ち上げる必要があるので、そこそこ面倒くさいです。
1つのアプリケーションだけで使えれば良い、と割り切るのであれば、SQLite3が便利だと聞きましたので、golangでちょっと使ってみました。
アプリで使えて、SQL文が使えるようにするために、SQLite3をインストールしてみました。忘れないうちに、手順を記録しておきます。

で、

をダウンロードして解凍して、c:\sqlite3を作って、ここの中に放り込む。

で、あとは、c:\sqlite3にパスを通しておく。

これで環境構築は完了です。
あとは、csvファイルからDBを作ってみます。
まず適当なディレクリを掘ります。

https://people.sc.fsu.edu/~jburkardt/data/csv/csv.htmlあたりから、サンプルのcsvファイル("biostats.csv","cities.csv"あたり)をダウンロードして、ちょっと加工しました(空白とか、ダブルコーテーションを削除)。
をクリックしてダウンロードする。

これで、このデイレクトリの中に、test_dbというデータベースファイルができます。
この中には、2つのデータベース、 biostats, cities が入っています。
これらのDBは、原則としてSQL文が使える(みたい)です。
"sqlite> .exit" で、コマンドプロンプトに戻れます。

で、もって、 "sqlite3 test_db" でDBをSQL文で使えるようになります。

あと、日本語を使えるようにする問題が、未解決ですが、まあなんとかなるでしょう。
取り敢えず、今日のところは、ここまで。
以上
私が普段使っているDBは、PostgreSQLを使っているのですが、構築手順が面倒くさいし、なによりDBサーバ立てるのを省略したかったです。
ですので、SQLiteを使ってDB構築の手を抜くことにしました。
今日のところは、mattn/go-sqlite3 に _example/simple/simple.go というサンプルファイルがあったので、これを動くことを確認するところまでやりました。
/*
C:\Users\ebata\kese\gonet-html\1-1
go get github.com/mattn/go-sqlite3
どういう訳か、VSCodeのデバッグでトレースができなかったので、fmt.Println("----->10") と入れて確認をしている。
このプログラムは、
*/
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "github.com/mattn/go-sqlite3"
)
func main() {
os.Remove("./foo.db")
fmt.Println("----->10")
db, err := sql.Open("sqlite3", "./foo.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
sqlStmt := `
create table foo (id integer not null primary key, name text);
delete from foo;
`
_, err = db.Exec(sqlStmt)
if err != nil {
log.Printf("%q: %s\n", err, sqlStmt)
return
}
fmt.Println("----->9")
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
fmt.Println("----->8")
stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
for i := 0; i < 100; i++ {
_, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i))
if err != nil {
log.Fatal(err)
}
}
tx.Commit()
fmt.Println("----->7")
rows, err := db.Query("select id, name from foo")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
err = rows.Scan(&id, &name)
if err != nil {
log.Fatal(err)
}
fmt.Println(id, name)
}
fmt.Println("----->6")
err = rows.Err()
if err != nil {
log.Fatal(err)
}
fmt.Println("----->5")
stmt, err = db.Prepare("select name from foo where id = ?")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
var name string
err = stmt.QueryRow("3").Scan(&name)
if err != nil {
log.Fatal(err)
}
fmt.Println(name)
fmt.Println("----->4")
_, err = db.Exec("delete from foo")
if err != nil {
log.Fatal(err)
}
fmt.Println("----->3")
_, err = db.Exec("insert into foo(id, name) values(1, 'foo'), (2, 'bar'), (3, 'baz')")
if err != nil {
log.Fatal(err)
}
fmt.Println("----->2")
rows, err = db.Query("select id, name from foo")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
err = rows.Scan(&id, &name)
if err != nil {
log.Fatal(err)
}
fmt.Println(id, name)
}
fmt.Println("----->1")
err = rows.Err()
if err != nil {
log.Fatal(err)
}
fmt.Println("----->0")
}
もう一つのサンプルコード
https://qiita.com/geniusmaaakun/items/b3cb44de3b10a526be98 を参照させて頂き、微小修正
package main
/*
C:\Users\ebata\kese\gonet-html\1-1-1
sqliteをインストールする際の手順の確認
# ドライバのインストール
go get github.com/mattn/go-sqlite3
*/
import (
"database/sql"
"fmt"
"log"
"os"
//インポート _にしないとコンパイルエラーになる。使用しない為
_ "github.com/mattn/go-sqlite3"
)
var DbConnection *sql.DB
type Person struct {
Name string
Age int
}
func main() {
os.Remove("./example.sql") // これを入れないと、DBが太り続ける
//1
//DBを開く なければ作成される
DbConnection, _ := sql.Open("sqlite3", "./example.sql")
//終わったら閉じる
defer DbConnection.Close()
//DB作成 SQLコマンド
cmd := `CREATE TABLE IF NOT EXISTS person(
name STRING,
age INT)`
//実行 結果は返ってこない為、_にする
_, err := DbConnection.Exec(cmd)
//エラーハンドリング
if err != nil {
fmt.Println("エラー")
log.Fatalln(err)
}
//ターミナル
//sqlite3
//.table
//SELECT * FROM tablename;
//で確認
//2 CRUD処理
//Create
//データを追加
//VALUES (?, ?) 値は後で渡す。セキュリテイの関係でこのようにする方がいい
//SQLインジェクション 悪意あるコマンドでDBが操作されてしまうのを防ぐ ?でエスケープしてくれる
cmd = "INSERT INTO person (name, age) VALUES (?, ?)"
//実行
//レコードを取得する必要のない、クエリはExecメソッドを使う
//第二引数からは、コマンド?部の値
_, err = DbConnection.Exec(cmd, "Nancy", 20)
if err != nil {
log.Fatalln(err)
}
_, err = DbConnection.Exec(cmd, "Mike", 20)
if err != nil {
log.Fatalln(err)
}
_, err = DbConnection.Exec(cmd, "Tom", 55)
if err != nil {
log.Fatalln(err)
}
//Update
//データの更新 Mike が存在する場合
cmd = "UPDATE person SET age = ? WHERE name = ?"
_, err = DbConnection.Exec(cmd, 25, "Mike")
if err != nil {
log.Fatalln(err)
}
//Read
//Get All
//マルチセレクト
//データ全てをループで表示
//Queryは全て取得する
cmd = "SELECT * FROM person"
rows, _ := DbConnection.Query(cmd)
defer rows.Close()
//structを作成
var pp []Person
//取得したデータをループでスライスに追加 for rows.Next()
for rows.Next() {
var p Person
//scan データ追加
err := rows.Scan(&p.Name, &p.Age)
if err != nil {
log.Println(err)
}
pp = append(pp, p)
}
err = rows.Err()
if err != nil {
log.Fatalln(err)
}
//表示
for _, p := range pp {
fmt.Println(p.Name, p.Age)
}
//特定のデータを取得
cmd = "SELECT * FROM person where age = ?"
//age = 20にして実行
//QueryRowは最初の一件だけ取得する。
//row := DbConnection.QueryRow(cmd, 20)
//row := DbConnection.QueryRow(cmd, 25)
row := DbConnection.QueryRow(cmd, 55)
var p Person
err = row.Scan(&p.Name, &p.Age)
if err != nil {
//データがなかったら
if err == sql.ErrNoRows {
log.Println("No row")
//それ以外のエラー
} else {
log.Println(err)
}
}
fmt.Println(p.Name, p.Age)
//Delete
//データの削除 全て
cmd = "DELETE FROM person WHERE name = ?"
_, err = DbConnection.Exec(cmd, "Nancy")
if err != nil {
log.Fatalln(err)
}
//マルチセレクト テーブルもSQLインジェクション対策
//こちらを推奨
//データを構造体に入れて表示
//テーブルはSQLインジェクション対策が使えない
tableName := "person"
//テーブル名指定は?が使えない為、%sを使う。
//後に対応されるかも?
cmd = fmt.Sprintf("SELECT * FROM %s", tableName)
rows1, _ := DbConnection.Query(cmd)
defer rows1.Close()
var pp1 []Person
//パターンとして覚える
for rows1.Next() {
var p Person
//データを構造体に追加
err := rows1.Scan(&p.Name, &p.Age)
if err != nil {
log.Println(err)
}
//ppに追加
pp1 = append(pp1, p)
}
//まとめてエラーチェック
err = rows1.Err()
if err != nil {
//エラーなら終了
log.Fatalln(err)
}
for _, p := range pp1 {
fmt.Println(p.Name, p.Age)
}
}
背景は、
に記載した通りなのですが、家族から「警告音がうるさい!」と文句を言われて、システムを停止させております。
そこで、添付ファイルのようなものを実現したいと考えています。ただ、これを実現する回路図や、または、製品を見つけられずにおります。

追記:読者の方から、「遅れ消灯スイッチ」
https://www2.panasonic.biz/ls/
を勧めて頂きました。しかし、私の環境では、階段に設置する3つのスイッチ(3路、4路スイッチ)を使うもの(私が、説明していませんでした)で、このデバイスは使えないと思います。

追記:さらに読者の方から「あけたらタイマ」の利用を教えて貰いました。
https://www2.panasonic.biz/ls/densetsu/haisen/switch_concent/cosmo_wide21/lineup/switch/timer/
『このスイッチ(親機、WTC5332W)はタイマー機能のほか、遅れ消灯機能もあります。これと、子機WT5652を3路の位置、Wt5654を4路の位置に使用すれば、いずれのスイッチからも遅れ消灯が可能かと思われます。』
仕様書を読んでみたら、ドンピシャと思われる記載がありました。

なるほど、これならできる(かもしれない)。

合計金額が、7900円 + 2600円 + 2900円 = 13000円 になりますね。
これでちゃんと動くのであれば、この出資には意味がありそうです。しかも、居留守対策もできそうです。
というか、この装置を買って、分解して、リバースエンジニアリングしてみてもいいな、と思っています。
ちなみに、居留守対策装置は、実家でも稼動中です。
keyword WTC 5331 WK panasonic あけたらタイマ 実家
先程、玄関(外と中)の電灯も、自動的に点灯/消灯する装置を設置しました。

18:30 ± 30分 に点灯し、 23:30 ± 30分 に消灯するようにセットしておきました。
今、実験しています。
基本的には、私が帰省する時に設定しなおせば良いと思っていますが、
この設定、多分ラジカセより簡単だと思いますので、設定を試みて頂けると助かります。
(ちなみにラジオの方が動いているのは、内蔵の電池が停電中も電力を供給し続けているからです)
マニュアルをここに置いておきました(ちなみに館の2Fのテーブルの上にも、分かるように置いてあります)
http://kobore.net/genkan.pdf
が、マニュアルなんぞ読むのは面倒でしょうから、
まず、

でボタンを確認して、

の赤枠でかこった部分をまねて操作して下さい。
万一設定に失敗しても、問題は発生しませんので、御安心下さい。
智一
停電後に、このスイッチが動かなくなった時の普及の方法は簡単です。
玄関のタイマー直しておきました。
写真の赤色のボタンを押して時刻(2400表記)を直すだけで、あとは何もしなくても大丈夫のようです(設定ボタンなどの操作不要)。

githubの中のファイルを直接編集したいだけなのに、色々調べたけど、余計な説明が多すぎて困っています。

上記の2つ(GitHub Repositories, GitHub Pull Request and Issues)がインストールされていることが前提です。
以下の操作をやるとできるようになるようです。


ブラウザに次の画面が次の画面が立ち上がってきます。


VSCodeの方に以下のメッセージが出てくるので「許可」を押下して下さい。

VSCodeにリポジトリの内容が表示されます。

編集するファイルを選んで、変更します。"test5"と記載して、ファイルをセーブして、以下の手続を行います。

コミットのメッセージを記載する。

以下のボタンを押して下さい。

Webで、反映されているのが確認できます。

本来は、ブランチとかマージとか、いろいろできるのでしょうが、とりあえず、iPad用の編集用に使えれば足りるので、これでいいのです。
https://vscode.dev で、編集もできそうだ、ということも確認しました。
ーーーー
コミットができなかったりするので、できたケースをもう一つ

変更してセーブすると「M」が表示されるので、以下の手順でコミットを進める。


の手順でコミットができるようです。
反映されています。

<論説の4要素>
(1)解説 (物事がどのようにして、なぜ起こるのかを説明する)→現在形
(2)叙述 (何が起ったのか述べる)→過去形
(3)描写 (図表によるイメージを与える)→現在形
(4)論拠 (理由を挙げることによって納得させる)→現在形
■アブストラクト(要約)は通常現在形で書く→(1)解説
■サマリー(概要)は通常過去形で書く→(2)叙述
■過去の研究(例えば参考文献にある)は通常過去形で書く→(2)叙述
■恒久施設は現在形で書く→(1)解説
■特定の研究の為の実験手順および装置は過去形で書く→(2)叙述
■図表に示された結果は現在形で書く→(3)描写
■研究した試薬品、資料等の挙動は過去形で書く→(2)叙述
■結論部分では、結論そのものを除いては通常過去形で書く
■導かれた結論(演繹)は、研究調査の条件に関係なく「一般真理」であるので、現在形で書く→(1)解説
■仮定法は、書き手が「強い疑いを暗示したい場合」のみに使用する
(出典:NASAに学ぶ英語論文・レポートの書き方)
Pythonのスクレイピングを、Golangで記述してみる。
まずpythonの記述です。
# -*- coding: utf-8 -*-
from pyquery import PyQuery
q = PyQuery('https://kabutan.jp/stock/?code=7203')
sector = q.find('#stockinfo_i2 > div > a')[0].text
print(sector)
これでターゲットとなるhtml部分は、多分ここ
<div id="stockinfo_i2">
<dl>
<dt>業績</dt>
<dd><img src="/images/cmn/gyouseki_2.gif" title="今期予想" /></dd>
</dl>
<div>
<a href="/themes/?industry=17&market=1">輸送用機器</a>
</div>
これをgolangで実現するには、このようにコーディングするようです。
package main
import (
"fmt"
"github.com/PuerkitoBio/goquery"
)
func main() {
//get_url_info, err := goquery.NewDocument("https://profile.yahoo.co.jp/search/?w=トヨタ自動車")
get_url_info, err := goquery.NewDocument("https://kabutan.jp/stock/?code=7203")
if err != nil {
fmt.Println("get html NG")
}
//result := get_url_info.Find("div > div > table > tbody > tr > td")
//result := get_url_info.Find("div > div > a")
result := get_url_info.Find("#stockinfo_i2 > div > a")
result.Each(func(index int, s *goquery.Selection) {
fmt.Println(s.Text())
})
}
出力結果は以下の通りです。
ebata@DESKTOP-P6KREM0 MINGW64 ~/kese/gonet-html
$ go run main3.go
輸送用機器
こうやると、もっと簡単にできそう。
package main
import (
"fmt"
"github.com/PuerkitoBio/goquery"
)
func main() {
q, err := goquery.NewDocument("https://kabutan.jp/stock/?code=7203")
if err != nil {
fmt.Println("get html NG")
}
name := q.Find("div.company_block > h3").Text()
fmt.Println(name)
code_short_name := q.Find("#stockinfo_i1 > div.si_i1_1 > h2").Text()
fmt.Println(code_short_name)
market := q.Find("span.market").Text()
fmt.Println(market)
unit_str := q.Find("#kobetsu_left > table:nth-child(4) > tbody > tr:nth-child(6) > td").Text()
fmt.Println(unit_str)
sector := q.Find("#stockinfo_i2 > div > a").Text()
fmt.Println(sector)
}