コロナ対策の換気メモ

厚生労働省の換気に関する資料をみると一人辺り30立米/hが取り敢えずの推奨値。なお、ゼロリスクということではなく一般事業者がとれる現実的な水準としての値。

同時に10人入れるとして300立米/hの換気。遮光をしたエリアで紫外線使えば余裕をもって殺菌できる量。その3倍ぐらいになったら水銀灯タイプの電球だとオゾンがですぎる気がする。手持ちの電球だと600立米/hぐらいが換気がわりの殺菌ができる現実ライン?LEDタイプだとオゾン発生する波長しぼれている模様なのでオゾンを気にしないで出力あげられる?

なお、一時的な来客増ならば自然/機械換気を積みませる。なのでエアコンの效きが悪くなることを受け入れられる範囲で同時入店人数を増やせる。うちは夏の昼はエアコンいっぱいいっぱいなので+50%も厳しいけどそれ以外の時間は+100%ぐらいは余力あり。

LEDは単品だと電球より高いけれどもソケットが不要になったりするので値段的にはトントンぐらいになると思う。部品が減るので組み立てはむしろ楽?最近聞く人体に影響のない波長のは売っているのを見たことはないけど250nmぐらいのはAliとかで売っている。今のところ供給も安定してそう。

店の消毒をオゾンで行う検討メモ(クライミングジム)

目的

表面は紫外線で殺菌できるけど(ガバとか)裏面は殺菌できない。裏面もある程度手軽に殺菌したい。

要件

  • 毎日1回裏面もウィルス1/10ぐらいまで殺菌
  • 色々腐食されそうだけどほとんどのものはDIYで修復できるのでもろもろの寿命が短くなるのはこのさい無視してOK
  • 殺菌中は原則として店内に立ち入らないけど緊急で入れる程度の濃度で押さえておく

要件を満たす条件

  • 1日1回やればよいので2330-0930の10時間が稼働時間(換気込み)
  • 緊急時の立ち入りを考え濃度は0.3ppm程度まで(参照
  • CT60(ppm*min=60)で1/10から1/100程度らしい(参照)のでCT60を目指す(表現あっている?)

計算

換気等を考え殺菌可能時間は9時間程度。オゾン発生させて濃度が安定するまで1時間程度らしいので8時間=720分。目標値の60にするには60/720で0.083ppm程度。

こちらに目的の濃度にするために必要なオゾン発生量の式が乗っているがそれに基づいて計算する。うちの店の体積が約900m3(幅7.5,奥行22,高さ5.5)なので900÷0.083÷2.14で160mg/h~320mg/h程度。薄まらない環境(発生量*0.5が安定する平均濃度)で計算しても濃度は0.17ppm程度で緊急時の立ち入りもできる。8wのオゾンランプ(参考:極光電気)1本で16mg/hなので20本あれば足りる。

AdoptOpenJDKをyumインストール(CentOS7)

CentOSにAdoptOpenJDKのでメモ

環境

  • CentOS Linux release 7.6.1810 (Core)
  • AdoptOpenJDK (build 11.0.3+7)

yumレポジトリ追加

基本公式の通り。(RPM installation on Centos, RHEL, or Fedoraあたり)

cat < /etc/yum.repos.d/adoptopenjdk.repo
[AdoptOpenJDK]
name=AdoptOpenJDK
baseurl=http://adoptopenjdk.jfrog.io/adoptopenjdk/rpm/centos/7/$(uname -m)
enabled=1
gpgcheck=1
gpgkey=https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public
EOF

https://adoptopenjdk.net/installation.html?variant=openjdk11&jvmVariant=hotspot#x64_linux-jdk

確認及びインストール

選択肢確認

yum list adoptopenjdk*


読み込んだプラグイン:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
base: ftp.iij.ad.jp
epel: ftp.jaist.ac.jp
extras: ftp.iij.ad.jp
nux-dextop: mirror.li.nux.ro
updates: mirror.horizon.vn
インストール済みパッケージ
adoptopenjdk-11-hotspot.x86_64 11.0.3+7-1 @AdoptOpenJDK
利用可能なパッケージ
adoptopenjdk-11-hotspot.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-11-hotspot-jre.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-11-openj9.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-11-openj9-jre.x86_64 11.0.4+11-1 AdoptOpenJDK
adoptopenjdk-12-hotspot.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-12-hotspot-jre.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-12-openj9.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-12-openj9-jre.x86_64 12.0.2+10-1 AdoptOpenJDK
adoptopenjdk-8-hotspot.x86_64 8u222_b10-1 AdoptOpenJDK
adoptopenjdk-8-hotspot-jre.x86_64 8u222_b10-1 AdoptOpenJDK
adoptopenjdk-8-openj9.x86_64 8u222_b10.openj9_0.15.1-1 AdoptOpenJDK
adoptopenjdk-8-openj9-jre.x86_64 8u222_b10.openj9_0.15.1-1 AdoptOpenJDK

インストール

好きな奴

sudo yum install adoptopenjdk-11-hotspot.x86_64

設定

sudo update-alternatives –config java

(2020/9/30追記)

最近だと多分こっち

sudo alternatives –config java

4 プログラムがあり ‘java’ を提供します。
選択 コマンド
1 java-11-openjdk.x86_64 (/usr/lib/jvm/java-11-openjdk-11.0.3.7-0.el7_6.x86_64/bin/java)
2 java-1.7.0-openjdk.x86_64 (/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/jre/bin/java)
3 java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el7_6.x86_64/jre/bin/java)
4 /usr/lib/jvm/adoptopenjdk-11-hotspot/bin/java
Enter を押して現在の選択 [+] を保持するか、選択番号を入力します:

