카테고리 보관물: 디지털액자 프로젝트

디지털액자 (4)

알림 기능을 구현해 보겠습니다.

실시간 알림은 websocket 등으로 구현 하면 되지만 디지털액자는 그렇게까지 실시간으로 알림을 보지 않아도 되기 때문에 2~5초 간격으로 서버에 ajax 요청을 보냅니다. 서버는 알림 배열을 반환합니다.

알림 API 응답

클라이언트는 이 배열을 읽고 알림 표시 기한(expire)이 지났는지 확인한 후 알림을 display_time 초 동안 표시합니다. 랜덤 id 값이 부여되기 때문에 클라이언크는 알림을 표시했으면 localstorage 에 이 id 값을 저장합니다.

localstorage 에 읽을 알림 id 저장

이 localstorage 는 새로고침해도 데이터가 지워지지 않습니다. 따라서 한번 표시된 알림은 다시 표시되지 않습니다.

이제 notification 을 자동으로 넣어주는 코드를 짜야 합니다. 우선 일정 시작 전 알려주는 코드를 짭니다.

매 1분마다 작업을 돌려줘야 하기 때문에 fastapi_utils 라이브러리를 사용했습니다.

@app.on_event("startup")
@repeat_every(seconds=60 * 1)  # 1 minute
def event_notification() -> None:
    now = datetime.datetime.now(tz=tzoffset(None, 32400))
    evt_list = get_event_list()
    expire = 60 * 10  # 10 분
    display_time = 30  # 30 초

    for evt in evt_list:
        for reminders in evt.reminders:
            reminder_at = get_evt_start_date(evt) - datetime.timedelta(minutes=reminders.minutes_before_start)
            if now <= reminder_at < now + datetime.timedelta(minutes=1):
                start_date = get_evt_start_date(evt).strftime("%m-%d %H:%M")
                add_notification(f"{start_date}<br>{evt.summary}<br>일정이 {reminders.minutes_before_start}분 후 시작합니다.",
                                 expire=now.timestamp() + expire, display_time=display_time, show_notiboard=False)

        if len(evt.reminders) == 0:
            reminder_at = get_evt_start_date(evt) - datetime.timedelta(minutes=30)
            if now <= reminder_at < now + datetime.timedelta(minutes=1):
                start_date = get_evt_start_date(evt).strftime("%m-%d %H:%M")
                add_notification(f"{start_date}<br>{evt.summary}<br>일정이 {30}분 후 시작합니다.",
                                 expire=now.timestamp() + expire, display_time=display_time, show_notiboard=False)

임의의 초에 1분마다 실행되는 것이기 때문에 (현재시각 <= 리마인더시각 < 현재시각 + 1분) 범위로 잡아야 알림을 빠짐없이 잡을 수 있습니다.

다음은 학사·학과공지 코드로 알림을 구현할 예정입니다.

디지털액자 (3)

하드웨어를 구성했으니 웹 서버를 만들 차례입니다.

FastAPI 와 Jinja2 템플릿을 사용하여 빠르게 만들겠습니다.

FastAPI: https://fastapi.tiangolo.com/ko/

레이아웃은 아래와 같이 할겁니다

눈이 부시면 안 되기 때문에 다크 테마로 적용시켰고 미래 일정까지 다 보이도록 일정 공간을 넓게 잡았습니다. 그리고 매주 금요일마다 서울에 다녀와야 해서 예매 까먹는걸 방지하기 위해 예매 내역을 조회할 수 있는 칸을 만들었습니다.

그리고 웹 페이지기 때문에 주기적으로 데이터를 변경해줘야 합니다. 아래 코드로 120초마다 페이지를 새로 고칩니다.

function reloadFunction() {
    window.location.reload();
}

let timeout = window.setTimeout(reloadFunction, 120 * 1000);

이제 데이터를 만들어 넣어야 합니다.

파이썬 srt 라이브러리로 티켓 가져오는 코드를 짭니다.

def srt_get_reservations() -> List:
    srt = SRT(srt_id=srt_id, srt_pw=srt_pw)
    reservations = srt.get_reservations()
    return reservations[0:3]

레이아웃이 깨지지 않게 하려면 3개까지 보여지게 합니다.

페이지가 120초마다 새로고침 되므로 위 코드도 계속 실행되면 SRT 서버에 부하를 발생시키고 심하면 차단당할 것입니다. 따라서 적절하게 캐시를 사용해야 합니다. 함수 실행결과를 disk 에 캐시해주는 라이브러리인 diskcache 를 사용하였습니다.

from diskcache import FanoutCache
cache = FanoutCache("./.cache/memoize")

@cache.memoize(expire=60 * 30)
def srt_get_reservations() -> List:
    ...

위와 같이 어노테이션 한줄만으로 쉽게 캐시할 수 있습니다.

마찬가지로 구글 캘린더도 gcsa 라이브러리를 사용하여 작업합니다. 타임존 관련으로 어려운 부분이 있었는데 이 부분은 따로 포스팅 하겠습니다.

다음은 일정이 임박하면 소리와 함께 알려주는 코드를 작성해 보겠습니다.

디지털액자 (2)

먼저 하드웨어 세팅을 해 봅시다. 준비물은 다음과 같습니다.

  1. 남는? TV 또는 모니터
  2. 우분투가 올라가는 업보드/라즈베리파이/미니PC
  3. OS 설치용 USB 1개
  4. 유선 키보드, 마우스
  5. HDMI 케이블 1개

