2020年10月24日土曜日

【計画編】原木椎茸の収穫のタイミングで通知してくれるものを作ってみる。

小さな農園でも取り組めるスマート農業の可能性を探るべく、まず第一歩として「原木椎茸の収穫のタイミングで通知してくれるもの」を作ろうと思っています。

はじめは Google Cloud AutoML Vision という機械学習による画像認識AIにより実現してみようかと思っていたのですが、ふと積算温度(と湿度)からも収穫のタイミングが分かるのではないかという気がしてきたので、そちらの方も並行して進めてみることにしました。


作るの自体は積算温度の方が簡単そうです。こちらはモノのインターネット、IoTを用いたスマート化になりますね。↓のリンク先ページによさそうな方法が書いてありました。

暑くて眠れないので温度を自動記録するNature Remo API/GASアプリを作った

Nature Remo mini のモデルだと湿度は測れないみたいですね。

温度を測定してリアルタイムでスプレッドシートに書き込みができたら、あとはスプレッドシート内で積算温度の計算を行い、GAS(Google Apps Script)でLINE通知という流れですね。Nature Remo を購入すればすぐにでもできそう!


一方、Cloud AutoML Vision を使うには、インターバル撮影をするAndroidアプリの作成、教師用画像データの準備、予測モデルの作成・評価、組み込み用のアプリ作成と、やったことのない工程だらけなのでただいま勉強中(主にKotlin)。今椎茸シーズン中の完成を目指しています。


共通ではビニールハウス内にWi-Fi環境を整えたり、半野外の環境で機器が壊れないように工夫が必要になりますね。電源はあります。

Wi-Fiに関してはとりあえずデータ専用SIMでテザリング or モバイルWi-Fiルーターという形を検討。SIMは今のところ HIS Mobile のビタッ!プラン(データ通信のみ)が100MB(180円)~で上限の切替設定ができるのでよさそう。積算温度の場合はほとんど通信容量を使わないですが、Cloud AutoML Visionの方は画像を送るのである程度通信容量が必要になりますからね。


ということで、将来的に何か役立つものになっていくのかいかないのか全く分かりませんが、楽しく取り組みたいと思います。

ちなみにテスト開始から半月くらいが経った「気象データを加工してLINE botで配信してみるテスト。」は撒水等々の判断に意外と役立っています。あと猫たちの寝床選択の基準となる気温がだいたい把握できるようになってきました(笑)。



Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。

2020年10月21日水曜日

【自作】ビニールハウス補修用ハシゴ

いつの間にかビニールハウスが破けていたので補修しました。

原木椎茸の施設栽培では保温や保湿、また逆に気温を落としたり湿気を飛ばしたりということを細かくやりますのでハウスに穴があいていると調整がしづらくなってしまうのですね。

今回はまたそこそこ盛大な破れ方で内側から補修テープを貼るだけでは心許なかったので、内側から別のビニールをあてて外側から補修テープを貼る形にしました。


その際活躍したのが下の写真のハシゴ。ビニールハウスのパイプを使って作ってみました。


パイプの接続はパイプクロス。

25mmのパイプを使っていますが、強度としては60kg程度の人が登って作業可能でした。若干たわむ感じはあるので下で支えている人はいた方がいい気がします。

調べると専用のハシゴも販売しているのですがなかなかのお値段で…。日常的に使うのであればしっかりした製品の方がよいかと思いますが、うちの場合などは使用頻度も高くないですからね。

工作にかかった時間30分、費用6千円くらいでした。冬場は風も強くなりますし、ビニールハウスの補修は早め早めが安心でございます。




Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。

2020年10月19日月曜日

小さく始めるスマート農業

スマート農業の本流は大規模農園ではありますが、一方でさわのような家族経営規模の小さな農園はいかに対応していくべきなのか。今のうちにしっかり考えておきたいところです。

しかしまぁ、考えているだけでもよく分からないのでまずは小さくできる取り組みから始めて見ようかと。

ということで、まずは身近なところで「Google Cloud AutoML Vision」を使った椎茸の画像認識を試してみることにしました。

ハウス内に設置したスマートフォン(の自作Androidアプリ)でインターバル撮影→収穫のタイミングで通知をしてくれる、みたいなものをイメージしています。