古いAndroidとラズパイで多機能モニターを作る

目的

以前作った店の呼び出しブザーは押されたらline&メール通知を飛ばすようになっている。ただし、押した側からみるとなんの反応も無いため動作しているか不安。なので、googleクラウドで合成させた音声を古いAndroidで流すようにした。(+画面でメッセージ表示)

なお、この記事を書いている段階ではエミュレータで動いたのみ。実機は古すぎて現行のAndroidStudioで素直にビルドできていない。ビルドする方法を調べてから再挑戦。

環境

呼出側

開発環境

  • OS Centos7
  • python 3.7.0
  • the Cloud Client Libraries for the Cloud Text-to-Speech API のアルファ版(ドキュメントが古いだけかも)

想定環境

  • raspberry pi typeB
  • python 3.7.0
  • the Cloud Client Libraries for the Cloud Text-to-Speech API のアルファ版(ドキュメントが古いだけかも)

触った限り、本番環境用のraspberry pi typeBで動作はするもののCloud Text-to-Speech APIの呼出が重い。10文字程度で3秒ほど。gRPCの接続/暗号化処理周りでcpuがいっぱいいっぱいになっている模様で端末能力の問題と思う。

呼び出される側(android)

開発環境

  • android studio 3.3.1
  • SHL22
  • Androidバージョン 4.2.2
  • compileSdkVersion/targetSdkVersion 28
  • minSdkVersion 16 (開発端末がこの時代の)
  • com.nanohttpd:nanohttpd-webserver:2.1.1

想定環境

  • IS11PT
  • Androidバージョン2.3.4

端末が古すぎるので開発環境を簡単に整えられなかったので、まずは新しめ(といっても8年ぐらい前)の端末で作成。

呼出側機能

概要

googleのText-to-Speech API を呼び出して、requestでAndroid内に起動させているnanohttpsにpostをするのみ。Text-to-Speech APIはほぼドキュメントどおり。requestのファイルのところはパラメータにMIMEタイプまでしていしてやらないとAndroid側で使っっているnanohttpdでファイルをよみ込めなかったので注意。

ソース

#!/usr/bin/env python3
# coding: utf-8
import requests

from google.cloud import texttospeech

def make_voice(current_time):
    _make_voice(current_time,"少々お待ちください")

def _make_voice(current_time,msg):
    file_name = 'output.mp3'
    client = texttospeech.TextToSpeechClient()
    synthesis_input = texttospeech.types.SynthesisInput(text=msg)

    voice = texttospeech.types.VoiceSelectionParams(
        language_code='ja-JP',
        ssml_gender=texttospeech.enums.SsmlVoiceGender.NEUTRAL)
    audio_config = texttospeech.types.AudioConfig(
        audio_encoding=texttospeech.enums.AudioEncoding.MP3)

    response = client.synthesize_speech(synthesis_input, voice, audio_config)

    with open('output.mp3', 'wb') as out:
        out.write(response.audio_content)

    url = 'http://127.0.0.1:8081/msg/'
    files = {'file': (file_name,open(file_name, 'rb'),'audio/mpeg')}
    payload = {"message" :  msg}
    r = requests.post(url ,files=files ,params=payload)
    print(r)