라즈베리파이는 라즈비안도 가능할 수 있습니다. 일단 크로미움이 깔리고 UI가 잘 뜨면 됩니다. 키보드/마우스는 무선이라도 블루투스가 아닌 unifying 수신기가 달린 제품이면 상관 없습니다.

우분투 버전은 Desktop 버전을 설치했습니다. 사용 이유는 일단 UI 가 설치되어 있어 구성의 편리함이 있을 것 같았습니다.

우선 우분투를 설치 안내에 따라 설치합니다. 그리고 kiosk 라는 사용자를 하나 만듭니다. 나중 관리를 위해 메인 계정은 살려두고 kiosk 로 자동 로그온이 되도록 할 겁니다.

우선 패키지를 업데이트 하고 몇개를 새로 설치할 겁니다. unclutter 패키지는 일정 시간이 지나면 마우스를 숨겨주는 패키지입니다.

sudo -s
apt update  && apt upgrade -y && apt install -y chromium-browser unclutter

그리고 자동 로그인을 위해 /etc/gdm3/custom.conf 파일을 편집합니다. gdm3 는 디스플레이 매니저 이름이며 다른 디스플레이 매니저를 쓴다면(lightdm 등) 그 설정 파일을 수정해주면 됩니다.

[daemon]
AutomaticLoginEnable=true # <-- 여기
AutomaticLogin=kiosk # <-- 여기

그리고 kiosk 홈 디렉토리에 chromium 을 실행하는 스크립트를 하나 만듭니다. kiosk.sh

#!/bin/bash

# Run this script in display 0 - the monitor
export DISPLAY=:0

# Hide the mouse from the display
unclutter &

# If Chromium crashes (usually due to rebooting), clear the crash #flag so we don't have the annoying warning bar
sed -i 's/"exited_cleanly":false/"exited_cleanly":true/' /home/kiosk/.config/chromium/Default/Preferences
sed -i 's/"exit_type":"Crashed"/"exit_type":"Normal"/' /home/kiosk/.config/chromium/Default/Preferences

# Run Chromium and open tabs
/usr/bin/chromium-browser --window-size=1920,1080 --kiosk --incognito --window-position=0,0 http://URL &

크로미움에는 키오스크 모드가 있습니다. 맥도날드 같은 곳에 설치된 키오스크 를 의미합니다. 전체 화면으로 실행되며 개발자 모드 비활성화, 알림 억제 등의 효과가 있습니다. 키오스크 모드라고 해서 어떤 동작을 해야 한다 라는게 명세되어 있지는 않고 각 브라우저마다 동작이 다 다릅니다. 일단 지금 상황에서는 전체화면으로 실행되고 주소 바만 없으면 되므로 키오스크 플래그를 줘서 실행합니다.

그리고 방문 기록 등 쓰레기 파일을 남기고 싶지 않으므로 incognito(private mode) 플래그를 줘서 실행합니다. URL 칸에는 URL 을 입력하면 됩니다.

다음은 kiosk.sh 를 자동 실행하게 하는 파일을 작성합니다. 디스플레이 매니저마다 설정이 다를 수 있습니다. home/kiosk/.config/ 아래에 autostart 디렉토리를 만들어 줍니다. 그리고 kiosk.desktop 를 파일을 만들고 편집합니다.

[Desktop Entry]
Type=Application
Name=Kiosk
Exec=/home/kiosk/kiosk.sh
X-GNOME-Autostart-enabled=true
StartupNotify=true
Terminal=false
kiosk 모드 작동 영상(타임랩스)

다음은 디스플레이에 표시될 웹 서버를 작성하도록 하겠습니다.

토이프로젝트 디지털액자 (1)

원룸에 기본 옵션으로 있는 TV를 다양한 정보가 담긴 디지털 액자?로 만드는 프로젝트입니다.

자취를 하면 원룸에 옵션으로 딸려오는 TV가 있는데요. 이건 화질도 좋지 못하고 공간만 잔뜩 차지합니다. 개인적으로 TV를 자주 보지 않기 때문에 무관심으로 1년을 지낼 수도 있지만 마침 눈에 잘 밟히는 자리에 있기에 어떻게든 활용해 보려 합니다.

일단 구상하고 있는 액자 기능은 다음과 같습니다.

  1. 오전 6시에 자동으로 켜져야 함
  2. 오후 9시에 자동으로 꺼져야 함
  3. 주간 날씨와 평균 기온 표시
  4. 시간별 경산시 기온 표시
  5. 회사 이메일 / 개인 이메일 안 읽은 개수 표시
  6. 주간 일정 표시

그리고 준비물은 아래와 같습니다

  1. TV
  2. 미니 PC(라즈베리파이 같은)
  3. 리모컨 전파 발신기(?)

티비를 켜고 끄기 위해서 3번이 필요할 것 같습니다. 아니면 IoT 콘센트 등으로 전원을 내리는 방법이 있는데 다시 켰을 때 TV가 잘 켜질지는 미지수입니다.

그리고 디지털 액자를 만들기 위해 해결해야 할 문제입니다.

  1. 디스플레이 되는 웹 서버
  2. 여러 데이터 소스에서 데이터 조회 및 가공
  3. 미니 PC를 부팅했을 때 자동으로 웹 페이지 띄우기
  4. 시간에 맞춰 TV ON/OFF 신호 송신

일단 계획은 이렇게 잡고 미니 PC부터 다뤄보도록 하겠습니다.