とりあえず、今の椎茸シーズン中の完成を目指して参考書を買ってポチポチやっています。



(参考書は紙の方が便利ですね。)


Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。

2020年10月5日月曜日

秋2020

2020年も、もう秋ですね。

本当にまさかまさかといった状況が続きますが、こういった時にこそしっかりと計画を練り、脊髄反射せず、確実なものを積み重ねていけたらと思うところです。



順調なら、稲刈りは今週終わりそうです。コンバイン、泥濘む足場で頑張ってくれています。



原木椎茸も少しずつ発生を開始しました。朝晩冷え込むようなになってきたので良い感じに育ってくれています。


***

最近、ちょこちょこ自作ツールを作り業務改善・効率化を進めております。まぁ、一足飛びにとはいきませんがじわじわ地道にいきたいと思います。


***

そんなこんなで色々ですが、積ん読が溜まってきたので秋は読書に勤しみます。





そういえば、しばらくやめていたAudibleもラインナップが充実してきたようなのでまた会員になろうかと思案中です。


Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。

2020年10月4日日曜日

気象データを加工してLINE botで配信してみるテスト。

Webスクレイピングで自動取得した気象データを加工してLINE botで(自分に)配信するようにしてみました。いつものごとくGAS(Google Apps Script)を使っています。



データはひとまず、前日、過去7日間、過去30日間の気温、最高気温、最低気温、合計降水量、湿度、風速、日照時間を用意。

データの種類は要検討ですが、栽培にあたっての判断の補助になればいいなと思っています。現状、原木椎茸の栽培では撒水や換気の目安にはできそうです。


***

LINE botでのプッシュ通知や気象データの取得のGASはすでに作ったことがありますので、スクリプトは概ねのところ切り貼りで作れました。

(コード省略)

データの加工については二次元配列から一次元配列への変換や、配列内の数値の操作、小数点以下の桁数の指定あたりができれば自動化のした上でそれなりにできそうです。



あとは後々統計的な処理が必要になることを見越して学び直しを。


・関連投稿
GASで簡単な予定通知LINE botを作ってみる。

気象データをGASのWebスクレイピングで自動取得する。

原木椎茸の有効積算温度の計算


Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。

2020年9月26日土曜日

Gmailで定期的にメールを自動送信する。

通常の機能だと(たぶん)できないGmailでの定期的な自動送信。でも、GAS(Google Apps Script)を使うと簡単にできます。

さわでは直売所の売り上げ確認のために毎日空メールを送る必要があるのですが、こういう場合に便利です。


① コードの作成

「Google ドライブ」の「新規」→「その他」→「Google Apps Script」よりスクリプトファイルを作成します。コードは以下の通り。

function myFunction() {
  GmailApp.sendEmail(
    '送信先メールアドレス', 
    '件名',
    '本文'
  );
}

空メールですと「件名」「本文」はシングルコーテーション「'」で囲う形になります。


function myFunction() {
  GmailApp.sendEmail(
    '送信先メールアドレス', 
    '件名',
    '本文',
    {
      from: '送信元メールアドレス',
      name: '送信者名'
    }
  );
}

オプションでfrom(送信元メールアドレス)、name(送信者名)、cc、bccなども指定できます(詳しくはこちら参照)。

送信元メールアドレスにはGmail アカウントにエイリアスとして追加している別アドレスも指定できます(参考:Gmail の「エイリアスとして扱います」の使用方法)。




② トリガーの設定

定期的にメールを送信するためにトリガーの設定をします。

以下、例です。

「トリガーを追加」より、

・「実行する関数を選択」→ myFunction
・「実行するデプロイを選択」→ Head
・「イベントのソースを選択」→ 時間主導型
・「時間ベースのトリガーのタイプを選択」→ 日付ベースのタイマー
・「時刻を選択」→ 午前7時〜8時
・「エラー通知設定」→ 毎日通知を受け取る

適宜設定してください。


***

このメール送信のスクリプトを以前作成した「予定通知LINE bot」と組み合わせれば通知をメールで送ることもできますね。

定期業務は自動化できると時間のゆとりも作れます。うまく使っていきたいですね。




Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。

2020年9月23日水曜日