if __name__ == '__main__':
    make_voice( '2019/02/12 固定' )

呼び出される側

概要

内部で立ち上げているnanohttpdで情報(リクエスト)を受け取って、音声再生し画面にメッセージを出しておく。ちなみに、古いバージョンで動かそうとしてたので文法が古いものから新しいものまで混在しているので適宜読み替えてください。

package jp.co.epea.monitor;

import android.media.MediaPlayer;
import android.os.Handler;
import android.widget.TextView;


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import fi.iki.elonen.NanoHTTPD;

import static fi.iki.elonen.NanoHTTPD.Response.Status.NOT_FOUND;
import static fi.iki.elonen.NanoHTTPD.Response.Status.OK;

public class MoniterServer extends NanoHTTPD {
    private Handler handler;
    private TextView textView;

    public MoniterServer(Handler handler, TextView textView) {
        super(8080);
        this.handler = handler;
        this.textView = textView;

    }

    @Override
    public Response serve(IHTTPSession session) {
        Method method = session.getMethod();
        String uri = session.getUri();

        System.out.println(method + " '" + uri + "' ");
        if (uri.equals("/msg/")) {
            final String msg = session.getParms().get("message");


            Map<String, String> files = new HashMap<>();
            try {
                session.parseBody(files);
            } catch (IOException ioe) {
                return new NanoHTTPD.Response(NOT_FOUND, MIME_HTML, "none");
            } catch (ResponseException re) {
                return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
            }

            MediaPlayer player = new MediaPlayer();
            try {
                player.setDataSource(files.get("file"));
                player.prepare();
                player.start();
            } catch (IOException e) {
                e.printStackTrace();
            }

            handler.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText(msg);
                }
            });
            return new NanoHTTPD.Response(OK, MIME_HTML, "ok");
        } else if (uri.equals("/clear/")) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText("");
                }
            });
            return new NanoHTTPD.Response(OK, MIME_HTML, "ok");
        }
        return new NanoHTTPD.Response(NOT_FOUND, MIME_HTML, "none");
    }
}

いつもどおり関係ないけど

そういえば嫁のサイトをazureにも置きました

GitlabPagesでアカウント名にドットが入っているとうまく動かない

事象

アカウント名がfoo.varとドットが入っているとGitlabPagesは https://foo.var.gitlab.io/val  みたいなURLになる。

しかし、GitlabPagesからもらえる証明書は*.gitlab.ioのワイルドカード証明書なため、foo.varと二段階になっているサブドメインではエラーとなる。

対処

URLにこだわりはないのでドットの入っていないグループを作成しそちらにレポジトリ移管した。https://epea.gitlab.io/sanko5/ (epeaが新しくつくったグループ)

自力でLetsEncript動かしてもよさそうなきもするけどそれはそれで面倒そうなので試していない。

ElementUIのForm-Item Attributesのerrorがうまく動作しない

やりたいこと

現在、ElementUIとNuxt.jsでサーバサイドで発生したエラー(一意制約違反)のメッセージを
Form-Item Attributesのerrorに設定することで表示させているけど、2度目の操作で祭表示されない。

暫定対処

ソースをみるとerrorをwatchしているけど、その中でエラーメッセージのレンダリングの判定を操作している。多分watchの反映がされる前に、レンダリングが走っている模様。

取り敢えず、待ちの処理を入れて対策。きちんと同期をさせていないがそもそもこんな状況になるということは何か、入り口が間違っている気がする。terateilに初めて質問してみたけどまだ回答はない。

ソース

抜粋。(全部はterateilに張っておいた)

<template>
  <section class="container">
    <el-form
      ref="userForm"
      :model="userForm"
      :rules="rules"
      @submit.native.prevent>
      <el-form-item
        ref="nickname"
        :error="errorMessage"
        label="ニックネームを登録してください。"
        prop="nickname">
        <el-input
          v-model="userForm.nickname"
          type="text"
          autocomplete="off"/>
      </el-form-item>
      <el-button
        type="primary"
        @click="registNickName">登録</el-button>
    </el-form>

  </section>
