OmniAICreator/Qiita-1.07M huggingface dataset:
[JQuery] Sortable

オブジェクトの順番をイントラクティブに操作させてくえるjQuery UIに含まれるSortableの紹介。　

Sortableのページ
https://api.jqueryui.com/sortable/

APIの詳細
https://api.jqueryui.com/sortable/

```html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQuery UI Sortable - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
  <link rel="stylesheet" href="/resources/demos/style.css">
  <style>
  #sortable { list-style-type: none; margin: 0; padding: 0; width: 60%; }
  #sortable li { margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; font-size: 1.4em; height: 18px; }
  #sortable li span { position: absolute; margin-left: -1.3em; }
  </style>
  <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  <script>
  $( function() {
    $( "#sortable" ).sortable();
    $( "#sortable" ).disableSelection();
  } );
  </script>
</head>
<body>
 
<ul id="sortable">
  <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1</li>
  <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2</li>
  <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3</li>
  <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 4</li>
  <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 5</li>
  <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 6</li>
  <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 7</li>
</ul>
 
 
</body>
</html>
```

順番を変更したら、新しいポジションを知りたい。そんな時は。

```html
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  <script>
    $(function () {
      $("#sortable").sortable({
        start: function (event, ui) {
          ui.item.startPos = ui.item.index();
        },
        stop: function (event, ui) {
          alert("Moved from " + ui.item.startPos + " to " + ui.item.index());
        }
      });
    $("#sortable").disableSelection();
  });
</script>
```
M5StackのWiFIクライアント設定をSDカードから読み込んで利用する

# 概要
　M5StackのWi-Fi設定（SSIDとPASS）をソースコードに埋め込むと次のような手間があります。

- M5Stackを利用する場所やWi-FI設定が変わるたびに、ファームウェアを書き替える手間が発生する
- ソースコードを共有する際に、Wi-FI設定を隠す手間がある

このような「手間」を省く手段を考え、SDカードから読み込むプログラムを作成しました。

　ソースコードは[Github](https://github.com/kmaepu/M5Stack_WiFi_SD)で公開しています。
ライセンスはフリーです。使用する際は自己責任でお願いします。
　このソースコードを動かすと、SDカードからWi-Fi設定を読み込んで接続します。M5StackのディスプレイにはSSIDとPASS、IPが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/fe8d15b0-18c1-c26b-acff-46e79057b840.png)


# 使い方
　1. Githubからソースコードを入手しファームウェアを書き込み
　2. SDカードに「wifi.csv」ファイルを作成する
　3. wifi.csvファイルに次のようなフォーマットでSSIDとPASSを記述


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/787161ab-13b6-e5c5-2b76-a419633f6314.png)

　4. SDカードをM5Stackにセットして再起動
　

# ソースコード

```c++:M5Stack_WiFi_SD.ino

#include <M5Stack.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <ESPmDNS.h>
#include <Preferences.h>
#include <string.h>

const char* fname = "/wifi.csv";
File fp;
char ssid[32];
char pass[32];

void SetwifiSD(const char *file){
  unsigned int cnt = 0;
  char data[64];
  char *str;
  
  fp = SD.open(fname, FILE_READ);
  while(fp.available()){
    data[cnt++] = fp.read();
  }
  strtok(data,",");
  str = strtok(NULL,"\r");    // CR
  strncpy(&ssid[0], str, strlen(str));
 
  strtok(NULL,",");
  str = strtok(NULL,"\r");    // CR
  strncpy(&pass[0], str, strlen(str));

  M5.Lcd.printf("WIFI-SSID: %s\n",ssid);
  M5.Lcd.printf("WIFI-PASS: %s\n",pass);
  M5.Lcd.println("Connecting...");

  Serial.printf("SSID = %s\n",ssid);
  Serial.printf("PASS = %s\n",pass);
  
  // STA設定
  WiFi.mode(WIFI_STA);     // STAモードで動作
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }

  M5.Lcd.print("IP: ");
  M5.Lcd.println(WiFi.localIP());
  Serial.printf("IP: ");
  Serial.println(WiFi.localIP());
  fp.close();
}

void setup() {
  M5.begin();
  M5.Lcd.setTextSize(2);

  SetwifiSD(fname); // Get ssid
}

void loop() {
  // put your main code here, to run repeatedly:
}
```

#おわりに
読んでいただいた皆様のM5Stackライフがより便利になると嬉しいです（^^）
初めての簡単な Alexa スキル開発でハマったこと

## はじめに

先日、社内のハッカソンイベントで簡単な Alexa スキル開発を経験しました。その際に少しハマったポイントを備忘も兼ねてまとめておきます。

## 前提

