{"id":30015009,"url":"https://github.com/devcybiko/pymirror","last_synced_at":"2025-08-05T18:43:07.134Z","repository":{"id":305702509,"uuid":"1023232826","full_name":"devcybiko/pymirror","owner":"devcybiko","description":"Python version of Magic Mirror","archived":false,"fork":false,"pushed_at":"2025-07-31T04:57:19.000Z","size":17995,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-31T07:37:23.708Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devcybiko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2025-07-20T19:59:24.000Z","updated_at":"2025-07-26T09:33:03.000Z","dependencies_parsed_at":"2025-07-21T16:34:02.462Z","dependency_job_id":"a0459034-3e99-48d0-8032-68e6f2e601ad","html_url":"https://github.com/devcybiko/pymirror","commit_stats":null,"previous_names":["devcybiko/pymirror"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/devcybiko/pymirror","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fpymirror","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fpymirror/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fpymirror/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fpymirror/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devcybiko","download_url":"https://codeload.github.com/devcybiko/pymirror/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fpymirror/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268953630,"owners_count":24335168,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-05T02:00:12.334Z","response_time":2576,"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":[],"created_at":"2025-08-05T18:43:04.425Z","updated_at":"2025-08-05T18:43:07.091Z","avatar_url":"https://github.com/devcybiko.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pymirror\n\n- A Python Dashboard inspired by Magic Mirror 2 \n    - By Greg Smith (greg@agilefrontiers.com)\n    - Created June, 2025\n- Developed for low-resource RPI Zero 2 W\n- Writes directly to the frame buffer using Pillow / PIL\n\n## Installation\n\n- RPI OS Lite 32-bit\n- `sudo apt update`\n- `sudo apt install git`\n- `git clone https://github.com/drfrancintosh/pymirror.git`\n- `sudo apt install fortune`\n- `sudo apt install libdrm-tests`\n  - `modetest` (will display hardware information on the displays)\n- `sudo apt install calendar`\n- `sudo apt install python3-setuptools`\n\n## Installing Libraries\n\n- Install with `pip install -r requirements.txt`\n- or `source ./scripts/install-libs.sh` (for RPi OS)\n\n### clib\n- If using Homebrew Python\n  - `brew install python@3.13-dev`\n- Or reinstall Python with dev headers\n  - `brew reinstall python@3.13`\n- Run python setup.py\n  - python3 setup.py build_ext --inplace\n  \n## Fontlist.txt\n\n- One font per line\n- Basically the output of \"fc-list\"\n- add your own fonts as well\n- Star Trek fonts in the `fonts` folder were appropriated from https://www.st-minutiae.com/resources/fonts/index.html\n- There were no credits there, but if anyone knows who to credit, I'll add the credits here\n- The 7segment.txt font was from https://torinak.com/font/7-segment\n- The Default font DejaVuSerif.ttf was delivered with RPI-OS and copied locally for convenience\n- The Roboto fonts came from https://fonts.google.com/specimen/Roboto\n- Update `fontlist.txt` as per your system\n- Icons fonts (Font Awesome) Free download\n  - https://fontawesome.com/search?o=r\u0026s=solid\u0026ip=classic\n  - https://fontawesome.com/search?o=r\u0026s=regular\u0026ip=classic\n  - https://fontawesome.com/search?ic=brands\n\n## Images folder\n\n- Thanks to `https://github.com/yavuzceliker/sample-images` for sample images\n\n## Running\n- `cd ./pymirror`\n- `./run.sh`\n\n## Web Server\n\n- GET: `http://rp01:8080`\n    - serves up HTML pages that allow you to control your PyMirror\n- POST: `http://rpi01:8080/command`\n    - will allow you to publish an `event` directly to PyMirror\n    - All \"subscribed\" modules will receive the event \n    - the payload is event-specific...\n    - Here is an example for the `alert` module\n```json\n{\n    \"name\": \"weather_alert\",\n    \"heading\": \"Hello PyMirror!\",\n    \"body\": \"This is your first PyMirror event\",\n    \"footer\": \"(C) 2025\",\n    \"timeout\": 10000\n}\n```\n\n## Folders\n\n- caches/ - last calls from the web api in case the api is down and in the case that that PyMirror is calling faster than the rate limit\n- configs/ - .json config files (one per dashboard)\n- events/ - python classes for each type of event that can be published\n- fonts/ - local .ttf files\n- moddefs/ - module definition files (json)\n- random/ - test code and other fancy biscuits\n- samples/ - web api samples\n- scripts/ - test scripts\n- src/\n  - events/ - python definition of PyMirror events\n  - modules/ - PyMirror modules\n  - pmsever/ - the flask web server class and other files (html, etc...)\n  - pymirror/ - pymirror python source files\n\n## Files\n\n- .env - list of environment variables in KEY=value format (ignored by .gitignore, read into the PyMirror os.environ)\n- .secrets - list of API keys in KEY=value format (ignored by .gitignore, read into the PyMirror os.environ)\n- config.json - PyMirror main config file\n- fontlist.txt - PyMirror list of fonts and where to find them\n- README.md - this file\n- requirements.txt - list of Python packages needed to support PyMirror and the default modules\n- run.sh - script to run PyMirror\n- secrets - sample .secrets file\n- TODO.md - wish list of things to add to PyMirror\n\n## Modules\n\n- Modules are stored in the `./src/modules` folder\n- The name of the module must be the snake-case version of the camel-case name of the module class\n  - example: analog_clock.py -\u003e AnalogClock (class name for the module)\n- In the `config.json` you can call up a module multiple times with different instances\n\n- alert.py\n  - displays header, body, and footer. Subscribed to AlertEvent which updates the alert\n- analog_clock.py\n  - Displays analog clock (round face)\n- cli.py\n  - Runs a CLI command on a timer and displays the results\n- clock.py\n  - Displays current time as strftime() format, \n- cron.py\n  - no display, but runs on a timer and sends the defined event\n- fonts.py\n  - list of fonts in fontlist.txt\n- fps.py\n  - Frames Per Second display\n- pymirror_controller.py\n  - handles and dispatches external events\n- rainbow.py\n  - test pattern showing all colors in RGB Bands\n- text.py\n  - displays static text\n- weather.py\n  - Displays current temperature, humidity, and effective temperature\n- web_api.py\n  - sends a web request and displays the formatted payload\n\n## config.json\n\n```json\n{\n  \"debug\": false, // when true displays boxes around module 'windows'\n  \"secrets\": \".secrets\", // your API keys and other secrets\n  \"force_render\": false, // forces a render of a module regardless of state changes\n  \"screen\": {\n    \"output_file\": null, // optional output file for use in debugging or html display\n    \"frame_buffer\": \"/dev/fb0\", // frame buffer device (esp. rpi 02w)\n    \"font_name\": \"Roboto-Thin\", // default font used when modules do not specify a font\n    \"font_size\": 64, // default font size\n    \"color\": \"#fff\", // default line color\n    \"bg_color\": \"#000\", // default fill color\n    \"text_color\": \"#fff\", // default text color\n    \"text_bg_color\": null // default text background color\n  },\n  \"positions\": {\n    // A list of rectangles on the display in \"percentage\" of screen geometry \n    \"None\": \"0.0,0.0,0.0,0.0\", // used by non-displaying modules (cron, pymirror_controller, eg)\n    \"top_strip\": \"0.00,0.0,1.0,0.15\", // format is (x0, y0, x1, y1) where values are 0.0 up to 1.0\n    \"alert_strip\": \"0.30,0.30,0.7,0.7\", // note that this one overlays others. also note that the 'position' is tied to the module name\n    \"fps_strip\": \"0.0,0.95,0.33,1.00\", // fps module location. in future it may be better to specify the module position rather than the relative positions like \"top_left\", etc...\n    \"top_left\": \"0.0,0.15,0.33,0.33\",\n    \"top_center_left\": \"0.33,0.15,0.55,0.33\",\n    \"top_center_right\": \"0.55,0.15,0.66,0.33\",\n    \"top_right\": \"0.66,0.15,1.00,0.33\",\n    \"middle_left\": \"0.0,0.33,0.33,0.66\",\n    \"middle_center\": \"0.33,0.33,0.66,0.85\",\n    \"middle_right\": \"0.66,0.33,1.00,1.00\",\n    \"bottom_left\": \"0.0,0.66,0.33,1.00\",\n    \"bottom_center\": \"0.33,0.85,0.66,1.00\",\n    \"bottom_right_center\": \"0.7,0.66,1.00,0.90\",\n    \"bottom_right\": \"0.66,0.90,1.00,1.00\"\n    },\n  \"modules\": [\n    \"moddefs/alert.json\", // an alert displayed by way of an AlertEvent\n    \"moddefs/pymirror_controller.json\", // the controller for external events\n    \"moddefs/weather.json\", // displays current temperature and weather alerts (OpenWeatherMap API)\n    \"moddefs/fortune.json\", // runs linux 'fortune' command every 15 secs\n    \"moddefs/news.json\", // displays news using web_api module\n    \"moddefs/date.json\", // displays the current date in 7-segment display font\n    \"moddefs/time.json\", // displays the current time in 7-segment display font\n    \"moddefs/week.json\", // displays the word \"Week:\" - static text\n    \"moddefs/day_of_week.json\", // displays the current day of the week\n    \"moddefs/week_of_year.json\", // displays the week number (1-52)\n    \"moddefs/analog_clock.json\", // analog clock\n    \"moddefs/weather_alert.json\", // an alert box for any weather alerts that are received by the alert module\n    \"moddefs/fps.json\", // displays the Frames Per Second calculation\n    // NOTE: you may also put full module definitions right here as a json dictionary\n    {\n      \"module\": \"fps\",\n      \"moddef\": {\n        \"disabled\": false,\n        \"name\": \"fps\",\n        \"position\": \"fps_strip\",\n        \"font_size\": 32\n      },\n      \"fps\": {\n        \"valign\": \"bottom\",\n        \"halign\": \"left\"\n      }\n    }\n  ]\n}\n\n```\n\n## Module Definition Files (./moddefs)\n- Module Definitions identify the module class and the parametrs to display the module\n- Not that that a module (like Date) may be instantiated many times.\n- Each with a different ModDef (see date.json, day_of_week.json, time.json, week_of_year.json) \n```json\n{\n    \"module\": \"alert\", // module name. must reside in src/modules/alert.py\n    \"moddef\": { // all modules have a required generic module definition or 'moddef'\n        \"name\": \"Alert\", // a unique name\n        \"position\": \"alert_strip\", // a position defined in config.json, the 'positions' section\n        \"text_color\": null, // default text color - overrides config.json if non-null\n        \"text_bg_color\": null, // default text background color - overrides config.json if non-null\n        \"font_name\": \"TNG_Title\", // font name definined in fontlist.txt\n        \"font_size\": 24, // default font size\n        \"subscriptions\": [\"AlertEvent\"], // any events this module is 'subscribed' to\n        \"force_render\": false // when true, forces rendering despite no changes in stateful informaton\n    },\n    \"alert\": { // every module has module-specific information that is the same name as the name of the module\n        \"header\": \"Alert\", // this module uses the 'card' format which has a header, footer, and body\n        \"body\": \"This is an alert message.\", // these are the default text values for header, footer, and body\n        \"footer\": \"Alert Footer\",\n        \"timeout\": 2000 // when specified, this is the duration of the alert. 0 == disabled\n    },\n    \"card\": { // when a module is a subclass of a 'PMCard', the config will be found inside a 'card' dict. not all modules have this\n        \"header\": {\n            \"font_name\": \"TNG_Credits\",\n            \"font_size\": 24,\n            \"text_color\": \"#000\",\n            \"text_bg_color\": \"#0ff\",\n            \"height\": 48\n        },\n        \"body\": {\n            \"font_name\": \"TNG_Credits\",\n            \"font_size\": 24,\n            \"text_color\": \"#ff0\",\n            \"text_bg_color\": \"#333\"\n        },\n        \"footer\": {\n            \"font_name\": \"DejaVuSans\",\n            \"font_size\": 24,\n            \"text_color\": \"#000\",\n            \"text_bg_color\": \"#fff\",\n            \"height\": 48\n        }\n    }\n}\n```# pymirror\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevcybiko%2Fpymirror","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevcybiko%2Fpymirror","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevcybiko%2Fpymirror/lists"}