{"id":51510179,"url":"https://github.com/eigger/paneo","last_synced_at":"2026-07-08T05:00:33.134Z","repository":{"id":369032664,"uuid":"1287891121","full_name":"eigger/paneo","owner":"eigger","description":"Web-editable dashboards for Raspberry Pi \u0026 always-on displays. Self-hosted, real-time updates.","archived":false,"fork":false,"pushed_at":"2026-07-03T08:52:51.000Z","size":1841,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-03T10:12:35.195Z","etag":null,"topics":["dashboard","home-assistant","kiosk","nodejs","raspberry-pi","self-hosted","websocket"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eigger.png","metadata":{"files":{"readme":"README.ko.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-03T05:08:16.000Z","updated_at":"2026-07-03T08:53:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/eigger/paneo","commit_stats":null,"previous_names":["eigger/paneo"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/eigger/paneo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigger%2Fpaneo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigger%2Fpaneo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigger%2Fpaneo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigger%2Fpaneo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eigger","download_url":"https://codeload.github.com/eigger/paneo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigger%2Fpaneo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35252324,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dashboard","home-assistant","kiosk","nodejs","raspberry-pi","self-hosted","websocket"],"created_at":"2026-07-08T05:00:32.183Z","updated_at":"2026-07-08T05:00:33.127Z","avatar_url":"https://github.com/eigger.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Paneo\n\n[![CI](https://github.com/eigger/paneo/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/eigger/paneo/actions/workflows/ci.yml)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D22.5-339933?logo=node.js\u0026logoColor=white)](https://nodejs.org/)\n[![GitHub Release](https://img.shields.io/github/v/release/eigger/paneo)](https://github.com/eigger/paneo/releases/latest)\n[![License](https://img.shields.io/github/license/eigger/paneo)](https://github.com/eigger/paneo/blob/master/LICENSE)\n[![Raspberry Pi](https://img.shields.io/badge/Raspberry%20Pi-supported-C51A4A?logo=raspberrypi\u0026logoColor=white)](docs/install-device.ko.md)\n[![Self-hosted](https://img.shields.io/badge/hosting-self--hosted-2563EB)](docs/design.md)\n[![Home Assistant](https://img.shields.io/badge/Home%20Assistant-widget-41BDF5?logo=home-assistant\u0026logoColor=white)](docs/install-device.ko.md#83-home-assistant)\n[![Docker](https://img.shields.io/badge/docker-ghcr.io%2Feigger%2Fpaneo-2496ED?logo=docker\u0026logoColor=white)](https://github.com/eigger/paneo/pkgs/container/paneo)\n\n[English](README.md) · [한국어](README.ko.md)\n\n라즈베리 파이와 상시 디스플레이를 위한 웹 편집형 대시보드입니다.\n브라우저에서 레이아웃을 편집하고 **적용**을 누르면 연결된 화면이 새로고침 없이 즉시 갱신됩니다.\n\n위젯은 **크기를 스스로 인식**합니다 — 모서리를 드래그해 크기를 바꾸면 별도 설정 없이 그 자리에서\n레이아웃을 다시 짭니다. 달력은 하루 일정 목록에서 월간 그리드로, 날씨 카드는 예보 스트립이 자라나고,\n뉴스 피드는 발행 시각을 드러냅니다 — 전부 같은 위젯이 얼마나 큰 공간을 받았느냐에 따라 결정됩니다.\n\n## Gallery\n\n| 화면 | 스크린샷 |\n|------|----------|\n| 편집기 | \u003cimg src=\"docs/images/editor.png\" alt=\"Paneo 편집기\" width=\"640\" /\u003e |\n| 디스플레이 | \u003cimg src=\"docs/images/display.png\" alt=\"Paneo 디스플레이 키오스크\" width=\"640\" /\u003e |\n\n### 크기 적응형 위젯\n\n`paneo.calendar.month` 위젯 하나를 4가지 크기로 렌더링한 모습입니다 — 위젯 자신의 렌더링 박스\n크기만으로 어떤 뷰를 보여줄지 스스로 결정합니다:\n\n| 일간 (작음) | 주간 | 3주간 | 월간 (큼) |\n|:---:|:---:|:---:|:---:|\n| \u003cimg src=\"docs/images/widgets/calendar-day.png\" alt=\"캘린더 일간 뷰\" width=\"160\" /\u003e | \u003cimg src=\"docs/images/widgets/calendar-week.png\" alt=\"캘린더 주간 뷰\" width=\"220\" /\u003e | \u003cimg src=\"docs/images/widgets/calendar-3week.png\" alt=\"캘린더 3주간 뷰\" width=\"260\" /\u003e | \u003cimg src=\"docs/images/widgets/calendar-month.png\" alt=\"캘린더 월간 뷰\" width=\"340\" /\u003e |\n\n날씨 위젯도 같은 방식으로 예보 스트립이 나타나고, 대기질/RSS/일정 목록 위젯은 공간이 생기면\n추가 정보(오염물질 세부 항목, 발행 날짜, 소스별 색상 범례)를 보여줍니다:\n\n| 날씨 — 축약 | 날씨 — 예보 포함 |\n|:---:|:---:|\n| \u003cimg src=\"docs/images/widgets/weather-compact.png\" alt=\"날씨 위젯 축약\" width=\"200\" /\u003e | \u003cimg src=\"docs/images/widgets/weather-forecast.png\" alt=\"날씨 위젯 예보 포함\" width=\"240\" /\u003e |\n\n별도의 설정 토글이 전환하는 게 아니라, 위젯 자신의 박스에 붙은 `ResizeObserver`가 판단합니다 —\n그래서 편집기에서 리사이즈 핸들을 드래그하는 도중에도 실시간으로 반응합니다.\n\n### 전체 위젯\n\n| 위젯 | 미리보기 |\n|------|----------|\n| 시계 | \u003cimg src=\"docs/images/widgets/clock.png\" alt=\"시계 위젯\" width=\"200\" /\u003e |\n| 날짜 | \u003cimg src=\"docs/images/widgets/date.png\" alt=\"날짜 위젯\" width=\"200\" /\u003e |\n| 알람 타이머 | \u003cimg src=\"docs/images/widgets/timer.png\" alt=\"알람 타이머 위젯\" width=\"200\" /\u003e |\n| 사진/동영상 슬라이드쇼 | \u003cimg src=\"docs/images/widgets/photo.png\" alt=\"사진 위젯\" width=\"220\" /\u003e |\n| 외부 페이지 | \u003cimg src=\"docs/images/widgets/iframe.png\" alt=\"외부 페이지 위젯\" width=\"240\" /\u003e |\n| 대기질 (확장) | \u003cimg src=\"docs/images/widgets/airquality.png\" alt=\"대기질 위젯\" width=\"200\" /\u003e |\n| RSS / 뉴스 (확장) | \u003cimg src=\"docs/images/widgets/rss.png\" alt=\"RSS 위젯\" width=\"220\" /\u003e |\n| 일정 목록 (확장) | \u003cimg src=\"docs/images/widgets/calendar.png\" alt=\"일정 목록 위젯\" width=\"240\" /\u003e |\n\n스크린샷은 없지만 함께 제공되는 위젯: **텍스트**, **홈어시스턴트** 엔티티(상태/토글 + 전용 날씨\n카드), **세계시계**, **D-Day 카운트다운**, **할 일 목록**, **환율**, **QR 코드** — 내장 위젯만 17종,\n서드파티 플러그인 위젯도 추가할 수 있습니다.\n\n스크린샷 재생성: `node scripts/capture-screenshots.mjs` (최초 1회: `npm install --no-save playwright \u0026\u0026 npx playwright install chromium`. 위젯만: `PANEO_SCREENSHOT_MODE=widgets`)\n\n\u003e 설계 문서: [docs/design.md](docs/design.md) (결정 로그 §0) · 마일스톤: **M6 비-RTSP 완료**, RTSP 보류\n\n## 현재 동작하는 기능\n\n- **크기 적응형 위젯**: 달력(일간/주간/3주간/월간), 날씨(현재 vs 예보), 대기질(핵심 수치 vs 전체\n  오염물질), RSS, 일정 목록이 렌더링 박스 크기에 따라 내부 뷰를 스스로 전환합니다 — 위 갤러리 참고.\n- **편집기** (`/`): 위젯 그리드 드래그/리사이즈, 위젯별 설정(텍스트/숫자/체크박스/동적 URL 목록),\n  카테고리별 **위젯 추가** 팝오버, 기기별 해상도(프리셋·직접 입력·회전), 편집기 UI 언어(ko/en)와\n  디스플레이 로케일 — 모두 편집 툴바와 분리된 ⚙ 설정 패널에서 관리합니다.\n- **디스플레이** (`/d/\u003ctoken\u003e`): 키오스크 페이지, WebSocket 레이아웃 푸시, 오프라인 캐시(네트워크가\n  끊겨도 마지막 레이아웃 유지), 실제 CSS Grid로 해상도·종횡비에 비례해 렌더링합니다.\n- **초안/발행 모델**: 편집 중인 내용은 **적용** 전까지 실제 디스플레이에 반영되지 않습니다.\n- **내장 위젯 17종**: 시계, 날짜, 텍스트, 날씨, 대기질, 일정 목록, 크기 적응형 달력, RSS/뉴스,\n  샌드박스 외부 페이지, 사진/동영상 슬라이드쇼, 알람 타이머, 홈어시스턴트 엔티티, 세계시계,\n  D-Day 카운트다운, 할 일 목록, 환율, QR 코드 — 그리고 서드파티 플러그인 시스템.\n- **데이터 프록시** (`src/dataproxy.js`): 위젯이 외부 API를 직접 호출하지 않고 서버가\n  fetch·소스별 캐시·병합합니다.\n- **기기 관리**: 해상도/로케일/성능 프로파일, 그룹, 원격 새로고침/화면 확인, 컴패니언 에이전트\n  전원 스케줄, 편집기에서 바로 실행하는 원클릭 원격 업데이트.\n- **업데이트 확인**: 업데이트를 실행하기 전에 편집기가 새 릴리즈가 있는지 먼저 알려줍니다.\n- **M6 서드파티 가드레일**: 외부 페이지는 샌드박스 iframe으로 표시하고, 편집기에서 위젯\n  버전/필요 기능/권한을 배포 전에 확인할 수 있습니다. RTSP/카메라 스트리밍은 의도적으로 보류했습니다.\n- **SQLite 영속화**: Node 내장 `node:sqlite` 사용 — 네이티브 컴파일 불필요.\n\n## 실행\n\n```sh\nnpm install\nnpm start          # http://localhost:4321\n```\n\n또는 Docker로 (단일 컨테이너, `data/`는 볼륨에 영속화 — [docs/design.md](docs/design.md) §10 참고). [GitHub Release](https://github.com/eigger/paneo/releases)가 나올 때마다 GHCR에 빌드된 이미지가 자동으로 올라갑니다(`linux/amd64` + `linux/arm64` — 라즈베리 파이 본체에서도 그대로 실행 가능):\n\n```sh\ndocker compose pull \u0026\u0026 docker compose up -d   # 릴리즈 이미지 사용\n# 또는: docker compose up -d --build          # 로컬 소스에서 직접 빌드\n```\n\n- **편집기**: http://localhost:4321/ (최초 실행 시 기본 화면이 자동 생성됩니다)\n- **디스플레이**: 편집기에서 **디스플레이 열기**를 누르거나 `http://localhost:4321/d/\u003ctoken\u003e` 접속\n\n디스플레이를 다른 탭/창에서 열고, 편집기에서 위젯을 배치한 뒤 **적용**을 누르면 실시간으로 반영됩니다.\n\n### 운영 환경 배포 및 HTTPS\n\n에디터가 비밀번호로 보호되어 있으므로, 이를 평문 HTTP로 외부 인터넷에 노출하는 것은 안전하지 않습니다. HTTPS 암호화를 적용하기 위해 Paneo 전면에 역방향 프록시(예: Nginx, Traefik 또는 Cloudflare Tunnels 등)를 두고 운영할 것을 강력히 권장합니다:\n\n1. **프록시 설정**: 역방향 프록시에서 외부 443(HTTPS) 포트 요청을 받아 Paneo의 로컬 4321 포트로 전달하도록 설정합니다.\n2. **SSL 인증서**: 역방향 프록시 단에서 Let's Encrypt 등을 통해 SSL/TLS 인증서를 적용합니다.\n3. **보안 구성**: 브라우저와 Paneo 간의 통신이 HTTPS 암호화 채널로 통하게 하여 세션 쿠키(`paneo_session`)가 네트워크에서 평문으로 탈취되는 것을 원천 차단합니다.\n\n\n## 테스트\n\n```sh\nnpm test\n```\n\nGitHub Actions(`.github/workflows/ci.yml`)에서 Node.js 22·24 환경으로 동일한 테스트를 실행합니다.\n\n## 라즈베리 파이 설치\n\n**명령 한 줄, 기기 한 대** — 서버 + 키오스크 디스플레이 + 컴패니언 에이전트를 한 번에:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/eigger/paneo/master/install.sh | sudo env PANEO_MODE=all bash\n```\n\n디스플레이가 바로 키오스크로 부팅됩니다. 서버와 디스플레이를\n여러 대의 Pi로 나누는 경우가 아니라면 아래는 건너뛰어도 됩니다.\n\n\u003e **참고:** 설치 및 재부팅 후 디스플레이(키오스크)가 완전히 뜨기까지 **처음에는 약 5분** 정도\n\u003e 걸릴 수 있습니다. Docker 이미지 다운로드, 폰트 설치, Chromium 기동 등 Pi 첫 부팅 시 시간이 더 걸립니다.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003e여러 대 설치, 수동 설치, 기타 옵션\u003c/strong\u003e (클릭해서 펼치기)\u003c/summary\u003e\n\n**역할에 맞는 블록 하나만** 실행하세요.\n\n**서버 Pi** (서버만, 키오스크 없음):\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/eigger/paneo/master/install.sh | sudo env PANEO_MODE=server bash\n```\n\n**디스플레이 Pi** (kiosk + 에이전트, 서버가 이미 실행 중이어야 하며 편집기의 **디스플레이 열기**에서 토큰 필요):\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/eigger/paneo/master/install.sh \\\n  | sudo env PANEO_MODE=display \\\n      PANEO_SERVER=http://\u003cserver-ip\u003e:4321 \\\n      PANEO_TOKEN=\u003ctoken\u003e \\\n      bash\n```\n\n그 밖의 옵션(버전 고정, 기기 이름 지정, Docker Compose/systemd 수동 구성, 컴패니언 에이전트만 설치,\nChromium kiosk 자동실행 세부사항)은 전체 가이드\n[`docs/install-device.ko.md`](docs/install-device.ko.md) ([English](docs/install-device.md))를\n참고하세요 — 첫 대시보드 구성, Home Assistant, 사진 액자, 문제 해결도 함께 다룹니다.\n\n\u003c/details\u003e\n\n## 업데이트\n\nPi 본체에서 명령 한 줄로 서버 이미지와 에이전트(`all` 모드라면 코덱과 키오스크 브라우저 재시작까지)를\n데이터 손실 없이 갱신합니다:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/eigger/paneo/master/scripts/update-pi.sh | sudo bash\n```\n\n또는 디스플레이의 컴패니언 에이전트가 연결되어 있다면 편집기 ⚙ **설정** 패널에서 바로 같은 업데이트를\n실행할 수 있습니다 — 새 릴리즈가 있는지도 먼저 보여줍니다. 자세한 내용(서버 전용 모드, Docker\nCompose 수동 업데이트, 에이전트만 업데이트): [`docs/install-device.ko.md`](docs/install-device.ko.md#12-업데이트)\n\n## 제거\n\nPaneo와 컴패니언 에이전트, 키오스크 설정을 기기에서 완전히 삭제하고 이전 상태로 복구하려면 아래 명령어를 실행하세요:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/eigger/paneo/master/uninstall.sh | sudo bash\n```\n\n## 구조\n\n- `src/server.js` — Fastify + `@fastify/websocket` REST + WS 허브\n- `src/store.js` — SQLite(`node:sqlite`) 영속화, M0 JSON 자동 마이그레이션\n- `src/dataproxy.js` — 날씨/대기질/iCal/RSS 서버 사이드 fetch + 캐시 + 병합\n- `src/version.js` — 구성 요소 버전 정보 + GitHub 릴리즈 기반 업데이트 확인\n- `src/brand.js` — 제품명/`pluginPrefix` 중앙 관리\n- `public/shared/widgets.js` — 편집기 미리보기와 디스플레이가 공유하는 위젯 레지스트리\n  (크기 적응형 `ResizeObserver` 위젯 포함)\n- `public/shared/gridlayout.js` — 편집기·디스플레이 비례 레이아웃 공통 CSS Grid 계산\n- `public/editor/` — 그리드 편집기\n- `public/display/` — 키오스크 디스플레이 페이지\n- `agent/` — 선택적 컴패니언 에이전트(화면 전원 제어 + 원격 업데이트)\n- `install.sh` — GitHub에서 clone 후 Pi 설치를 시작하는 부트스트랩 스크립트\n- `scripts/install-pi.sh` — 라즈베리 파이 원클릭 설치 스크립트\n- `scripts/update-pi.sh` — 라즈베리 파이 원클릭 업데이트 스크립트\n- `scripts/capture-screenshots.mjs` — README 스크린샷 재생성 (Playwright)\n- `test/` — Node.js 내장 테스트 러너\n\n## 위젯\n\n`paneo.clock` · `paneo.date` · `paneo.text` · `paneo.weather` · `paneo.airquality` · `paneo.calendar` ·\n`paneo.calendar.month` · `paneo.rss` · `paneo.iframe` · `paneo.photo` · `paneo.timer` ·\n`paneo.homeassistant` · `paneo.worldclock` · `paneo.dday` · `paneo.todo` · `paneo.exchangerate` ·\n`paneo.qrcode`\n\n## 저장소\n\nhttps://github.com/eigger/paneo\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feigger%2Fpaneo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feigger%2Fpaneo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feigger%2Fpaneo/lists"}