</template>

<script>

export default {
  data: function() {
    return {
      userForm: {
        nickname: ''
      },
      errorMessage: '',
      rules: {
        nickname: [
          {
            required: true,
            whitespace: true,
            message: 'ニックネームは必ず入力してください。'
          }
        ]
      }
    }
  },
  methods: {
    registNickName: async function() {
        // 一意制約(ニックネーム重複)

        /*
          XXX 2度目の時にthis.errorを一度クリアしないと発火しない。
          ただし、少しまってやらないといけない。
          データの変更が非同期に走っているから整合性がとれなくなっている?
         */
        this.errorMessage = ''
        await new Promise(function(resolve, reject) {
          window.setTimeout(resolve, 10)
        })
        const duplicateMessage =
          this.userForm.nickname +
          'は既に使用されています。別のニックネームにしてください。'
        this.errorMessage = duplicateMessage

        return

    }
  }
}
</script>

いつもどおり関係ないけど

掲載してもらったBOLLOGのリンクがgoogleに認識されない。大手だけど更新がしばらく止まってるとクローラー見に行かないのかな??

lambdaとRoute53を使ってDDNS機能を作った

概要

固定IPを振っていない店用にlambdaとRoute53を使ってDDNS機能を作った

背景

この間、店の呼び出しブザーを作ったけど死活監視をしていない。zabbixエージェントを入れようとしたけど店には固定IPが来ていないのでDDNSなり(VPNを貼るなり)しないといけない。

システム

環境

  • python3.7.0
  • raspbian9.4
  • Raspberry Pi B+
  • API Gateway
  • Lambda
  • Route53

システム概要

店のネットワーク内にあるラズパイからAPI Gateway経由でLambdaを呼出。

呼び出されたLambdaで呼び出し元(ラズパイ)のグローバルIPを取得。

今回のグローバルIPが前回のグローバルIPと違ったらLambda内でbotoを使ってRoute53のレコードを変更

lambdaのソース

ポン置きのラズパイから起動しているのでセキュリティ的に不安。なので、対象サーバとかzoneIdは引数でなくlambda側で持っている。
対象のAレコードなかったりしたら動かないけどログ見たらなんとかなるはず。

import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
        ZONE_ID = 'Route53のHosted Zone ID'
        logger.debug('call lambda')
        source_ip = event['source_ip']
        original_ip = event['original_ip']

        logger.debug( 'source_ip -> ' + source_ip)
        logger.debug( 'original_ip -> ' + original_ip)


        if original_ip == '' or original_ip != source_ip:
            logger.info('original_ip != source_ip')
            logger.info( 'source_ip -> ' + source_ip)
            logger.info( 'original_ip -> ' + original_ip)
            client = boto3.client('route53')

            try:
                response = client.list_resource_record_sets(HostedZoneId=ZONE_ID)
                target = [item for item in response['ResourceRecordSets'] if item['Name'] == 'hogehoge.epea.co.jp.' and item['Type'] == 'A'][0]
                logger.info(target)

                setting_ip = target['ResourceRecords'][0]['Value']

                if setting_ip != source_ip:
                    logger.info('modify start')
                    target['ResourceRecords'][0]['Value'] = source_ip

                    client.change_resource_record_sets(
                        HostedZoneId = ZONE_ID,
                        ChangeBatch = {
                            'Comment': '多分IPかわった',
                            'Changes': [{
                                'Action': 'UPSERT',
                                'ResourceRecordSet':target
                                }]
                            }
                    )
                    logger.info('modify finish')
            except Exception as e:
                logger.error('KOKODESUKOKODESU')
                import traceback
                traceback.print_exc()
                raise Exception("Check CloudWatch")
        return {
            'statusCode': 200,
            'body': source_ip
        }

全体の呼び出し元

Loopしながら呼び出し続けるのみ。

#!/usr/bin/env python3
# coding: utf-8
import json
import logging
import time
import os
import signal
import sys

import requests