減価償却費の計算用スプレッドシート(定額法・平成19年4月1日以後取得の資産に対応)

減価償却費の計算用スプレッドシートを作ってみました。計算と減価償却資産ごとのシートの作成はGAS(Google Apps Script)で自動化しています。手作業で計算するととても面倒ですからね。

平成19年4月1日以後に取得した減価償却資産を定額法を用いて計算する場合に使うことができます。

スプレッドシートやエクセルでは減価償却費の計算(定額法)をするSLN関数が用意されていますが、これはそのまま使うとずれるのですね。なので、計算式は自分で作る必要があります。


① スプレッドシートのダウンロード




PCでGoogleアカウントにログインした状態で、リンク先にて「ファイル」から「コピーを作成」で編集可能なファイルが作成されます。エクセルで使いたい方はファイル作成後にMicrosoft Excel(.xlsx)形式でダウンロードすれば使えると思います。

スプレッドシートには「参照」「減価償却費データ」「減価償却資産リスト」「1サンプル」というシートが含まれています。

次にGASのコードの作成を行います。


② コードの作成

スプレッドシートの「ツール」→「スクリプト エディタ」からスクリプトファイルを作成します。コードは以下の通りです。

function depreciableAssets() {
  var spreadsheet = SpreadsheetApp.getActive();
  var sheet = spreadsheet.getSheetByName("減価償却資産リスト");
  var lastRow = sheet.getLastRow();
  
  var num = sheet.getRange(lastRow, 1).getValue(); //管理番号 
  var name = sheet.getRange(lastRow, 2).getValue(); //資産名称
  var acc = sheet.getRange(lastRow, 3).getValue(); //勘定科目
  var date = sheet.getRange(lastRow, 4).getValue(); //取得年月日  
  var year = Utilities.formatDate(date, "JST", "yyyy"); //取得年
  var month = Utilities.formatDate(date, "JST", "MM"); //取得月
  var life = sheet.getRange(lastRow, 5).getValue(); //耐用年数
  var ratio = sheet.getRange(lastRow, 6).getValue(); //事業専用割合
  var qty = sheet.getRange(lastRow, 7).getValue(); //数量
  var price = sheet.getRange(lastRow, 8).getValue(); //単価
  
  var depreciation = qty * price; //取得価格(償却費)
  var rate = "=VLOOKUP(E" + lastRow + ",'参照'!$A$2:$B$100,2,FALSE)"; //償却率
  var expense = depreciation * ratio; //経費算入額
  var months = 13 - month; //初年償却月数
  
  sheet.getRange(lastRow, 9).setValue(depreciation);
  sheet.getRange(lastRow, 10).setValue(rate);
  sheet.getRange(lastRow, 11).setValue(expense);
  sheet.getRange(lastRow, 12).setValue(months);
  
  rate = sheet.getRange(lastRow, 10).getValue();
    
  spreadsheet.insertSheet(num + name);
  var newSheet = spreadsheet.getSheetByName(num + name);

  newSheet.getRange(1, 1).setValue("管理番号");
  newSheet.getRange(1, 1).setBackground("#d9ead3");
  newSheet.getRange(1, 2).setValue(num);
  newSheet.getRange(1, 1, 1, 2).setBorder(true, true, true, true, true, false);
  
  newSheet.getRange(3, 1).setValue("資産名称");
  newSheet.getRange(3, 1).setBackground("#d9ead3");
  newSheet.getRange(3, 2).setValue(name);
  newSheet.getRange(4, 1).setValue("勘定科目");
  newSheet.getRange(4, 1).setBackground("#d9ead3");
  newSheet.getRange(4, 2).setValue(acc);
  newSheet.getRange(3, 3).setValue("取得年月日");
  newSheet.getRange(3, 3).setBackground("#d9ead3");
  newSheet.getRange(3, 4).setValue(date);
  newSheet.getRange(4, 3).setValue("耐用年数");
  newSheet.getRange(4, 3).setBackground("#d9ead3");
  newSheet.getRange(4, 4).setValue(life);
  newSheet.getRange(3, 5).setValue("事業専用割合");
  newSheet.getRange(3, 5).setBackground("#d9ead3");
  newSheet.getRange(3, 6).setValue(ratio);
  newSheet.getRange(3, 6).setNumberFormat("0%");
  newSheet.getRange(4, 5).setValue("償却率");
  newSheet.getRange(4, 5).setBackground("#d9ead3");
  newSheet.getRange(4, 6).setValue(rate);
  newSheet.getRange(4, 6).setNumberFormat("0.000");
  newSheet.getRange(3, 1, 2, 6).setBorder(true, true, true, true, true, true);

  newSheet.getRange(6, 1).setValue("経過年数");
  newSheet.getRange(7, 1).setValue("1");
  newSheet.getRange(6, 2).setValue("対象年");
  newSheet.getRange(7, 2).setValue(year);
  newSheet.getRange(6, 3).setValue("数量");
  newSheet.getRange(7, 3).setValue(qty);
  newSheet.getRange(6, 4).setValue("単価");
  newSheet.getRange(7, 4).setValue(price);
  newSheet.getRange(7, 4).setNumberFormat("[$¥-411]#,##0");
  newSheet.getRange(6, 5).setValue('="取得価格"&CHAR(10)&"(償却費)"');
  newSheet.getRange(7, 5).setValue(depreciation);
  newSheet.getRange(7, 5).setNumberFormat("[$¥-411]#,##0");
  newSheet.getRange(6, 6).setValue("経費算入額");
  newSheet.getRange(7, 6).setValue(expense);
  newSheet.getRange(7, 6).setNumberFormat("[$¥-411]#,##0");
  newSheet.getRange(6, 7).setValue("初年償却月数");
  newSheet.getRange(7, 7).setValue(months);
  newSheet.getRange(6, 8).setValue('="対象年"&CHAR(10)&"経費算入額"');
  newSheet.getRange(7, 8).setValue("=ROUND((F7*F4)*G7/12,0)");
  newSheet.getRange(7, 8).setNumberFormat("[$¥-411]#,##0");
  newSheet.getRange(6, 9).setValue('="対象年末"&CHAR(10)&"未償却残高"');
  newSheet.getRange(7, 9).setValue("=F7-H7");
  newSheet.getRange(7, 9).setNumberFormat("[$¥-411]#,##0");
  newSheet.getRange(6, 10).setValue("備考");   
  for(var i = 1; i < 11; i++){
    newSheet.getRange(6, i).setBackground("#d9ead3");
    newSheet.getRange(6, i).setHorizontalAlignment("center");
  }
  newSheet.getRange(6, 1, 1, 10).setBorder(true, true, true, true, true, false);

  newSheet.getRange(8, 1).setValue("=$A7+1"); //経過年数
  newSheet.getRange(8, 2).setValue("=$B7+1"); //対象年
  newSheet.getRange(8, 8).setValue("=ROUND(IF((I7-(F7*F4))<=0,(((F7*F4)+(I7-(F7*F4))))-1,(F7*F4)),0)"); //対象年経費算入額
  newSheet.getRange(8, 8).setNumberFormat("[$¥-411]#,##0");
  newSheet.getRange(8, 9).setValue("=IF((I7-H8)<=0, 1,(I7-H8))"); //対象年末未償却残高
  newSheet.getRange(8, 9).setNumberFormat("[$¥-411]#,##0");
  newSheet.getRange(7, 1, 1, 10).setBorder(true, true, false, true, true, false);

  life = life + 1;
  
  var row1 = 8;
  var row2 = 7;

  for(var j = 1; j < life; j++){
    newSheet.getRange(row1, 1).setValue("=A" + row2 + "+1"); //経過年数
    newSheet.getRange(row1, 2).setValue("=B" + row2 + "+1"); //対象年
    newSheet.getRange(row1, 8).setValue("=ROUND(IF((I" + row2 + "-(F7*F4))<=0,(((F7*F4)+(I" + row2 + "-(F7*F4))))-1,(F7*F4)),0)"); //対象年経費算入額
    newSheet.getRange(row1, 8).setNumberFormat("[$¥-411]#,##0");
    newSheet.getRange(row1, 9).setValue("=IF((I" + row2 + "-H" + row1 + ")<=0, 1,(I" + row2 + "-H" + row1 + "))"); //対象年末未償却残高
    newSheet.getRange(row1, 9).setNumberFormat("[$¥-411]#,##0");
    newSheet.getRange(row1, 1, 1, 10).setBorder(true, true, true, true, true, false);
    newSheet.getRange(row1, 1, 1, 10).setBorder(true, null, null, null, null, null, "#000000", SpreadsheetApp.BorderStyle.DOTTED);
    
    if(newSheet.getRange(row1, 9).getValue() == 1){
      break;
    }
    
    row1 = row1 + 1;
    row2 = row2 + 1;
  }
  
  newSheet.protect().setWarningOnly(true); //シートの保護
  
  sheet.getRange(lastRow, 13).setValue('=IFERROR(VLOOKUP(YEAR(NOW())-1, INDIRECT(TEXTJOIN("", TRUE,A' + lastRow + ',B' + lastRow + ')&"!$B:$H"), 7,FALSE), 0)');
  sheet.getRange(lastRow, 14).setValue('=IFERROR(VLOOKUP(YEAR(NOW())-1, INDIRECT(TEXTJOIN("", TRUE,A' + lastRow + ',B' + lastRow + ')&"!$B:$I"), 8,FALSE), 1)');
  sheet.getRange(lastRow, 15).setValue('=IFERROR(VLOOKUP(YEAR(NOW()), INDIRECT(TEXTJOIN("", TRUE,A' + lastRow + ',B' + lastRow + ')&"!$B:$H"), 7,FALSE), 0)');
  sheet.getRange(lastRow, 16).setValue('=IFERROR(VLOOKUP(YEAR(NOW()), INDIRECT(TEXTJOIN("", TRUE,A' + lastRow + ',B' + lastRow + ')&"!$B:$I"), 8,FALSE), 1)');

}

