{"id":51138003,"url":"https://github.com/joshuaswarren/pyskylight","last_synced_at":"2026-06-25T20:01:10.173Z","repository":{"id":364670663,"uuid":"1268694096","full_name":"joshuaswarren/pyskylight","owner":"joshuaswarren","description":"Unofficial Python client + CLI for the Skylight Calendar / Buddy API (app.ourskylight.com)","archived":false,"fork":false,"pushed_at":"2026-06-14T00:10:47.000Z","size":38,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T02:16:44.704Z","etag":null,"topics":["api-client","cli","family-calendar","home-automation","ourskylight","python","reverse-engineering","skylight","skylight-calendar","unofficial-api"],"latest_commit_sha":null,"homepage":null,"language":"Python","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/joshuaswarren.png","metadata":{"files":{"readme":"README.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-06-13T20:38:14.000Z","updated_at":"2026-06-14T00:10:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/joshuaswarren/pyskylight","commit_stats":null,"previous_names":["joshuaswarren/pyskylight"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/joshuaswarren/pyskylight","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuaswarren%2Fpyskylight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuaswarren%2Fpyskylight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuaswarren%2Fpyskylight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuaswarren%2Fpyskylight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joshuaswarren","download_url":"https://codeload.github.com/joshuaswarren/pyskylight/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuaswarren%2Fpyskylight/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34790215,"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-06-25T02:00:05.521Z","response_time":101,"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":["api-client","cli","family-calendar","home-automation","ourskylight","python","reverse-engineering","skylight","skylight-calendar","unofficial-api"],"created_at":"2026-06-25T20:01:09.047Z","updated_at":"2026-06-25T20:01:10.140Z","avatar_url":"https://github.com/joshuaswarren.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pyskylight\n\n![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)\n![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)\n![status: alpha](https://img.shields.io/badge/status-alpha-orange)\n\nUnofficial Python client and `skylight` CLI for the [Skylight Calendar](https://www.skylightframe.com/)\n/ Buddy family of devices.\n\n\u003e Part of a three-repo set: **pyskylight** (client + CLI) ·\n\u003e [`openclaw-skylight`](https://github.com/joshuaswarren/openclaw-skylight) (OpenClaw skill) ·\n\u003e [`plantoeat-skylight-sync`](https://github.com/joshuaswarren/plantoeat-skylight-sync) (meal-plan sync).\n\nSkylight has **no official or public API.** This library talks to the same private\napp API (`app.ourskylight.com`) that the community has reverse-engineered. It is for\n**personal use against your own account** — see [Legal](#legal).\n\n## Features\n\n- Email/password login via the app's OAuth2 (PKCE) flow, with token caching + refresh.\n- **Near-complete coverage of the private API** — ~150 client methods / ~145 `skylight`\n  CLI commands spanning frames, calendar (events, search, countdowns, connected/source\n  calendars, webcal subscriptions, Google-link flow), **Meals** (recipes + planned\n  \"sittings\" + instances), chores, lists \u0026 list items, categories/profiles, **rewards**,\n  **photos/messages/albums**, month-in-review, **devices \u0026 Buddy alarms**, household\n  members \u0026 config, routines, task-box, weather/geolocation, and the AI Sidekick\n  (auto-creation intents). See [Command reference](#command-reference).\n- A JSON-first CLI suitable for scripting or driving from an agent (every command prints JSON).\n- 1Password-friendly: credentials may be `op://` references resolved at runtime.\n- Honest about uncertainty: a handful of write bodies are reverse-engineered and accept a\n  `--json` / `**extra` pass-through so they keep working as the API is confirmed live.\n\n## Install\n\n```bash\n# Until it is published to PyPI, install from git:\npip install \"git+https://github.com/joshuaswarren/pyskylight\"\n# (PyPI, once published: pip install pyskylight)\n# or from source:\npip install -e \".[dev]\"\n```\n\nRequires Python 3.11+.\n\n## Quick start\n\n### CLI\n\n```bash\nexport SKYLIGHT_EMAIL=\"you@example.com\"\nexport SKYLIGHT_PASSWORD=\"…\"          # or an op:// reference\n\nskylight login                         # caches the session token\nskylight frames                        # find your frame (household) id\nexport SKYLIGHT_FRAME_ID=123456\n\nskylight meal-categories               # Breakfast / Lunch / Dinner ids\nskylight recipes                       # list recipes\nskylight create-recipe --summary \"Tacos\" --description \"Tuesday classic\"\nskylight plan-add --date 2026-06-20 --meal-category-id 42 --recipe-id 99\n```\n\nEvery command prints JSON.\n\n### Library\n\n```python\nfrom pyskylight import SkylightClient\n\nwith SkylightClient.login(\"you@example.com\", \"…\") as sky:\n    frame = sky.list_frames()[0]\n    for recipe in sky.list_recipes(frame.id):\n        print(recipe.id, recipe.summary)\n    sky.create_sitting(frame.id, date=\"2026-06-20\", meal_category_id=\"42\", meal_recipe_id=\"99\")\n```\n\n## Command reference\n\nRun `skylight --help`, or `skylight \u003ccommand\u003e --help` for flags. Every command takes\n`--frame` (or uses `SKYLIGHT_FRAME_ID`) where a household is required.\n\n| Area | Commands |\n|---|---|\n| **Auth / identity** | `login`, `logout`, `whoami` |\n| **Frames** | `frames`, `frame`, `frame-rename`, `frame-settings`, `frame-hide`, `frame-activation-code` |\n| **Calendar events** | `events`, `events-search`, `countdowns`, `event-invitees`, `event-add`, `event-update`, `event-delete`, `event-notifications`, `event-notifications-update`, `reminder-notification`, `reminder-notification-update` |\n| **Connected calendars** | `calendars`, `calendar-account`, `calendar-account-update`, `calendar-link`, `webcals`, `webcal-add`, `source-calendars`, `source-calendar`, `source-calendar-update`, `source-calendar-delete`, `source-calendar-default`, `source-calendar-categorize`, `category-categorize` |\n| **Meals — recipes** | `recipes`, `recipe`, `create-recipe`, `update-recipe`, `delete-recipe`, `grocery-add`, `meal-categories`, `update-meal-category` |\n| **Meals — plan (sittings)** | `plan`, `plan-show`, `plan-add`, `plan-update`, `plan-remove`, `plan-instances`, `plan-instance-update` |\n| **Chores** | `chores`, `chore-add`, `chore-add-multiple`, `chore-update`, `chore-complete`, `chore-delete` |\n| **Lists \u0026 items** | `lists`, `list-show`, `list-items`, `list-create`, `list-update`, `list-delete`, `list-add`, `list-item-update`, `list-item-complete`, `list-item-delete`, `list-items-delete`, `list-item-move`, `list-items-section` |\n| **Categories / profiles** | `categories`, `category`, `category-add`, `category-find-or-create`, `category-update`, `category-delete` |\n| **Rewards** | `rewards`, `reward`, `reward-add`, `reward-update`, `reward-delete`, `reward-redeem`, `reward-unredeem`, `reward-points`, `reward-points-set` |\n| **Photos / messages** | `messages`, `message`, `message-delete`, `messages-delete`, `messages-copy`, `message-caption`, `message-likes`, `message-like`, `message-unlike`, `message-comments`, `message-comment-add`, `message-comment-delete`, `photo-upload`, `upload-credentials` |\n| **Albums** | `albums`, `album-add`, `album-update`, `album-delete`, `album-messages`, `album-message-ids`, `album-add-photos`, `album-remove-photos` |\n| **Devices \u0026 alarms (Buddy)** | `devices`, `device`, `device-update`, `device-delete`, `device-activation-code`, `device-reset`, `alarms`, `alarm-add`, `alarm-update`, `alarm-delete` |\n| **Household** | `members`, `member-invite`, `member-approve`, `member-remove`, `member-update`, `household-config`, `household-config-update`, `plus-status` |\n| **Routines / task-box** | `routines`, `routine-add`, `routine-update`, `routine-delete`, `routines-reorder`, `task-box`, `task-box-add`, `task-box-update`, `task-box-delete` |\n| **AI Sidekick** | `ai-intents`, `ai-intent`, `ai-intent-create`, `ai-intent-approve`, `ai-intent-retry`, `ai-intent-undo`, `ai-intent-items` |\n| **Reference / misc** | `avatars`, `colors`, `activities`, `month-in-review`, `month-in-reviews`, `weather`, `geolocation`, `share-link` |\n\n\u003e Request shapes were verified against the live web client + live API (2026-06): most\n\u003e updates take typed flags. **Alarms** apply only to Buddy devices and their body is set\n\u003e by the app form, so `alarm-add`/`alarm-update` accept a `--json '{...}'` pass-through.\n\u003e Source calendars have no manual create endpoint — add one by linking an account with\n\u003e `calendar-link`. Account-level and billing operations (registration/deletion,\n\u003e subscription purchase) are intentionally **not** exposed.\n\n## Configuration\n\n| Variable | Purpose |\n|---|---|\n| `SKYLIGHT_EMAIL` | account email (or `op://vault/item/field`) |\n| `SKYLIGHT_PASSWORD` | account password (or `op://…`) |\n| `SKYLIGHT_FRAME_ID` | default frame id, so `--frame` is optional |\n| `SKYLIGHT_TIMEZONE` | default IANA timezone for calendar queries |\n| `SKYLIGHT_BASE_URL` | override the API base URL |\n\nThe session token is cached at `${XDG_CACHE_HOME:-~/.cache}/pyskylight/token.json`\n(mode `0600`). `skylight logout` clears it.\n\n## Authentication notes\n\nLogin uses the app's **OAuth2 Authorization-Code + PKCE** flow (`/oauth/authorize` →\n`/auth/session` → `/oauth/token`) and stores the resulting `access_token` /\n`refresh_token`; subsequent requests send `Authorization: Bearer \u003caccess_token\u003e`. The\nclient refreshes (or re-logs-in) automatically when the token expires.\n\n\u003e The older `POST /api/sessions` email/password endpoint is version-gated and\n\u003e effectively retired (it returns \"This version of Skylight is no longer supported\"),\n\u003e which is why this client uses the OAuth flow. Verified against the live API\n\u003e (2026-06).\n\n\u003e **Skylight Plus:** some Meals features may require Skylight Plus. Where an endpoint\n\u003e is forbidden (HTTP 403) it surfaces as `SkylightPlusRequiredError`. (In practice the\n\u003e Meals/Recipes/Sittings endpoints work on a \"basic\" account.)\n\n## Development\n\n```bash\npip install -e \".[dev]\"\npytest                      # full suite + coverage (fails under 90%)\npre-commit run --all-files  # black, isort, flake8, mypy\n```\n\n## Related projects\n\n- **openclaw-skylight** — an OpenClaw skill that drives this CLI.\n- **plantoeat-skylight-sync** — syncs a Plan to Eat meal plan into Skylight Meals.\n\nPrior art that informed this client: [bryanmig/skylight-calendar-api](https://github.com/bryanmig/skylight-calendar-api),\n[kylebjordahl/skylight-calendar-home-assistant](https://github.com/kylebjordahl/skylight-calendar-home-assistant),\nand [kylejfrost/skylight-api-cli](https://github.com/kylejfrost/skylight-api-cli).\n\n## Legal\n\nUnofficial and not affiliated with, endorsed by, or supported by Skylight. The API\ncan change without notice. Use only with your own account and data; do not build a\nmulti-tenant or commercial service on it. Provided as-is under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshuaswarren%2Fpyskylight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoshuaswarren%2Fpyskylight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshuaswarren%2Fpyskylight/lists"}