def invoker(originalip):
    logger.debug('invocker start')
    logger.debug( 'original_ip -> ' + original_ip)

    headers = {'Content-Type' : 'application/json','x-api-key': ddns_token}
    payload = {'original_ip': original_ip}
    res = requests.post('https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/default/ddns'
        , data=json.dumps(payload)
        , headers=headers)
    if res.status_code != 200:
        print(res.text)
        print(res.status_code)
        raise Exception("TODO")

    logger.debug('res body ' + res.json()['body'])
    logger.debug('invocker finish')
    return res.json()['body']

def handler(signal, frame):
    logger.info('invocker stop')
    sys.exit(0)

signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)

try:
    formatter = '%(levelname)s : %(asctime)s : %(message)s'
    logging.basicConfig(level = logging.INFO, filename = 'ddns.log', format=formatter)
except:
    print >> sys.stderr, 'error: could not open log file'
    sys.exit(1)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

ddns_token = os.environ['DDNS_TOKEN']
original_ip = ''
logger.info('invocker start')
logger.debug('ddns_token ->[' + ddns_token + ']')
while True:
    logger.debug('in main loop')
    original_ip = invoker(original_ip)
    time.sleep(900)

権限

ラムダ作った時に作られる権限の他にRoute53のレコード参照/操作権限を付与

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "route53:ChangeResourceRecordSets",
                "route53:ListResourceRecordSets"
            ],
            "Resource": [
                "arn:aws:route53:::change/hostedzone/Route53のHosted Zone ID",
                "arn:aws:route53:::hostedzone/Route53のHosted Zone ID"
            ]
        }
    ]
}

API Gatewayの設定

リクエストのマッピング

#set ($body = $util.parseJson($input.json('$')))
{
   "original_ip" : "$body.original_ip",
   "source_ip" : "$context.identity.sourceIp"    
}

エラー時のマッピング

正規表現(でなくそのままだけど) “Check CloudWatch”でメソッドレスポンスのステータスを500に指定

systemd

特にコメントなし

[Unit]
Description=DDNS Daemon

[Service]
EnvironmentFile=/home/pi/.config/environment.d/ddns.conf
WorkingDirectory=/home/pi/develop/ddns/
ExecStart=/home/pi/develop/ddns/invoke_ddns.py
ExecStop=/bin/kill ${MAINPID}
Restart=always
Type=simple
User=pi
Group=pi

[Install]
WantedBy=multi-user.target

それはそうとボルログに店の情報のせてもらったけど今はほとんど更新されてないのね。。

Raspberry Piで作った玄関の呼び出しブザーにLine通知も付けてみた

概要

このまえqiitaに書いたメール通知ができる玄関の呼び出しブザーにLine通知も付けてみたのでそのメモ。

環境

python3.7.0
raspbian9.4
Raspberry Pi B+

プログラム

事前準備

requestsモジュールを使うのでインストール

pip install requests

ソース

ほぼこちらのとおり。他に指定できるものはドキュメント参照。

LINE_TOKENは環境変数から取得。引数のcurrent_timeは呼び出し元でフォーマット済みの文字列

(line_notify.py)

#!/usr/bin/env python3
# coding: utf-8
import os

import requests

def line_notify(current_time):
    url = 'https://notify-api.line.me/api/notify'
    token = os.environ['LINE_TOKEN']
    headers = {"Authorization" : "Bearer "+ token}

    message =  '呼出ブザーが押されました %s' % current_time
    payload = {"message" :  message}

    r = requests.post(url ,headers = headers ,params=payload)

if __name__ == '__main__':
    line_notify( '2018/11/12 固定' )

他のソース/設定ファイル

コントローラー

(control.py)

前の記事ではwait_for_edgeでボタンプッシュを検知していたが、SIGINTきても処理がとまらないとのことでadd_event_detectに変更

また、systemdに登録したのでsignalをハンドラで処理するように変更

#!/usr/bin/env python3
# coding: utf-8
from datetime import datetime
from datetime import timedelta
import logging
import signal
import sys
import time

import RPi.GPIO as GPIO

from mail import visitmail
from line_notify import line_notify

def handler(signal, frame):
    logger.info('break')
    GPIO.cleanup()
    sys.exit(0)

