Amazonのアソシエイトとして、ラズパイダ(raspida.com)は適格販売により収入を得ています。詳しくは当サイトの プライバシーポリシーをご覧ください。
作業した元記事はこちら。
IMAPメールをモニターに表示する仕組み(Zero2/2w)
Raspberry Pi OS lite(以下、Lite)だとターミナルしかないため、日本語は表示も入力もできない。入力はしないとしても表示は困る。localeコマンドで確認すると、GB(イギリス)のまま。
LANG=en_GB.UTF-8
LANGUAGE=
LC_CTYPE="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_PAPER="en_GB.UTF-8"
LC_NAME="en_GB.UTF-8"
LC_ADDRESS="en_GB.UTF-8"
LC_TELEPHONE="en_GB.UTF-8"
LC_MEASUREMENT="en_GB.UTF-8"
LC_IDENTIFICATION="en_GB.UTF-8"
LC_ALL=
これをraspi-configで日本語(ja_JP.UTF-8)を選択する。
LANG=ja_JP.UTF-8
LANGUAGE=
LC_CTYPE="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_PAPER="ja_JP.UTF-8"
LC_NAME="ja_JP.UTF-8"
LC_ADDRESS="ja_JP.UTF-8"
LC_TELEPHONE="ja_JP.UTF-8"
LC_MEASUREMENT="ja_JP.UTF-8"
LC_IDENTIFICATION="ja_JP.UTF-8"
LC_ALL=
それから、日本語フォントも入っていない。後でconsole-setupをしたいが、Liteでも既に入っていた。
sudo apt install fonts-ipafont console-setup
sudo reboot
IPAフォントはfonts-ipafont-gothic fonts-ipafont-minchoの2つがインストールされる。
ついでにフォントキャッシュを削除しておくと良かった。(結構重要かも)
fc-cache -fv
再起動後に確認する。
fc-list : family file | grep -i ipa
こんな感じ。文字化けしているのは文字コードが違うからで問題無い。
/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf: IPA Pゴシック,IPAPGothic,䥐䅐䝯瑨楣
/usr/share/fonts/truetype/fonts-japanese-mincho.ttf: IPA明朝,IPAMincho
/usr/share/fonts/opentype/ipafont-mincho/ipamp.ttf: IPA P明朝,IPAPMincho,䥐䅐䵩湣桯
/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf: IPAゴシック,IPAGothic
/usr/share/fonts/truetype/fonts-japanese-gothic.ttf: IPAゴシック,IPAGothic
/usr/share/fonts/opentype/ipafont-mincho/ipam.ttf: IPA明朝,IPAMincho
設定しておくと良いもの
最後でも良いけど、忘れないうちにcmdline.txtとconfig.txtに設定しておくことにします。
/boot/firmware/cmdline.txtに、次の3つを追加しました。
consoleblank=0 loglevel=3 vt.global_cursor_default=0
• consoleblank=0 → 画面が自動消灯しない • vt.global_cursor_default=0 → カーソル非表示(見た目の問題) • loglevel=3 → 起動ログを抑制
/boot/firmware/config.txtには、最下段に次の3つを追加しました。
hdmi_force_hotplug=1
hdmi_group=2
hdmi_mode=82
• hdmi_force_hotplug=1 → モニター未検出でもHDMI有効(壁掛け用だから) • hdmi_group=2 → DMT(PCモニター系) • hdmi_mode=82 → 1920x1080 @ 60Hz
この辺は環境によって変えないとならない。
fbtermのインストールと設定
ここでfbtermをインストールしておく。恒久的に日本語フォントを使いたいから、設定ファイルにも書いておく。
sudo atp install fbterm
nano ~/.fbtermrc
元々あった設定をコメントアウトして、その下に追記した。 フォントをIPAゴシックにし、フォントサイズは最終的に44ポイントでちょうど良かった。
# font family names/pixelsize used by fbterm, multiple font family names must be seperated by ','
# and using a fixed width font as the first is strongly recommended
##font-names=mono
##font-size=12
font-names=IPAGothic
font-size=44
サイズを計算するのに次のコマンドで
stty size
仮に22 80と返ってきたら、縦22行、横80文字ということで、これはフォントサイズ48pt相当になる計算です。
一時的にサイズを試すなら、fbterm内ではなくTTY1に戻ってから次のコマンド。 fbtermは仮想端末だから、実行中に一時的に変えられない。
fbterm --font-names=IPAGothic --font-size=40
■Pi 5は8GBモデルがオススメ
IMAPの接続確認
例は独自ドメインIMAPなので誰でもというわけにはいきませんが、gmailであれば先にアプリパスワードも発行しておいてから、読み替えてください。 ターミナルで実行して、先に確認しておきます。
単に接続の確認だけ:
python3 - << 'EOF'
import imaplib
m = imaplib.IMAP4_SSL("xxxxxx.xserver.jp", 993)
print(m.noop())
m.logout()
EOF
OKと返ってくれば問題無い。('OK', [b'...'])
パスワードを用いたログイン確認:
python3 - << 'EOF'
import imaplib
HOST = "xxxxxx.xserver.jp"
PORT = 993
USER = "アカウント@メールドメイン"
PASS = "パスワード"
m = imaplib.IMAP4_SSL(HOST, PORT)
print(m.login(USER, PASS))
m.logout()
EOF
('OK', [b'NOOP completed'])と返れば良い。
CERTIFICATE_VERIFY_FAILEDと出た場合、自己署名の可能性があるので、それはPythonコード内で処理するから、テストでは問題無い。
成功した最終的なPythonコード
Python内で、パスワードなどは外に出した。 新規作成したconfigファイルに、次のように記述。
# ---- 設定 ----
IMAP_HOST=imap.domain.jp
IMAP_PORT=993
IMAP_USER=user@domail.com
IMAP_PASS=password
肝心のPythonコード
#!/usr/bin/env python3
import imaplib
import email
from email import policy
import time
import re
import sys
# ---- 設定 ----
from pathlib import Path
def load_config():
config = {}
for line in Path.home().joinpath(".imap_config").read_text().splitlines():
if "=" in line:
k, v = line.strip().split("=", 1)
config[k] = v
return config
cfg = load_config()
IMAP_HOST = cfg["IMAP_HOST"]
IMAP_PORT = int(cfg["IMAP_PORT"])
IMAP_USER = cfg["IMAP_USER"]
IMAP_PASS = cfg["IMAP_PASS"]
MAILBOX = "INBOX"
CHECK_INTERVAL = 60
MAX_LINES = 20
sys.stdout.reconfigure(encoding='utf-8')
def decode_body(msg):
for part in msg.walk():
if part.get_content_type() == "text/plain":
charset = part.get_content_charset() or "utf-8"
payload = part.get_payload(decode=True)
try:
return payload.decode(charset, errors="replace")
except:
return payload.decode("utf-8", errors="replace")
return ""
def clean_text(text):
lines = []
for line in text.splitlines():
stripped = line.strip()
# 行頭が TEL のときだけ停止
if re.match(r'^TEL[::]', stripped, re.IGNORECASE):
break
# URLは除外
if re.search(r'https?://', line):
continue
line = line.strip()
if line:
lines.append(line)
return lines[:MAX_LINES]
def clear_screen():
print("\033[2J\033[H", end="")
def display_two(messages):
clear_screen()
for mail_id, subject, lines in messages:
print("=" * 60)
print(f"[件名] {subject}")
print("-" * 60)
for line in lines:
print(line)
print("=" * 60)
print()
def fetch_latest_two():
import ssl
context = ssl.create_default_context()
mail = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT, ssl_context=context)
mail.login(IMAP_USER, IMAP_PASS)
mail.select(MAILBOX)
status, data = mail.search(None, "ALL")
if status != "OK":
mail.logout()
return None
ids = data[0].split()
if len(ids) < 1:
mail.logout()
return None
latest_two = ids[-1:] # ← ここ
results = []
for mail_id in reversed(latest_two): # 最新を上に
status, msg_data = mail.fetch(mail_id, "(RFC822)")
if status != "OK":
continue
raw = msg_data[0][1]
msg = email.message_from_bytes(raw, policy=policy.default)
subject = msg.get("Subject", "")
body = decode_body(msg)
lines = clean_text(body)[:22] # ← 22行制限
results.append((mail_id, subject, lines))
mail.logout()
return results
def main():
last_top_id = None
while True:
try:
results = fetch_latest_two()
if results:
top_id = results[0][0]
if top_id != last_top_id:
display_two(results)
last_top_id = top_id
except Exception as e:
clear_screen()
print("IMAP ERROR:", e)
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
main()
中でtwoとしているのは、2通のメールを上下に表示させるように改編したため。 実際に、次の箇所を変更すれあ2通表示にもできる。
latest_two = ids[-1:] # ← ここを-2で2通表示へ
自動起動させる
取りあえず問題無く動くだけで終了した。 あとは、これをサービスにして自動起動させるだけです。 最初にsystemdでサービスを作成したけど、結果的に上手くいかなかった。 fbtermが落ちてしまう。ttyの奪い合い? これじゃない。
上手くできたやり方:
自動ログインをraspi-configで有効にした。
~/.bash_profileを新たに作成してループするようにした。
nano ~/.bash_profile
中身はこれだけでOKだった。
# ~/.bash_profile
# 壁掛けメール端末用
while true; do
fbterm --font-names=IPAGothic --font-size=44 -- python3 /home/raspida/imap_display_single.py
sleep 5
done
もしもこれでもエラーが出るなら、エラーをmail.logとして出力するのを追加して、あとで確認してみよう。
while true; do
fbterm --font-names=IPAGothic --font-size=44 -- python3 /home/raspida/imap_display_single.py 2>&1 | tee ~/mail.log
sleep 5
done
おっと、まだダメだ。 画面は出た。問題無い。だけど、SSH接続が接続できるがおかしい。
SSH接続すると、
stdin isn't a interactive tty!と出てCtrl+Cでやっとログインできた。
# ~/.bash_profile
# tty1 以外(SSHなど)では fbterm を起動しない
if [ "$(tty)" = "/dev/tty1" ]; then
while true; do
fbterm --font-names=IPAGothic --font-size=44 -- python3 /home/raspida/imap_display_single.py 2>&1 | tee ~/mail.log
sleep 5
done
fi
再び、画面は表示されプログラムは動いた。一応、エラー無くSSH接続もできました。
ただ、macOS側のターミナルで、プロンプトraspida@zero2:~$の表示が色分けされなくなった。
これはfbtermで入っているってこと?
AIに聞いてみた。
.bash_profile 内で 無限ループ + fbterm 起動 を書いている場合、
非ログインや SSH でログインしたシェルでも ループ前に PS1 や .bashrc の色設定がスキップされる ことがあります。
特に、SSH では .bashrc が読み込まれるのですが、.bash_profile の条件によっては .bashrc の設定が適用されなくなる場合があります。
なるほど。 最終的には次に変更して、プロンプトの色やエイリアス設定を維持、stdin isn’t a interactive tty! エラーを回避できた。
.bash_profileの最終形:
# ~/.bash_profile
# ----------------------------------------
# 壁掛けメール端末用 (tty1 専用)
# SSH 等の非 tty1 では通常シェルを維持
# 現在の端末を確認
current_tty=$(tty)
if [ "$current_tty" = "/dev/tty1" ]; then
# fbterm + Python 無限ループで自動起動
while true; do
fbterm --font-names=IPAGothic --font-size=44 -- python3 /home/raspida/imap_display_single.py 2>&1 | tee ~/mail.log
sleep 5
done
else
# 非 tty1(SSH 等)の場合は ~/.bashrc を読み込む
[ -f ~/.bashrc ] && . ~/.bashrc
fi
以上、思いのほかスムーズに完成しました。