まるっとコピペで使えるかと思います。

GASの初回実行時に承認が必要となります。無料のGoogleアカウントの場合「このアプリは確認されていません」というページが表示されます。ここで「安全なページに戻る」をクリックすると実行できませんので、「詳細」→「プロジェクト名(安全ではないページ)に移動」から承認します。


(もう少しさっぱりしたコードにできないものかと思いつつ、まぁとりあえず動けばいいでしょう。笑)


③ スプレッドシートの使い方

・「参照」シート

他シートからの参照用に定額法の償却率と勘定科目が入っています。はじめにD2:D11の範囲に必要となる勘定科目を入力してください(必要ないものは消してください)。


・「減価償却費データ」シート

自動で勘定科目ごとの資産数、償却費、経費算入額、前年経費算入額、前年末未償却残高、当年経費算入額、当年末未償却残高が表示されます。入力はありません。


・「減価償却資産リスト」シート

管理番号、資産名称、勘定科目、取得年月日、耐用年数、事業専用割合、数量、単価の8項目の入力をします(黄色のセル)。数字は全て半角です。勘定科目は「参照」シートに記載した科目をプルダウン リストから選択します。

8項目入力後、先ほど作成したGASのdepreciableAssets関数を実行します。「減価償却資産リスト」シートの取得価格(償却費)、償却率、経費算入額、初年償却月数、前年経費算入額、前年末未償却残高、当年経費算入額、当年末未償却残高が入力され、管理番号と資産名称をくっつけた名前で個別のシートが作成されます。「1サンプル」シートが例になります。

個別のシートでは耐用年数に応じて残存価格1円になるまでの対象年経費算入額と対象年末未償却残高が表示されます。



④ 注意点

depreciableAssets関数は新しい減価償却資産の入力毎に実行してください。複数資産をまとめて実行することはできません。

管理番号は重複しないように気をつけてください。

減価償却資産を除去する場合には、個別のシートと「減価償却資産リスト」シートの対象の行を削除してください。

自分でも使っていますので計算ミス等は無いように注意して作っていますが、実際にお使いになる際にはご自身でよくご確認ください。


***

自営業にとって毎年の確定申告は経営状態を把握する良い機会ですからね。正確に、かつできる限り省力化していきたいところです。

今年は領収証等も都度フォームで入力→自動で合計残高試算表作成という形にしたので、年明けとともに申告の準備が整う予定です。

(そううまくいくだろうか。笑)