def callBuzzer(channel):
    try:
        # ノイズ対策 静電気等のノイズでないか0.1秒以上続いていることをチェック
        time.sleep(0.1)
        if GPIO.input(pin) == GPIO.LOW:

            # 連続クリック対応 何度もメールが飛ぶと面倒なので一定時間内のボタンプッシュは無視
            # ノイズ対策の前に置くとこの処理時間で継続時間が長くなってしまうのでこの処理順にしている
            global before_calltime
            current_time = datetime.now()
            if current_time < (before_calltime + timedelta(seconds=60)):
                logger.debug('callback yet')
                return

            logger.debug('before_calltime:%s current_time:%s'
                % (before_calltime.strftime("%Y/%m/%d %H:%M:%S"),current_time.strftime("%Y/%m/%d %H:%M:%S")))
            before_calltime = current_time

            logger.info('visit actions call')
            str_current_time = current_time.strftime("%Y/%m/%d %H:%M:%S")
            visitmail(str_current_time)
            line_notify(str_current_time)
    except Exception as e:
        logger.error('error:  %s' % e)

signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)

try:
    formatter = '%(levelname)s : %(asctime)s : %(message)s'
    logging.basicConfig(level = logging.INFO, filename = 'doorphone.log', format=formatter)
except:
    print >> sys.stderr, 'error: could not open log file'
    sys.exit(1)
logger = logging.getLogger(__name__)
#logger.setLevel(logging.DEBUG)
loop_logger = logging.getLogger('loop_logger')
#loop_logger.setLevel(logging.DEBUG)

logger.info('start doorphone moniter')
before_calltime = datetime.now() - timedelta(seconds=60)

GPIO.setmode(GPIO.BCM)
pin = 25

GPIO.setup(pin, GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(pin, GPIO.FALLING, callback=callBuzzer, bouncetime=300)

while True:
    loop_logger.debug('main loop runnning')
    time.sleep(1)

メール送信処理

(mail.py)

引数かえたぐらいで特に変更なし

#!/usr/bin/env python3
# coding: utf-8
import configparser
from email.mime.text import MIMEText
from smtplib import SMTP
import os

def visitmail(current_time):
    inifile = configparser.ConfigParser()
    inifile.read('./config.ini', 'UTF-8')

    ini_host = inifile.get('server', 'host')
    ini_port = inifile.get('server', 'port')
    ini_from = inifile.get('mail', 'from')
    ini_to = inifile.get('mail', 'to')
    ini_title = inifile.get('mail', 'title')

    with SMTP(host=ini_host, port=ini_port) as smtp:
        smtp.starttls()
        smtp.login(
                user = ini_from,
                password = os.environ['MAIL_PASSWORD'],
                )
        msg = MIMEText(current_time)
        msg['Subject'] = ini_title
        msg['To'] = ini_to
        msg['From'] = ini_from

        smtp.send_message(
                from_addr = ini_from,
                to_addrs = ini_to.split(','),
                msg = msg,
                )

if __name__ == '__main__':
    visitmail( '2018/11/12 固定' )

設定ファイル

(./config.ini)


[server]
host = mail.hoge.co.jp
port = 587

[mail]
from = kitamura@hoge.co.jp
to = kitamura@hoge.co.jp,keitaiaddr@ezweb.ne.jp
title = 呼出ブザーが押されました

systemd

(/etc/systemd/system/doorphone.service)

[Unit]
Description=doorphone Daemon

[Service]
EnvironmentFile=/home/pi/.config/environment.d/doormail.conf
WorkingDirectory=/home/pi/develop/doorphone
ExecStart=/home/pi/develop/doorphone/control.py
ExecStop=/bin/kill ${MAINPID}
Restart=always
Type=simple
User=pi
Group=pi

[Install]
WantedBy=multi-user.target

そういえば嫁のサイトをfirebaseに準備しました

atom-runnerでpyenvで指定したpythonを使う

環境

  • CentOS Linux release 7.6.1810 (Core)
  • Atom 1.31.2
  • atom-runner 2.7.1

設定

Atomの設定ファイル(config.cson)にpyenvが指しているpythonのパスを指定する。ホームをチルダで指定したら動かなかったので絶対パスで指定している。

pythonのパス確認

$ which python
~/.pyenv/shims/python

~/.atom/config.csonに追記(“*”: は元からあるのでその下にrunnerからの3行を追記)

"*":
  'runner':
    'scopes':
      'python': '/home/ユーザ名/.pyenv/shims/python'
  以下、元からあった分