* Alexa スキル開発は初めて
* [ASK CLI](https://developer.amazon.com/ja/docs/smapi/ask-cli-command-reference.html) を使ってローカルでデプロイしながら開発
* ランタイムは nodejs で開発

## ハマったポイント 

### ベータテストはスキルを審査に出すと使えなくなる

これは実際の Amazon Echo のような実機テストをしようとした際に少しハマりました。スキルの審査を提出してしまうと、スキル検証がパスしていてもスキルのベータテストが利用できなくなります。

審査はベータテストも完了してから実施せよということなのかなと思います。

### Alexa Developer Console の GUI でスキル設定を変更した後はローカルファイルにも反映が必要

ask cli を使ってローカル環境で開発しているときに、Alexa Developer Console 上で直接スキル設定やインテントを変えてから再度 `ask deploy --force` という感じでデプロイすると、当然ですが Alexa Developer Console 上で加えた反映がクリアされてしまいます。

具体的には以下ファイルが更新対象でした。

* インテント設定： ロケールファイル（`ja-JP.json`など）
* 公開用情報：`skill.json `

とはいえ、Alexa Developer Console 上で編集した情報がどうやって json ファイルに反映されるかがわからなかったので、その場合は以下のような手順で更新していました。

* `ask clone` で Alexa Developer Console 上にデプロイされた状態のリポジトリをクローン
* クローンしたフォルダ(`_` というようなフォルダ) の中の `skill.json` と `ja-JP.json` をもともとの `skill.json` と `ja-JP.json` にコピペ

### Alexa Developer Console の コードエディタは Alexa-hosted スキルでしか使えない

今回僕は `ask new` からカスタムスキルを作る形で開発したのですが、そうすると Alexa Developer Console 上のコードエディタが使えませんでした。Alexa Developer Console 上のコードエディタは、Alexa-hosted スキルであれば利用できるとのこと。

軽微な修正であれば Alexa Developer Console 上でできたら便利だなとは思っていたのですが、コードエディタが使えなかったため、結局開発はローカルで行い、`ask deploy` で反映するという形で進めていました。

後々調べたところ、 ASK CLI でも Alexa-hosted スキルを使いたい場合は、`ask create-hosted-skill` を使えば良いようです。（参考：https://developer.amazon.com/ja/docs/hosted-skills/build-a-skill-end-to-end-using-an-alexa-hosted-skill.html#askcli）

## おわりに

色々手探りで Alexa スキルに取り組んだのですが、難しくもあり楽しくもありという感じでした。公式ドキュメントや動画が割と充実していたため、初見でも地道に調べながら開発は進められるのかなと思いました。

## 参考

* [Rails開発者に贈る、Alexa開発事始め · aguuu.com](https://aguuu.com/archives/2019/10/alexa-basic-development/)
* [Alexa Skills Kitによるスキルの作成 | Alexa Skills Kit](https://developer.amazon.com/ja/docs/ask-overviews/build-skills-with-the-alexa-skills-kit.html)
erbをhamlに変えよう

ryoです。AP午後の自己採点が５８点、部分点次第で合格ワンチャンなせいで気持ち悪い２ヶ月になりそうです。
昨日は新しくアプリケーションを立ち上げるセットを覚えました。
通っているスクールではhtmlはhamlを使って記述しています。
しかし、昨日scaffoldで出来上がったビューファイルはerbです。
そこで今日は、erbからhamlに自動変換をするコマンドを使います。

## この記事で出来る様になること
ビューファイルをerbからhamlに変換する。

## 目次
1. コマンドと記述一覧
2. 各コマンドと記述の説明
3. よく聞くエラーとか


## コマンドと記述一覧
gemをインストールするため、Gemfileに下記を記述します。

```ruby:Gemfile
gem 'haml-rails'
gem 'erb2haml'
```

ターミナルコマンドは下記だけです。

```ruby:ターミナル
bundle install
rake haml:replace_erbs
```

これだけです。手抜きな感じです。

## 各コマンドと記述の解説

Gemfileです。
haml-railsがhamlを使える様にするGemです。
erb2hamlが変換できる様にするGemです。

＊erb2hamlは、haml-railsに備わっているため、不要であるとの話も聞いたことあります。
俺は実際に確かめていないです。
むしろ誰か試してみて、結果がわかったら教えてください。

```ruby:Gemfile
gem 'haml-rails'
gem 'erb2haml'　
```


上記二つをインストールします。

```ruby:ターミナル
bundle install
```

これが、ergを全てhamlに変換するコマンドです。

```ruby:ターミナル
rake haml:replace_erbs
```

出力結果は下記のようになります。

```ruby:出力
Converting: app/views/users/registrations/edit.html.erb... Done!
Removing: app/views/users/registrations/edit.html.erb... Removed!
Converting: app/views/users/registrations/new.html.erb... Done!
Removing: app/views/users/registrations/new.html.erb... Removed!
Converting: app/views/users/sessions/new.html.erb... Done!
Removing: app/views/users/sessions/new.html.erb... Removed!
Converting: app/views/users/shared/_error_messages.html.erb... Done!
Removing: app/views/users/shared/_error_messages.html.erb... Removed!
Converting: app/views/users/shared/_links.html.erb... Done!
Removing: app/views/users/shared/_links.html.erb... Removed!
Converting: app/views/users/unlocks/new.html.erb... Done!
Removing: app/views/users/unlocks/new.html.erb... Removed!
```

＊たまに、途中でYorN?みたいに聞かれることはありますが、これはhamlファイル作ったけど、元のerbのファイルは消しちゃっていい？的なことを聞かれています。基本的に使わないはずなのでYesのYで大丈夫です。

これで、変換終了です。

## よく聞くエラーとか

変換して早速ビューを見てみようとしたら、エラーが出たよ！って聞くことがあります。
templateがmissingしてtext/html的な・・・スクショしておけばよかったorz

結論、**rails sを再起動すれば大抵治ります**。焦らず再起動しましょう。

一つだけerbをhamlに変換したいけど、コマンドとか面倒くさい・・・って人もいるかもしれないです。
[Convert HTML to HAML](http://htmltohaml.com/erb)というhtmlをコピペしただけでhamlに変換してくれるサイトもあります。載せておきますね。


終わりです。お疲れ様でした。
windowsでwordpressのためにphp5.6からphp7.3に移行

今更ながら5.6系のサポートは2018年末までだった。あわてて7.3に更新することにした。
windowsのwordpressで苦労している人ってすくないのか、ググっても記事が少ない。
なんとか動いたのでメモ

# 手順
1.　http://windows.php.net/download/　から7.3系統のマルチスレッド対応で64bit用の「VC15 x64 Thread Safe」をローカルにダウンロード
2.　ダウンロードしたzipをサーバにコピー
3.　古いPHPフォルダをC:\php_5.6.versionにリネーム
4.　zipを展開してC:\phpとして保存
5.　C:\php_5.6.version\php.iniをコピーしてC:\php\php.iniとする
6.　apache再起動・・・動かない


# 原因

PHPのおまじないをやっていたことを思い出した。
httpd.confにphp.ini へのパスを設定します

## 5.6のおまじない
どこで調べたのかは忘れた
>LoadModule php5_module C:/php/php5apache2_4.dll
>AddHandler application/x-httpd-php .php
>PHPIniDir "C:/php"


## 7.3のおまじない
足して2で割ったようなかんじ
[centos7 phpinfo.phpが表示されない](https://qiita.com/bzy/items/576e85a1e44c6d54a25c)
[モジュール(php7_module)の追加と拡張子(.php)の登録](https://www.adminweb.jp/apache/php/index1.html)

>LoadModule php7_module C:/php/php7apache2_4.dll
>AddHandler php7-script .php
>DirectoryIndex index.php
>PHPIniDir "C:/php"


##　もし「PHPIniDir "C:/php"」を付け忘れると、、、

- phpinfoで「Loaded Configuration File	(none)」ってなってpip.iniが読み込まれない
- wordpressで「Fatal error: Uncaught Error: Call to undefined function mysql_connect() 」でハマる

# php.iniの修正
上と順番が逆になっているかもしれないけど、こんな感じで修正する。
>extension_dir = "c:\php\ext"
>
>extension=curl
>extension=mbstring
>extension=mysqli
>extension=openssl
シーケンスの利用方法（PostgreSQL）

#シーケンスとは
　**シーケンス**とは、連番を払い出してくれるものです。テーブルのIDなどを挿入する際に使用することができます。

#シーケンスの使用例
　テーブルのIDにシーケンスを使用する例を見てみましょう。以下の手順でシーケンスを使います。
(1)シーケンスを作成する
(2)シーケンスをテーブルのIDとして挿入する

##(1)シーケンスを作成する
　シーケンスは以下のようにして作成します。オプションには一番最初に払い出す番号をしていするSTARTオプション（例：START 10）があります。開始番号を指定しなかった場合は1となります。

**書き方：CREATE SEQUENCE シーケンス名 （オプション）**

```sql

CREATE SEQUENCE test_seq;
```
##(2)シーケンスをテーブルのIDとして挿入する
　テーブルにIDを挿入するときに、(1)で作成したシーケンスを使います。**nextval('シーケンス名')**で連番を払い出すことができます。

```sql
postgres=# CREATE TABLE test_table (id int, name varchar(20));
CREATE TABLE
postgres=# INSERT INTO test_table VALUES (nextval('test_seq'), 'suzuki');
INSERT 0 1
postgres=# INSERT INTO test_table VALUES (nextval('test_seq'), 'ito');
INSERT 0 1
postgres=# INSERT INTO test_table VALUES (nextval('test_seq'), 'sato');
INSERT 0 1
postgres=# SELECT * FROM test_table ;
 id |  name
----+--------
  1 | suzuki
  2 | ito
  3 | sato
(3 rows)
```

#シーケンスの値をリセットする方法
　シーケンスの値は**setval('シーケンス名', 値)**を使ってリセットすることができます。ちなみに、**currval('シーケンス名')**で一番最後に払い出したシーケンスの値を確認することができます。

```sql
postgres=# select setval('test_seq', 1);   ---現在のシーケンス番号を1にリセット
 setval
--------
      1
(1 row)

postgres=# select currval('test_seq');    ---現在の値を確認
 currval
---------
       1
(1 row)

postgres=# select nextval('test_seq');    ---値の払い出し
 nextval
---------
       2
(1 row)
```
#（参考）シーケンスの内容を確認する
　シーケンスの内容はメタコマンドを使用して確認することができます。

**書き方：\d シーケンス名**

```sql
postgres=# \d test_seq
                          Sequence "public.test_seq"
  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache
--------+-------+---------+---------------------+-----------+---------+-------
 bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
```

#まとめ
　シーケンスを使用することで一意な連番を作成することができました。nextvalで値の払い出し、currvalで値の確認、setvalで値のリセットをすることができます。
　読んでくださり、ありがとうございました！

#参考
PostgreSQLのマニュアル
https://www.postgresql.jp/document/11/html/functions-sequence.html
【wget】Webサイトを丸ごとローカル環境にダウンロードする備忘録

# はじめに

管理者画面にアクセスできなくなったワードプレスのサイトがあり、保存しておきたい場面がありました。その方法とハマりどころをまとめます。

# 環境

- Mac OSX 10.15（19A602）

# wgetの準備

Webサーバーからコンテンツをダウンロードするためのコマンドです。

```.bash
$ brew install wget
```

# Webサイトの一括ダウンロード

```.bash
$ wget -rkp -l 3 http://example.com/
```

- `-r`, `--recursive`: 再帰ダウンロードを行う
- `-k`, `--convert-links`: HTML や CSS 中のリンクをローカルを指すように変更する
    - 変更されるのは全てのダウンロードが終了後に行われるので、**ダウンロード中に確認しても適用されていない点に注意**
- `-p`, `--page-requisites`: HTML を表示するのに必要な全ての画像等も取得する
- `-l`, `--level=NUMBER`: 再帰時の階層の最大の深さを NUMBER に設定する (0 で無制限)

## 課題

再起的に保存する`.css`や`.js`にURLパラメータが付いていることがあり、URLパラメータ付きの名前として保存されてしまっていた。保存時に`?`や`&`はエスケープされてしまうためローカルHTML上からアクセスできない問題が発生した。

参照が外れている`.css`, `.js`ファイルをリネーム(`?`以下を削除)することで解決した。
(数が多くなかったので手動で対応したが、多い場合は正規表現などで置換できそう。)

## 試したが使わなかったコマンド

- `-H`, `--span-hosts`: 再帰中に別のホストもダウンロード対象にする
    - あまり外部ドメインに依存していなかった
- `--content-disposition`: Content-Dispositionで指定されたファイル名で保存する

## 最後に

`-k`の適用タイミングで結構ハマりました。
OpenCVとNumPy環境におけるグレースケール画像を用いた画素値反転

#初めに(^ω^)
以前の投稿で、グレースケール画像の作成に成功しました（やったね！）
その画像を用いて面白いことができないかと考えた結果です
画素値を上手に扱えたら便利なんだよということを知ってほしかったため作りました

#作成したソースコード

```Python
#-----------------------------------------
#OpenCVとNumPy環境におけるグレースケール画像を用いた画素値反転
#-----------------------------------------
import numpy as np
import cv2

hoge = './'
#保存先指定
file = './text8.txt'
#カラー読み込み
img = cv2.imread(hoge+'shigure.png')
#グレースケールで読み込み
img2 = cv2.imread(hoge+'shigure.png',0)


#Numy配列に保存
im = np.array(img2)

#画像サイズ取得
shape = im.shape

#閾値設定
Threshold = 220
for y in range(shape[0]):
    for x in range(shape[1]):
        if im[x][y] >= Threshold:
            im[x][y] = 0
            
#色反転
for y in range(shape[0]):
    for x in range(shape[1]):
        im[x][y] = 255 - im[x][y]
        
#テキスト出力
np.savetxt(file, im, fmt="%3.0f", delimiter=",")

#配列出力
print (im)

#画像出力
cv2.imwrite(hoge+'gray.png', im)
```
#結果
![shigure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515390/27edfb4b-0914-a473-8ff1-c9ead3cf99e6.png)
これが
![gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515390/0d697090-2e2b-e956-1def-835f6e37e22e.png)
こうなって
![gray2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515390/1f1eabd9-234c-5bc0-0997-59361a6f2697.png)
こうなった！
かわいい(^ω^)

#コード解説
##①画素値反転
```Python      
#色反転
for y in range(shape[0]):
    for x in range(shape[1]):
        im[x][y] = 255 - im[x][y]
```
画像の画素値を扱う際に、２次元配列として扱うことができるといろいろと便利だったりします。
今回のソースで閾値等使ってますが次回以降に詳しい解説をしたいと考えています（願望）

#参考文献
とくにないかも

#目次のようなもの
[Re：ゼロから始める機械学習生活（深層学習もあるよ）](https://qiita.com/KengoKudou/items/0973ee8f98315460b195)
ここに進捗状況等を載せてます。
今まで書いたものを一覧にしていたりするので見てみてください！
【Bash】Shellの関数内だけsetコマンドオプションを有効化

普段Bashを使うことが多いので、よく使うコマンドとかワンコマンドで自動化したいものを関数化しています。関数のログとか未定義の変数を使用時にエラーメッセージを出力するようにしたかったのでsetコマンドを使用しています。ただそれを関数内だけ有効にしないと関数以外の普通のコマンドを実行した時も出力されてしまうので、setコマンドのオプションを無効化する必要がありました。
備忘録としてその方法を説明したいと思います。

# setコマンドオプションの無効化

## 公式マニュアル
setコマンドオプションの無効化ですが、公式マニュアルには以下のように書かれていました。

> Using + rather than - causes  these  options  to  be  turned  off.

`-`でオプションを指定して有効化しますが、`+`を使えばオプションを無効にすることができます。

## 方法
まずは`-`で有効化したいオプションを指定します。指定できるオプションはマニュアルを参照してください。

```bash
set -eux
```

3つのオプションを無効にするには`+`を使います。

```bash
set +eux
```

それぞれを関数の先頭行と最終行に記述すれば、関数内だけ実行コマンドのログやエラーメッセージを出力できるようになります。こんな感じに↓

```bash
function test {
    set -eux
    # Shellに設定されているオプションの確認
    # つまり、差分をとればsetで設定されたものが確認できる
    echo $SHELLOPTS
    echo "test"
    set +eux
    echo $SHELLOPTS
}
```

## 実行ログ
先頭に`＋`が付いているのが`x`オプションによって出力されたログになります。

```bash
+ echo braceexpand:emacs:errexit:hashall:histexpand:history:interactive-comments:monitor:nounset:xtrace
braceexpand:emacs:errexit:hashall:histexpand:history:interactive-comments:monitor:nounset:xtrace
+ echo test
test
+ set +eux
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
```

`set -eux`と`set +eux`の差分は以下の3つのオプションです。

|option|description|
|:--:|:--|
|errexit|`-e`によって指定する<br>何かしらのコマンドがexit code０以外を出力した際に即座にerror exit|
|nounset|`-u`によって指定する<br>未定義の変数の利用があればエラーメッセージを出力してerror exit|
|xtrace|`-x`によって指定する<br>実行ログを出力する|


:wave::wave:
HCJCODE - Webコーディングベース


# HTML base

```html
<!DOCTYPE html>
<html>
<script>
  function demo() {
    var demo = document.getElementById('demo');
 
    demo.innerHTML = 'Hello World';
  }
</script>
<noscript>Sorry, your browser does not support JavaScript!</noscript>
<body>
  <h1>My coding sample</h1>
  <button type="button" onclick="demo()">Click me to show demo</button>
  <p id="demo"></p>
</body>
</html> 
```

# Web API library
* https://developer.mozilla.org/ja/docs/Web/Guide/API
* https://developer.chrome.com/multidevice/user-agent
    * https://github.com/GoogleChrome/chrome-app-samples



# tool
* https://www.w3schools.com/html/tryit.asp?filename=tryhtml_scripts_intro


# 関連知識
## エンジン
* [HTMLレンダリングエンジン](https://ja.wikipedia.org/wiki/HTML%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3)
* [Microsoft EdgeがChromeと同じエンジンを採用した理由とは？　日本マイクロソフトに直接聞いてみた](https://nlab.itmedia.co.jp/nl/articles/1906/17/news124.html)
* ⭐️[Webブラウザ、レンダリングエンジン、JavaScriptエンジンを整理して図視化してみた](https://qiita.com/umashiba/items/8cb47825624c5cb043d6)
* https://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A7%E3%83%96%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%AE%E4%B8%80%E8%A6%A7
* https://www.jianshu.com/p/03b70c89934b



## HTML


## CSS


## JS
Serviceクラスの意義と勘所

今回はRails界隈でもいくつかの議論がある「Serviceクラス」について
一つの考え方を書いていこうと思います

# Serviceクラスとは
MVCやDDDにおいて分離させている**レイヤーの責務が大きくなった際に活躍の場がある概念**です
DDDでは「ドメインサービス」と「アプリケーションサービス」とそれぞれのレイヤーで定義されていますが
MVCにおいても「モデルサービス」「ビューサービス」「コントローラーサービス」みたいな考え方ができますね

ただし、「ビューサービス」ではなく「ビューヘルパー」だったり、別に置き換えられたりします
それ故、世間一般にいうServiceクラスは「**ドメイン知識を持った手続きクラス**」を指すことが多いです

# 意義
MVCなど完成された概念になぜServiceクラスが必要なのか？
その答えは肥大化したクラスを整理することにあります
**処理系をまとめて、カプセル化し、責務を分ける**といったことが意義になりますね

DDDの考え方ではオブジェクトとしてモデル化すると不自然になる場合に
サービスとして宣言される独立したインターフェイスとしてモデルに追加する＝Serviceクラスの追加
が有効であるとしています

# 制約
Serviceクラスは手続きをまとめているだけなので**状態を持ちません**
イメージすればわかりますが「口座振り込み手続きサービス」が状態を持つのはおかしいですね
状態は「口座」「ユーザ」「窓口」が持つべきで手続きはステートレスである必要があります

# 注意点
上記の内容だけ聞くと入れたほうがいい概念な気がしてきますね
しかし、導入には注意が必要です
やたらめったら入れると以下のような問題が発生します

* 重要な知識がドメインモデルから漏れ出す
* 設計の柔軟さが失われる
* 再利用性の低いコードが増えやすい

なぜ、そのような問題が発生するのか？
それは以下の理由があります

* Serviceクラスがドメインモデルに近いため
* 状態を持たないように手続きを記述するため
* 個々のモデル手続きをまとめるため

一概には言えませんが、Railsのようなフレームワークを使うのであれば
極力Serviceクラスの出現は抑えるべきと言えます

# 勘所
以上を踏まえてServiceクラスはいろいろ考慮し、利用しましょう

個人的にServiceクラスを導入する場合は以下の問答をしています

* どうしてもドメインモデルが表現するには苦しい
* ValueObjectの導入で吸収できなさそう
* 状態を持たずに実装できそうな手続きか？
* 他のモデルでは利用しそうにない（再利用可能性が低い）

利用できると強力ですが、理解不十分で使うには恐ろしいServiceクラスの話でした

# その他

[軽量DDDでもやったほうがいい理由、けどやらない理由](https://qiita.com/kichion/items/b830ceffd08d3f6cfc9e)
[レイヤードアーキテクチャの視点](https://qiita.com/kichion/items/aca19765cb16e7e65946)
[ValueObjectという考え方](https://qiita.com/kichion/items/151c6747f2f1a14305cc)
[集約で境界を正しく表現する意味](https://qiita.com/kichion/items/3826277eb372826a313e)
Nuxt.js(SPA)+FirebaseのWebアプリで初マイグレーションをしてけど、いろいろ失敗した話。。

これはただの失敗談です。。役に立つかわからないけど、誰かの参考になるといいな。。

Nuxt.js+Firebaseで開発した[Webサービス](https://tsundoku.site)をリリースして2ヶ月目くらい。
サービス止めて、初マイグレーションしてときの失敗談です。


## サービスを止めてやりたかったこと

いままではちょこっとした変更が多かったため、
サービスを止める必要なく、Nuxtアプリの更新だけで十分でしたが、
新しい機能を追加する際に、Firestoreのスキーマを変える必要が出てきました。

やりたいことは、
「 **今までのデータを読み取って、別の形式に再構成する** 」
という感じのことで、止めないとダメかなと。


## メンテンナンスのときの手順

手順はこんな感じ。

1. メンテナンス画面に遷移するようにmiddlewareを変更
2. firestoreのルールを全部ブロックするように変更
3. スキーマ変更処理を実行
4. メンテナンス画面を解除し、hostingに最新版をアップロード
5. firestoreのルールをもとに戻す

ポイントとしては、

* ページを移動したら、**middlewareでメンテナンス画面に遷移**する
* クライアントから変更されないように、**全部ブロックするルールに変更**する

の2点くらい。

結果、かなりの失敗をしました。。

# やらかした失敗と反省点

ダメだったのは以下の2点。

1. **時間がかかりすぎ**(予想5h以内→実際11h)
2. **メンテナンス画面がちゃんと出ていなかった**


## 1. 時間がかかりすぎ問題

一番の失敗は時間かかりすぎです。。
予想以上に時間が。。予想5h以内→実際11h

対象のスキーマが、

```
本 - 履歴 - ユーザ
```

多対多の関係を持つ構造を持っていて、
本を起点に履歴データの構造を変更するような処理だった。

規模としては、本の件数が47670ドキュメントで、履歴が58144ドキュメント。

実際に実行してみたところ、速度が上がったり下がったりで、
1時間に5000冊位な感じ。。これだけで10時間近く。。


### 振り返ってみて

やってみていくつか良くない感じだったなと。。
振り返ってみて、改善できそうなのは以下の2点な感じ。。

#### a. メンテナンスでは差分部分のみ反映する

全量を読み込んで、反映していたので、かなりの時間が。。

とはいえ、1日の変更であれば、そこまで多くないので、
**事前に全量の変更を用意して**おき、メンテナンスにしてから、
**差分があるところだけを更新**をするなどのほうが良かった気がしてる。。

次はそうしよう。。

#### b. 処理の並列実行

内部の処理も`Promise.all`を使ってない部分がいくつか。。

`foreach`で実行している部分を置き換えて、
**並列実行にするだけでも改善されそうな感じ**。。

この記事がわかりやすい感じだった。
- [Node.js で Promise の直列実行と並列実行、同時実行数の制御 - Qiita](https://qiita.com/euxn23/items/b9bd8bf28fe3ca707fe3#%E4%B8%A6%E5%88%97%E3%81%A8%E7%9B%B4%E5%88%97%E3%81%AE%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9)


### 2. メンテナンス画面が出てない

メンテナンスをはじめてから、GoogleAnalyticsを見てると、リアルタイムユーザがちらほら。
終わってからも、メンテナンス画面じゃないとの報告もちらほら。。

Firebase Hostingにアップロードしてもキャッシュが残っているとだめだったもよう。。

### 振り返ってみて

振り返ってみて、改善できそうなのは以下の2点な感じ。。

#### a. PWAモジュールを使ってnetworkファーストで取得する

以下の記事の前半で書かれている通り、**キャッシュ戦略を設定することで解決**できそう。
・[Nuxt.js(SPA)とFirebaseで強制リビジョン（バージョン）アップするならPWAモジュールを使おう - Qiita](https://qiita.com/hecateball/items/29f726430d204c964f04)

#### b. Remote Configを使って切り替える

同じく上記の記事で書かれている通り、**Remote Configを使う**のが良さそう。
・[Firebase Remote Config  |  Firebase](https://firebase.google.com/docs/remote-config)

**Remote Configの値によって、メンテナンス画面の表示を切り替えれるようにしておけば**、
Firebaseのコンソール上での変更で、切り替えれるようにできる感じ。


## まとめ

やってみてわかることも多いですが、なかなかつらい感じ。。

Firebase/Firestore関連の運用系の話はあまり見ないので、
とりあえず書いてみましたが、誰かの役に立てば。。

こんな方法もあるよ！こっちのほうがいいよ！などあれば、
コメントもらえるとうれしいです(*´ω｀*)

## ほかの失敗談

以前にもこんなのを書きました。
初級向けの話が多いですが、実際にやってみての体験談的な話。
- [Nuxt.js(SPA)+Firebaseで積読用の読書管理サービスを作ってみたときにハマったこと... - Qiita](https://qiita.com/kira_puka/items/8c1d1240c3aa200cbec0)
- [Firebaseで作ったWebサービスを3ヶ月運用してみて、ハマったこと・知っておきたかったこと - Qiita](https://qiita.com/kira_puka/items/ef7dd47519403cd9bcf2)

## こんなのつくってます!!

やらかしたWebサービスはこちら。。
積読用の読書管理アプリ 『積読ハウマッチ』
[積読ハウマッチ](https://tsundoku.site)は、Nuxt.js+Firebaseで開発してます!

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/572d4947-f40b-e4dc-1c9c-bc584cd2a66c.png" width="200"/>

もしよかったら、遊んでみてください〜

要望・感想・アドバイスなどあれば、
公式アカウント([@MemoryLoverz](https://twitter.com/MemoryLoverz))や開発者([@kira_puka](https://twitter.com/kira_puka))まで♪
AHK(AutoHotkey)を使い　メールの定型文（署名）を作成する（Alt + X）　複数行の文字列送信は → 「Send」ではなく → 「Clipboar」 を経由すること！

;--------------------------------------------------
!x:: ; Alt+x
;--------------------------------------------------

;Menuを使用する時に使用
ALT_X:

;現在クリップボードに入っているデータを Backup へ退避
Backup := ClipboardAll

Sleep,300

;IMEGetstateOFF()
;Sleep,300

;ここで新規メールの内容に貼り付ける内容を作成する　メール最後に挿入する署名を作成
MailVar=●●です。
MailVar=%MailVar%以上`n ;←最後に改行マーク

Clipboard = %MailVar%

ClipWait , 3

;内容確認用
;MsgBox, %MailVar%

;【重要】データを Send するよりも　クリップボードに入れて Ctrl + V （貼り付け）した方が凄く速いのでオススメです
Send ^v

Sleep,300

Send {Left 3}

Sleep,300

;退避させていたデータをクリップボードに戻す
Clipboard := Backup

Sleep,300

;IMEGetstateON()

Return
;--------------------------------------------------
NLP4J [002] Javaで Yahoo! デベロッパーネットワーク 日本語係り受け解析（V1）を利用して日本語の構文解析をしてみる

[Indexに戻る](https://qiita.com/oyahiroki/items/ba79a759c599d3459d29) : [[001]形態素解析](https://qiita.com/oyahiroki/items/7319a1759b96fd6232e3) > [002]構文解析 > [[003]品詞の統計処理](https://qiita.com/oyahiroki/items/85975c7dd410d04ff256)

■追記

Yahoo! の構文解析APIについてV1が終了しましたのでこの記事は参考としてのみ置いておきます。
詳しくは
【重要】テキスト解析Web APIにおける日本語係り受け解析API仕様変更のお知らせ - Yahoo!デベロッパーネットワーク
https://developer.yahoo.co.jp/changelog/web_apiapi.html
をご覧ください。


---


NLP4J を使ってJavaで日本語の構文解析（係り受け解析）をしてみます。

構文解析ができると「車が急に止まった」というテキストから
・「車が→止まった」
・「急に→止まった」
というような情報を抽出することができます。

#Maven
```xml
<dependency>
  <groupId>org.nlp4j</groupId>
  <artifactId>nlp4j</artifactId>
  <version>1.0.0.0</version>
</dependency>
```

#Code1

```java
import nlp4j.Keyword;
import nlp4j.KeywordWithDependency;
import nlp4j.impl.DefaultNlpServiceResponse;
import nlp4j.yhoo_jp.YJpDaService;
public class HelloNLP4JDA {
		// 自然文のテキスト
		String text = "車が急に止まった。";
		// 係り受け解析
		YJpDaService service = new YJpDaService();
		// 係り受け解析の結果を取得する
		DefaultNlpServiceResponse response = service.process(text);
		for (Keyword kwd : response.getKeywords()) {
			if (kwd instanceof KeywordWithDependency) {
				// 係り受け解析の結果を出力する
				System.err.println(((KeywordWithDependency) kwd).toStringAsDependencyTree());
			}
		}
}

```

#Output1
このような感じで出力できます。簡単ですね！

```
-sequence=6,lex=null,str=。
	-sequence=5,lex=null,str=た
		-sequence=4,lex=null,str=止まっ
			-sequence=2,lex=null,str=が
				-sequence=1,lex=null,str=車
			-sequence=3,lex=null,str=急に
```

#Code2
キーワードのオブジェクトを操作することで細かい結果を調べることができます。

```java
	public static void main(String[] args) throws Exception {
		// 自然文のテキスト
		String text = "車が急に止まった。";
		// 係り受け解析
		YJpDaService service = new YJpDaService();
		// 係り受け解析の結果を取得する
		DefaultNlpServiceResponse response = service.process(text);
		for (Keyword kwd : response.getKeywords()) {
			if (kwd instanceof KeywordWithDependency) {
				// 係り受け解析の結果を出力する
				print((KeywordWithDependency) kwd, 0);
			}
		}
	}

	static void print(KeywordWithDependency kwd, int depth) {
		System.err.println(depth + ":" + kwd.getStr());
		for (KeywordWithDependency kwd2 : kwd.getChildren()) {
			print(kwd2, depth + 1);
		}
	}

```

#Output2

```
0:。
1:た
2:止まっ
3:が
4:車
3:急に
```

簡単ですね！

#Indexに戻る
[NLP4J のご紹介 - [000] Javaで自然言語処理 Index](https://qiita.com/oyahiroki/items/ba79a759c599d3459d29)

#プロジェクトURL
https://www.nlp4j.org/
<img width="64" alt="NLP4J_N_128.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110265/4f59b21b-4b43-187d-0aac-7b99c0fd4bac.png">



---
GASのWEBアプリケーションでUAをスプレッドシートにロギングして簡易アクセスログ

# 三行で

* `UiApp.getUserAgent()`(UiAPP)は廃止され`HtmlService.getUserAgent()`へ
* IPはアクセス時点では取得できそうにない
* スプレッドシートの年日でgroup byする方法が不明


# アクセスみたい


ウェブアプリケーションとして公開したapps scriptのアクセスは、
doGetの自分以外による実行数を見ればぼんやりとわかります。

![gas.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62354/993fec93-c0c1-9a77-3453-33739d6abdc8.jpeg)

でももうちょっとユニーク数を詳しく知りたいときが出たりするので、手軽な識別方法はないかなと考えました。

調べてみると、
[GoogleAppsScriptでのあれやこれや - gstudio - blog](http://www.gstudio.me/blog/2013/01/googleappsscript.html)
UserAgentを取得できるようなので、これでUAのユニーク数が最低限の利用者数だということができそうです。
短時間の連続実行も別々の人なのか同一の人のリロードなのかもなんとなくわかりますしね。

というわけで`var app = UiApp.createApplication();`を追加しましたが動きません。

UiAppは廃止されたのでHtmlServiceを使うようです

[Class HtmlService  |  Apps Script  |  Google Developers#getUserAgent()](https://developers.google.com/apps-script/reference/html/html-service#getUserAgent())

というわけでこんな風に

```js
function doGet() {
  const ua = HtmlService.getUserAgent();
  if (ua) {
    var as = getSheet(ACCESS_LOG_SHEET_NAME);
    as.appendRow([Utilities.formatDate(new Date(),  'Asia/Tokyo', 'yyyy年MM月dd日 HH:mm:ss'), ua]);
  }
  var htmlOutput = HtmlService.createTemplateFromFile("template").evaluate();
  htmlOutput.setTitle(WEB_TITLE);
  return htmlOutput;
}
```

レスポンスに悪影響がでる書き方ですが、スプレッドシートが短い現在は目立った処理速度の低下は見られませんでした。

ifにしてるのはエディターから直接実行した場合などを考えてますが、特に必要ではないはず。

問題点としては、ヘッダーとして公開しているウェブアプリか(/exec)、最新バージョンでの動作の確認か(/dev)見分けが付いてないということでしょうか。
解決策はあるかもしれませんが、些細な問題なので現状スルーで。

![gas2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62354/0b212b88-4863-6932-c3c2-48a6a0006ed9.jpeg)



UAのユニーク数チェックには別シートに
`=QUERY('アクセスログ'!B:B,ʺselect count(B), B group by Bʺ)`
とすると何種類のUAがあるのかがわかります。

できれば日ごとのアクセス数も集計したいのですが、日付フィールドの時間を切り捨てて年月日だけで集計する方法がわからず…SQLの不勉強さがたたっていますね…

# IPは？

[Retrieve Public IP From Google Web App - Stack Overflow](https://stackoverflow.com/questions/18666322/retrieve-public-ip-from-google-web-app)

doGetの中で取得する方法は見つからなかったので、クライアント側で取得してdoPostに投げてロギングする方法などが考えられますね。
そこまでするならAnalyticsを乗っけたほうが早い気がしますが、doGetがすばやくなるのでもろもろをクライアント側とdoPostでやっちゃうことは覚えていてもいいかなと。
非同期で処理できますし。

***

使ってみたら意外とモバイルからのアクセスがあったりでそっちの動作確認していないなと気づきを得られたりでよかったです。
CentOS7+anaconda3-5.3.1インストール手順

CentOSをインストールして、下記をrootで実行。

### 更新

```
yum update -y
```

### SELinux

```
setenforce 0
```

```
vi /etc/sysconfig/selinux
SELINUX=disabled
```

### vim, git

```
yum -y install vim-enhanced
yum -y install git
```

### 環境

```
git clone https://github.com/yyuu/pyenv.git ~/.pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrc
source ~/.bashrc
```

### 解凍系

bzip2は、pyenv installで使うので必要。無いとインストールがエラーになる。

```
yum -y install zip
yum -y install unzip
yum -y install bzip2
```

### インストール

pyenv install -l | grep anaconda
でバージョンを確認して、指定して入れる。

```
pyenv install anaconda3-5.3.1
```

下記が表示されたら成功。


```
[root@python ~]# pyenv install anaconda3-5.3.1
Downloading Anaconda3-5.3.1-Linux-x86_64.sh.sh...
-> https://repo.continuum.io/archive/Anaconda3-5.3.1-Linux-x86_64.sh
Installing Anaconda3-5.3.1-Linux-x86_64.sh...
Installed Anaconda3-5.3.1-Linux-x86_64.sh to /root/.pyenv/versions/anaconda3-5.3.1
```


# 設定

```
pyenv rehash
pyenv global anaconda3-5.3.1
echo 'export PATH="$PYENV_ROOT/versions/anaconda3-5.3.1/bin/:$PATH"' >> ~/.bashrc
source ~/.bashrc
conda update conda
```

```
Proceed ([y]/n)? y
```


# update

```
conda update numpy
```
firebase authのUserクラスで取得できるTwitterアイコン画像のサイズについて

## 躓いた点
Firebase AuthでTwitter認証を行ったときに、firebase.UserInfo.photoURLで取得できるアイコン画像の解像度が48x48と低くて困っていました。

photoURLに入っているURL文字列の例：
https://abs.twimg.com/sticky/default_profile_images/default_profile_normal.png

このURLで取得できる画像サイズ：
![icon](https://abs.twimg.com/sticky/default_profile_images/default_profile_normal.png)

html5のcanvas上に合成して使いたかったのでもっと高画質のものが欲しかったのです。

## 困ったときの公式ドキュメント

https://developer.twitter.com/en/docs/accounts-and-users/user-profile-images-and-banners

"By modifying the URL,"とあったので、ドキュメントにある規則通りに自分でURLを書き換えてアクセスすればよいのだと判断しました。

## 対応策

TypeScript(JavaScript)のstringに備えられた`replace`メソッドで`"normal"`の部分を置き換えます。

### 78x78(bigger)

```typescript
user.photoURL.replace("normal", "bigger");
```
https://abs.twimg.com/sticky/default_profile_images/default_profile_bigger.png
![icon](https://abs.twimg.com/sticky/default_profile_images/default_profile_bigger.png)


### 24x24(mini)

```typescript
user.photoURL.replace("normal", "mini");
```
https://abs.twimg.com/sticky/default_profile_images/default_profile_mini.png
![icon](https://abs.twimg.com/sticky/default_profile_images/default_profile_mini.png)

### original size

```typescript
user.photoURL.replace("_normal", "");
```
https://abs.twimg.com/sticky/default_profile_images/default_profile.png
![icon](https://abs.twimg.com/sticky/default_profile_images/default_profile.png)

### 200x200
公式ドキュメントにはありませんが、[公式Webクライアント](https://twitter.com)のプロフィール画面に表示されるアイコンの`img`タグが`src`指定していました。

```typescript
user.photoURL.replace("normal", "200x200");
```
https://abs.twimg.com/sticky/default_profile_images/default_profile_200x200.png
![icon](https://abs.twimg.com/sticky/default_profile_images/default_profile_200x200.png)

### 400x400
こちらも公式ドキュメントには記載がないようです。しかし[公式Webクライアント](https://twitter.com)のプロフィール画面に表示されるアイコンをタップして表示される拡大アイコンのimgタグが`src`指定していました。

```typescript
user.photoURL.replace("normal", "400x400");
```
https://abs.twimg.com/sticky/default_profile_images/default_profile_400x400.png
![icon](https://abs.twimg.com/sticky/default_profile_images/default_profile_400x400.png)

## まとめ

original sizeだとユーザーがどんな画像を登録しているかで解像度が変わる可能性があります。多分。
200x200や400x400のサイズ固定ができるとcanvas上で利用するのも楽ですね。
エッセンシャルWPF

#諸言
　学習履歴全体のリンク
　https://qiita.com/shigural/private/444b991293e5ab1b40af
#本文
##エッセンシャルWPF
###1章　はじめに
User32→Winform→WPFと発展してきたがいずれもCLRというシステムで動作している
従来との違い

- 従来との違いは総合的なレンダリングとベクタベースのシステム
- XAML(ザムル)を利用した汎用的な記述
- 
StachPanelを使うと縦に、WrapPanelを使うと回り込んだ配置ができる
配置は他にもViewportとTileModeによる塗りつぶし2D配置、GeometryModel3Dによる3D配置、DoubleAnimationによるアニメーションなどができる。Styleにより一斉にプロパティを定義できる。
###2章　アプリケーション
####WPFの原則
WPFの基本原則としてスケーラブルであること、つまり万能であることが上げられる。
WPFはビルドするとXBAPが生成される。これは極めて多機能であるがHTMLベースが標準である。HostInBrowser属性をfalseにすることでデスクトップアプリケーションプログラムとなり更に多機能となる。
WPFの基本原則にWebプログラミングとデスクトッププログラミングの結合も上げられる。例えばハイパーリンクのようなWeb機能をそのまま利用できる。
####リソースと構成
System.Configurationを利用することでアプリケーションの永続的な状態を管理することができる。つまり状況を保存して起動時にロードすることができる。
リソースは3通りのビルドアクションがある。Contentはリソースをディレクトリにコピーする、Resourceは共通リソースに埋め込む、EmbeddedResourceはアプリケーションに個別で埋め込むがマークアップ利用ができない。リソースはGetContentStream、GetResourceStream、GetRemoteStream関数により読み込むことができる。URI参照による読み込みは、下位レベルではこれらの関数を呼び出している。
####ウィンドウ
ウィンドウが呼び出されるとき、代表的なものとして次の手順が実行される。
1. コンストラクタが呼び出される
2. Window.Initializedイベントが発生
3. Window.Activeatedイベントが発生→Deactivate（不安定）と交互に何度も発生
4. Window.Loadedイベントが発生
5. Window.ContentRenderedイベントが発生
6. ユーザーがウィンドウを操作する
7. Window.Closingイベントが発生
8. Window.Unloadedイベントが発生
9. Window.Closeイベントが発生
ウィンドウを表示するためにはShow,ShowDialogを呼び出すもしくはVisibilityをVisibleに設定する方法があるが、どれも同じ挙動をする。これはプログラムから呼び出す場合とデータバインディングの両方に対応するためである。なお、Windowを別のWindowの子とするためにはOwnerプロバティを設定する。被オーナーは常にオーナーの上に表示され、オーナーの選択を妨げない。またオーナーが閉じると自動的に閉じ、オーナーが非表示になると自動的に非表示になる。ウィンドウの位置はWindowStartupLocationにより、大きさはSizeToContentにより設定できる。なおWindowStartupLocation.CenterOwnerによりオーナーの中心に表示できる。
ウィンドウの情報はすべてApplication.Windowsに与えられる。Application.Current.Windowsをforeachで取り出すことにより、各々のtitleからウィンドウ一覧を表示するようなことができる。
####ユーザーコントロール
ウィンドウは独立した機能セットであるが、これよりも小さな単位としてコントロールが上げられる。コントロールはUIをカプセル化したユーザーコントロールContentControlと他のアプリケーションから利用できる再利用可能なカスタムコントロールに分かれる。
####ナビゲーションとページ
WPFには3つのナビゲーションフレームワークを備えている。

- ナビゲーションホスト（NavigationWindow）
    - Windowから派生する。戻るボタンなどナビゲーションを可能にするインフラストラクチャを追加する。
- ナビゲーション対応コンテンツ（Page）
    - 
- ジャーナルである。
    - 

###3章　コントロール
###4章　レイアウト
###5章　視覚要素
###6章　データ
###7章　アクション
###8章　スタイル

###付録　基本サービス
キーボードを遠隔操作するデバイスは簡単に作れるらしい

こんばんは。菅谷です。
キーボードを遠隔できないかなーと思いまして、

調べたり、会社の人に聞いてみたりすると結構簡単にできるらしいのでまとめました。

##キーボードを遠隔で操作するのはBLE通信を使えば簡単にできるらしい
まずは、操作するためのマイコンが必要です。

最初はArduino UNOがいいんじゃないかと思ったんですが、
いくら書き込もうとしてもウンともスンともいきません、、

なんと、Arduino UNOではキーボードの操作はできないらしいのです。
できたとしても、Arduinoで遠隔操作しようとするとマイコンをPCに刺しっぱなしにしないといけないですよね、、（そうじゃなくてもできるのかもしれないけど、知らない笑）

刺しっぱなしも何かイケテナイですよね。
とゆうことで今回は、BLE通信を使って遠隔でPCの音量を操作していきます。
イメージはこんな感じ

```
マイコンにスケッチを書き込み
↓
BLE通信
↓
キーボードの操作
↓
音量が上がる
```

##準備を始めよう
準備は以下のようにしていきます

- BLE通信ができるマイコンを準備
- 上をPCに繋げるためのケーブル(type-B)
- Arduino IDE
- マイコンの電源にするためのバッテリー

##実際に動かしてみる
マイコンをPCにつなげてみる。
今回僕が使ったマイコンは会社の同僚が作った開発ボードなんですが、ESP32シリーズのボードであれば同じようにできるらしいです。

それでは、やってきましょう。
下のリンクのGithubからZIP形式のファイルをダウンロードする
https://github.com/T-vK/ESP32-BLE-Keyboard

ダウンロードしたZIPファイルは以下の流れでインストールしていきます。
Arduino IDEの「スケッチ」→「ライブラリをインクルード」→「.ZIP形式のライブラリをインストール」
でインストール完了します。

インストールを終えると「ファイル」の「スケッチ例」の中に「ESP32 BLE Keyboard」というスケッチが出てきますので、ここを選択するとマイコンに直ぐに書き込んで動くスケッチが出てきます。

ボードは「ESP32 Dev Module」で動かして見たら。動きました。
あとはスケッチの中身を少し変更して、音量調節していきます。

変更したあとのコードはこちら

```
/**
 * This example turns the ESP32 into a Bluetooth LE keyboard that writes the words, presses Enter, presses a media key and then Ctrl+Alt+Delete
 */
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");
  bleKeyboard.begin();
}

void loop() {
if(bleKeyboard.isConnected()) {
    Serial.println("volume up...");
    bleKeyboard.press(KEY_MEDIA_VOLUME_UP);
  
    delay(100);
    
  }
  Serial.println("Waiting 5 seconds...");
  delay(5000);
}
```

書き込んでみると、、、

いきなり音量がマックスにww
ビックリしましたが成功したようですw

##参考
ESP32をBluetoothキーボードやマウスにする┓
http://neocat.hatenablog.com/entry/2019/09/28/151947
Sidekiqの遅延実行エクステンション [翻訳]

# Sidekiqの遅延実行エクステンション

最終更新 2019/09/03 編集者 Mike Perham

遅延実行エクステンションを使えば非同期実行を非常に簡単かつシンプルに実装することができます。
デフォルトでは、すべてのクラスメソッドとActionMailerを非同期に実行することができます。

Sidekiq 5+からこの機能はデフォルトで無効化されています。有効にする場合は `Sidekiq::Extensions.enable_delay!` を使用してください。

## ActionMailer

メールを非同期に送信するには `delay` を使ってください。日時を指定して送る場合は `delay_for(interval)` または `delay_until(time)` を利用してください。

```ruby
UserMailer.delay.welcome_email(@user.id)
UserMailer.delay_for(5.days).find_more_friends_email(@user.id)
UserMailer.delay_until(5.days.from_now).find_more_friends_email(@user.id)
```

**上記のメソッドの引数にオブジェクトのインスタンスを渡すことは避けてください。** 代わりに、[ベストプラクティス](https://github.com/mperham/sidekiq/wiki/Best-Practices)に従って、オブジェクトのIDを渡しメソッド内で再度インスタンス化を行ってください。これは


## ActiveRecord

ActiveRecordを使って実装した任意のメソッド呼び出しを非同期に実行するためには、`delay`、`delay_for(interval)`、`delay_until(time)`のいずれかを使用してください。

```ruby
ser.delay.delete_old_users('some', 'params')
User.delay_for(2.weeks).whatever
User.delay_until(2.weeks.from_now).whatever
```

**レコードのインスタンスから上記の遅延実行メソッドを呼ぶことは可能な限り避けてください。** そうしないと、古くなったオブジェクトの状態がRedisに保存されてしまい、stale data問題を引き起こしてしまう可能性があります。

## クラスメソッド

どのようなクラスメソッドも上記の方法で遅延実行することができます。

```ruby
MyClass.delay.some_method(1, 'bob', true)
```

メソッドの引数は単純なものだけにし、複雑なRubyのオブジェクトを渡さないようにすることを忘れないでください。

## 高度なオプション

`.delay` に渡す引数を利用してワーカーのオプションを指定することができます。

```ruby
MyClass.delay(retry: false).some_method(1, 2, 3)
MyClass.delay(queue: 'low').some_method(1, 2, 3)
MyClass.delay_for(10.minutes, retry: false).some_method(1, 2, 3)
```

## ノート

この拡張機能には2つの欠点があります。

- この機能は引数のシリアライズにYAML形式を利用するため、複雑なRubyオブジェクトを引数として渡すとジョブのペイロードが簡単にとても大きくなってしまいます。

- この機能は `Class` にメソッドを追加します。

これらの理由から、この拡張機能はデフォルトで無効にされています。
『 LaravelDB.com 』 ER図から自動生成！！ 面倒なことは任せちゃえ！vol1

みなさんこんにちは 
[ジーズアカデミー 主席講師 山崎ですm(_ _)m](https://gsacademy.jp/mentor/)
今回はLaravelで超高速「モックアップ作成」が可能な LaravelDB.comについてMemoしておきます。

# ◇Laravel DB.com を初めての人はこちらから


#####
**YouTubeにて解説**

https://youtu.be/sHt5v4XOWbc


[**初めての人はこちらの記事から>>** ]
https://qiita.com/daisu_yamazaki/items/9f0dd73553367f8077f0



**『Laravel Migrationファイル生成』 『クエリービルダー生成』 機能**は、~~まだβ版ですが~~ ER図で設計したあとにベーステンプレートのMigrationを作成、QueryBuilderだけでも、全て書かなくて良いので捗るはずです。
※アプリ名はLaravelに寄せたので変更しました。

#新機能 「ER図から Query Builder 」も生成
外部キーの制約どおりに生成するようにしています。
（PWA CacheAPIが使用されているのでキャッシュで以前の画面が出る人もいるかも知れません。）
<img width="845" alt="2019_11_03_233247_create_t_company_users_table_php.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104530/cff33057-2b44-448e-6a4b-3fe5c72ee580.jpeg">

※ 外部キー制約がある場合、Migrationの順番が大事です。
####★重要参考URL：[【メモ】Laravelマイグレーションで外部キー制約をつけるときに気をつけること](https://qiita.com/Yorinton/items/0e388b458ccaee1d3c08)


#サイトデザインは映画マトリックスのような雰囲気 
<img width="800" alt="en.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104530/44a89fe7-10e9-785c-19c7-1fc6c691db0a.jpeg">
 リンク： [ https://laraveldb.com ](https://laraveldb.com) 




#ER図をかいたら保存！！

<img width="1028" alt="ER_diagram_Quick.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104530/c3070d6e-0692-66c0-842e-b08393a25d77.jpeg">

#作成したER図はCloud（Firebase） or ブラウザに保存

<img width="800" alt="ER_diagram_Quick.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104530/2185b7b4-95dc-8194-5132-dc5bba485204.jpeg">
 リンク： [ https://LaravelDB.com/ ](https://laraveldb.com) 

#リレーション機能

今まで、外部キー制約の機能のみ実装されていましたが 2020年03月04日から「 リレーション（JOIN） 」が生成できるようになりました。

<img width="1000" alt="LaravelDB_com_Editor_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104530/188960d9-8830-227b-97d0-cb1709785eab.png">
※外部キーは非推奨としています。Migrationの実行順でエラーになるため


#Migrationファイル or CRUDコードどちらも生成可能です。Migrationの場合はテーブル分のファイルがZip圧縮されて全てのMigrationファイルが作成されます。
中身はみなさんご存知の以下のようなCODEです。

<img width="724" alt="2019_10_25_162015_create_gs_user_table_table_php.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104530/94c00e29-ee33-78dc-560b-d773571b1c6b.jpeg">



##◇使用している技術

- HTML / CSS / Vanilla JS
- PWA (ChcheAPI, ServiceWorker, Manifest)
- Firebase / WWWSQLDesigner
- PHP7.2 / Laravel5.5（Migration検証） / MAMP
- ツール： PHPStorm、GoogleChrome


#こんなに早くできたのは各種APIやOSSのおかげ

- API（Firebase）を利用できたこと
- WWW SQL Designer を利用できたこと（１日コード眺めるくらいでどのような事をやってるかわかったので、必要な修正には時間がかからなかった。）
- Laravelを少し前に触ってたので、Migrationのドキュメントを読んでいた。そのため、全てではないがER図から生成できるカラムや記法等の調査にも時間がかからなかった。

開発の進めかたですが、個人としては**「アウトプット・ファースト」**を最優先にしています。
大体いつもα版（目的を満たす機能、動作ができてる最低限の状態）を一週間と決めて開発しています。
そこを軸に、大事な目的にあった機能を先行して作り込み、機能動作優先、見た目など細かい部分などはだいぶ見ないで進めました。現時点の進捗としては満足してます。いつもデザインはリリース後に調整していくので、このスピード感を出すためには、この方法が良いと思っています（まずは動くが優先、モチベーションも保ちやすい）。
**少しでもこのツールが役に立てば幸いです。**
Sidekiqの時間指定ジョブ [翻訳]

# Sidekiqの時間指定ジョブ

最終更新 2018/02/22 編集者 Mike Perham

Sidekiqにはジョブが実行される日時を指定する機能があります。通常の `perform_async(*args)` に代わって `perform_in(interval, *args)` または `perform_at(timestamp, *args)` を使ってください。

```ruby
MyWorker.perform_in(3.hours, 'mike', 1)
MyWorker.perform_at(3.hours.from_now, 'mike', 1)
```

この機能を使えば、「サインアップの三時間後にメールを送信する」といった機能を簡単に実現することができます。
過去の日時を指定されたジョブは即座に実行されます。

## タイムゾーン

Sidekiqのスケジューリング機能はタイムゾーンに非依存です。Sidekiqはタイムゾーン引数に対して `.to_f` をコールすることでタイムゾーンに関する混乱を回避しています。
さらに詳細については[このイシュー](https://github.com/mperham/sidekiq/issues/1689)を読んでください。

## 新しいジョブをチェックする

Sidekiqのスケジューラーは秒単位の精度で**動きません**。スケジュールされたジョブのチェック間隔はデフォルトで**およそ**5秒毎です。（Sidekiq 5.1より前は15秒でした）
このインターバルは設定で変更できます。

```ruby
Sidekiq.configure_server do |config|
  config.average_scheduled_poll_interval = 15
end
```

## 定期実行ジョブ

Sidekiq Enterpriseは[定期実行ジョブ](https://github.com/mperham/sidekiq/wiki/Ent-Periodic-Jobs)をサポートしています。その他のサードパーティgemもcronに似た機能を提供しています。
【備忘録】Basic認証設定方法

Basic認証覚えたてホヤホヤのところで1日寝たら忘れてしまう人間なので不安が残った。
立て続けに設定する機会があったので忘れないようにメモる。

# 概要
検証環境などを一時的に関係者以外閲覧できないようにするための簡単な認証方法のひとつ。
簡単すぎる代償にセキュリティ面で問題があるらしいのでその点は頭の隅に入れておく。

# 設定方法

## Basic認証の下準備
```html
sudo su -
cd /etc/nginx/mod.d/
```
/etc配下のnginxディレクトリにはnginxの設定ファイルが格納されている。

```html
cp wp.commonset{,.bak-yyyy-mm-dd}
```
編集前にバックアップを取っておく。大事な意識。

```html
vim wp.commonset
------------------------------------------------------
[編集画面]
# auth_basic    "Secret Area";
# auth_basic_user_file  "/var/www/vhosts/.htpasswd";
↓
  auth_basic    "Secret Area";
  auth_basic_user_file  "/var/www/vhosts/.htpasswd";
```
上記のコメントアウトを外す。
こうすることで、これから.htpasswdに記載する情報を入力した場合にのみBasic認証を通り該当ページを閲覧可能になる。

## IDとPassの作成
生成したIDと各種パスワードは控えておく。

[**.htaccess による認証用パスワード暗号化ツール**](http://www.luft.co.jp/cgi/htpasswd.php)
Basic認証用のパスワードを暗号化してくれるサイト。
IDは適当で自分がわかるものを考える。
<img width="657" alt="スクリーンショット 2019-10-26 1.25.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452653/231f22e8-7768-a9a6-e10a-a306d80e53d6.png">
入力して生成後ちょっと下にいくと
<img width="649" alt="スクリーンショット 2019-10-26 1.26.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452653/463ed156-d524-2c0b-9fce-ba0b07485c85.png">
作ってもらえる。便利。

[**Password Generator**](http://www.graviness.com/temp/pw_creator/)
Passwordは以下の自動生成ツールを使用。
文字数１６文字にチェックを入れてGenerateし好きなものを選ぶ。

```html
vim /var/www/vhosts/.htpasswd
-------------------------------
[編集画面]
hogehoge:sK9MKFtk8yqkc
```
先ほど生成した暗号化されたIDとパスワードを.htpasswdファイルに記入。

## nginxの確認

```html
nginx -t
```
nginxの構文チェック

```html
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```
こんな感じで返ってきたらOK

```html
systemctl restart nginx
```
nginxを再起動する

## 番外編：WPの自動アップグレードを無効化
サーバ上でWordPressを動かしている場合
`wp-config.php`を開き

```html
define('AUTOMATIC_UPDATER_DISABLED', true);
```
をファイルの最下部に追記。
自動アップグレードを無効化して、「突然プラグインが使えなくなった」など今後起こる可能性があるトラブルを事前に潰しておく。

## Basic認証が動いているか確認
検証環境のURLにアクセスし、Basic認証のログイン画面が表示されるか確認する。
IDとPassword Generatorで選んだ１６文字のパスワードを入力してログインできれば完了。
Jobschedulerメモ

#Jobschedulerとは
[Wiki](https://ja.wikipedia.org/wiki/JobScheduler)

#公式
https://www.sos-berlin.com/

#基本的な３要素
## JOE
 いわゆるジョブのエディター
## Jobscheduler
 JOEで定義したJOBやJOBCHEINを起動する命令を出すサーバプロセス。
 WebUIを持つ。
## JOCコックピット
 新世代のWebUI。このサーバプロセスで新世代のRESTを受け付ける。
 各JobschedulerへIFする。
### 所感
WebUIをJobschedulerおよびJOCで持っているが、
Jobscheduler側はかなりのレガシー感がある。
二者の違いは不明。JOCはより近代的な見た目。

#download
## 最新
https://www.sos-berlin.com/jobscheduler-downloads

## アーカイブ
https://sourceforge.net/projects/jobscheduler/files/

#RESTAPI

#ナレッジベース（公式のHowToガイドサイト）
https://kb.sos-berlin.com/display/PKB/Product+Knowledge+Base+Home

##所感
リンク切れが散見される。。。

#REST API
APIが旧世代のものと新世代のもので分かれている。

##旧世代：XMLベースとしたREST
　https://kb.sos-berlin.com/display/PKB/JobScheduler+-+REST+Web+Service

  さすがに今からXMLベースは厳しい。新旧の違いは精査していない。

##新世代：JSONベースとしたREST
　https://kb.sos-berlin.com/display/PKB/JOC+Cockpit+-+REST+Web+Service

#YADE
まったく未調査だったが一応。
同社の別製品。ファイル転送を主に担うようだ。

##公式
https://www.sos-berlin.com/yade-features

#全体を通して
##Pros
JOCが分かりやすい。
JOEも比較的理解しやすい。

##Cons
###文字コードがLatin1
日本語が扱えない。日本語がどこかに登場すると、マルチバイト文字列に関するエラーが出る。これが痛すぎる。
UTF-8化は、ver2.0で対応予定となっているが、どこまでやる気かは不透明だ。

#### 参考
* [Unicodeのサポートについて](https://kb.sos-berlin.com/display/PKB/Support+of+Unicode+with+UTF-8+encoding+for+languages+such+as+Japanese+in+addition+to+English)
* [JOEおよびその他のJobSchedulerコンポーネントのローカライズバージョンを作成する方法](https://kb.sos-berlin.com/display/PKB/How+to+make+a+localized+version+of+JOE+and+other+JobScheduler+components)
* [イシュー](https://change.sos-berlin.com/browse/JS-1374)

### ところどころ粗さが目に付く
ドキュメントのリンク切れやUFT-8化を率先してやろうとしない姿勢などは少し目に付く。有償サポートではちゃんと対応してくれるのだろうと思われる。

#困っていること。
JOBCHEINを起動したときに、ずっと未完了のステータスのままになってしまう事象が起きている。。。サーバプロセスを再起動しても変わらず。内部DBを覗いてもおおむね完了しているように見受けられる。

解消するパターンは、他のJOBが起動されて完了すると、未完了ステータスのJOBのステータスが最新化される。

事象認識に誤りがあるかもしれない。
実務でよく使うGitコマンド一覧

```
git init
git clone
git remote
git pull
git fetch
git merge
git checkout
git branch
git status
git diff
git add
git commit
git log
git push
git reset
git rebase
git cherry-pick
git stash
git revert
git rm
```

## 追記: git-remind

[コミット忘れ防止コマンド git-remind](https://qiita.com/suin/items/4932dd3f822ea8da8a01)

## 【ペチオブ】実務で使うGit勉強会

[株式会社ミライトデザイン](https://miraito-inc.co.jp)が主催するエンジニア向けオブジェクト指向ワーキンググループ「[ぺちオブ](https://phper-oop.connpass.com/presentation)」の【ペチオブ】実務で使うGit会にて学ばせていただきました。

- connpassイベントページ
  - https://phper-oop.connpass.com/event/150753
- YouTubeLive
  - https://www.youtube.com/watch?v=7_JtLzI9iqE&feature=youtu.be
- 資料(YouTube)
  - https://youtu.be/PprW1xE6JfI
- 資料(SpeakerDeck)
  - https://speakerdeck.com/yukimasa123/shi-wu-deshi-erugitkomando
PHPキャッチアップ～ToDoリスト作成～

## なんでわざわざ公開したの？
PHPを触りくらい使えるようにとキャッチアップ中だからです。
とりあえずアウトプットして皆様のご意見を戴かないと、自己研鑽できないからです。
お決まりのToDoリストくらいは作れるぜってアピールしたいからです。

PHPらしいコードってのがイマイチ理解できていないので、有識者様、どうか私奴に教えてください。


## コード

```php:ToDo.php
<?php
    $pdo =  new PDO('mysql:dbname=php;host=localhost','root','');
    
    if(isset($_POST['submit'])){
        $content = $_POST['content_name'];

        $sql = "INSERT INTO doing (content) VALUES ('$content')";
        $insert = $pdo->query($sql);
    }


    $sql = 'SELECT content FROM doing';
    $results = $pdo->query($sql);
?>

<html>
    <head>
        <meta charset="UTF-8">
        <Title>ToDoリスト</Title>
    </head>
    <body>
        <h1>ToDoリスト</h1>
        <form action="ToDo.php" method="post">
            <ul>
                <li><span>タスク名</span><input type="text" name="content_name">
                <span><input type="submit" name="submit"></span></li>
            </ul>
        <ul>
        <ul>
            <?php 
                foreach($results as $result){
                    echo "<li>";
                    echo $result['content'];
                    echo "</li>";
                };
            ?>
        </ul>
        </ul>
    </body>
</html>
```

DBは下記の通り作成しました

```sql:doing.sql
CREATE DATABASE php;

CREATE TABLE doing (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    content VARCHAR(100) NOT NULL
);
```
[ESP32]LINEbotからロボットアームを動かす/LINEbot編

※本記事は[[ESP32]LINEbotからロボットアームを動かす/ロボットアーム編](https://qiita.com/ynntech/items/0fe17c2ec33c83519b11)と合わせて御覧ください。

#1.概要
LINEbotからメッセージを送信して、ESP32を動かしたいお気持ち。そしてロボットアームを動かしてリモコンのボタンを押したいお気持ち。
[ロボットアーム側についてはこちらをご覧ください](https://qiita.com/ynntech/items/0fe17c2ec33c83519b11)

仕組みはこんな感じ。
通常状態では何も動かない。
![スライド1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288721/3f19298c-b7a5-3474-d248-cbf587a8a497.jpeg)
botに『冷房』と送信されたときだけロボートアームが動く～
![スライド2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288721/51bce6b7-43e8-2147-3e0b-9a1e789efee2.jpeg)

ESP32側のコードは上記のリンクからどうぞ。

#2.LINEbotのコード


```python:linebot.py
from flask import Flask, request, abort

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)
import os, json 
import time
import numpy as np

app = Flask(__name__)

line_bot_api = LineBotApi('access_token') 
handler = WebhookHandler('channel_secret') 

@app.route("/callback", methods=['POST'])
def callback():

    signature = request.headers['X-Line-Signature']

    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)
 
    try: 
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'


global state
state=0
@app.route("/ToI", methods=["GET"])
def handle_get_request():
    global state
    
    tmp = state
    state = 0
    print("accept_"+str(tmp))
    line_bot_api.push_message("user_id",TextSendMessage(text="accept_"+str(tmp)))
    
    return str(tmp)


dry = ["今日じめじめしとるなぁ、除湿しとくな","除湿まかしとき！"]
cool = ["ええよー、冷しとくで！","今日暑かったもんなぁ、しっかり冷やしとくわ！"]
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    text = event.message.text
    global state
    
    
    if text == "ToI! 除湿して!":
        line_bot_api.reply_message(event.reply_token,TextSendMessage(text=dry[np.random.randint(2)]))
        state = 1

    elif text == "ToI! 冷房して!":
        line_bot_api.reply_message(event.reply_token,TextSendMessage(text=cool[np.random.randint(2)]))
        state = 2

    else:
        line_bot_api.reply_message(event.reply_token,TextSendMessage(text=text))


if __name__ == "__main__":
    port = int(os.environ.get('PORT', 8000))
    app.run(host ='0.0.0.0',port = port)

```

LINEbot側は大体こんな感じ。
ESP32から"/ToI"あてにGETリクエストが飛んできて、現在のstateを返している。ユーザーから命令があると、stateが変更され、ロボットアームが動く。