Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 37 additions & 20 deletions .claude/skills/bump-pyhive/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,71 @@
---
name: bump-pyhive
description: Update the pyhive-integration (apyhiveapi) requirement in the Hive custom component's manifest.json, review what changed in the library, and verify the component against it. Use when a pyhive bump PR is open, a new pyhive release is out, or the user wants to test a pyhive beta/dev build.
description: Update the pyhive-integration (apyhiveapi) requirement in the Hive custom component's manifest.json, review what changed in the library, and verify the component against it. Use when a pyhive bump PR is open, a new pyhive release or beta is out, before cutting a component beta, or the user wants to test a pyhive beta/dev build.
---

# Bump pyhive-integration

`pyhive-integration` (imported as `apyhiveapi`) is pinned in `custom_components/hive/manifest.json` `requirements`. Dependabot can't bump it, so `.github/workflows/pyhive_bump.yml` opens `deps/pyhive-integration-<ver>` PRs instead.

Read the principles in `CLAUDE.md` first. In short:
- This repo may pin a **newer** `pyhive-integration` than core hive does, including pre-releases. That's intended.
- Keep the pin exact (`==`). Don't switch to `>=` to quiet hassfest; `.github/scripts/hassfest.py` already treats that difference as a warning.
- A pre-release pin can only ship in a component **beta**. A stable component release needs a stable pin.

## 1. Pick the target
```bash
jq -r '.requirements[]' custom_components/hive/manifest.json # current
jq -r '.requirements[]' custom_components/hive/manifest.json # current pin
curl -fsS https://pypi.org/pypi/pyhive-integration/json | jq -r '.info.version' # latest stable
curl -fsS https://pypi.org/pypi/pyhive-integration/json | jq -r '.releases | keys[]' | tail -10
gh pr list --label dependencies --search "pyhive-integration in:title" # existing bot PR?
# newest version overall, pre-releases included (sorted as versions, not by upload date)
curl -fsS https://pypi.org/pypi/pyhive-integration/json | jq -r '.releases | keys[]' \
| .venv/bin/python -c 'import sys; from packaging.version import Version; print(max(sys.stdin.read().split(), key=Version))'
gh pr list --search "pyhive-integration in:title" --state open # existing PR?
```
If there's already a bot PR for the target, work on that branch (`gh pr checkout <n>`) instead of making a new change.
- For a beta (or when the user asks for "latest"), the target is the newest version overall.
- For a stable release, the target is the latest stable.
- Also note core's pin, from `.venv/lib/python*/site-packages/homeassistant/components/hive/manifest.json`. Core's re-exported platforms are written against it, so a big gap needs a closer look.

If there's already an open PR for the target, work on that branch (`gh pr checkout <n>`) instead of making a new change.

## 2. Review what changed
Diff the two versions' sources and look closely at anything this component calls:
`uv pip download` doesn't exist, so fetch the wheels from PyPI's JSON API into the scratchpad and unpack them there:
```bash
S=$(mktemp -d)
S=<scratchpad>/pyhive
for v in <current> <target>; do
uv pip download --no-deps "pyhive-integration==$v" -d "$S/$v" 2>/dev/null || pip download --no-deps "pyhive-integration==$v" -d "$S/$v"
url=$(curl -fsS "https://pypi.org/pypi/pyhive-integration/$v/json" | jq -r '[.urls[]|select(.packagetype=="bdist_wheel")][0].url')
mkdir -p "$S/dl-$v" "$S/src-$v" && curl -fsSL -o "$S/dl-$v/pkg.whl" "$url" && unzip -qo "$S/dl-$v/pkg.whl" -d "$S/src-$v"
done
diff -rq "$S/src-<current>/apyhiveapi" "$S/src-<target>/apyhiveapi"
```
Unpack both and diff `apyhiveapi/`. Things to check:
- `Auth` methods used in `config_flow.py` and `__init__.py`: `login`, `sms_2fa`, `device_registration`, `get_device_data`, `is_device_registered`, `forget_device`.
- `Hive`, `session.startSession`, `session.deviceList`, `updateInterval`.
- `helper/const.py`: `PRODUCTS`, `DEVICES` and `sensor_commands`. A new or renamed `hiveType` needs a matching `SENSOR_TYPES` entry in `sensor.py`, or core's platform descriptions, before it shows up in HA.
- Exceptions in `helper/hive_exceptions.py`.
- The getters that tests mock: `getSensor`, `getClimate`, `getWaterHeater`, `getLight`, `getSwitch` and `getScheduleNowNextLater`.
Check anything this component, or core's re-exported platforms, calls:
- `Auth` methods used in `config_flow.py` and `__init__.py`: `login`, `sms_2fa`, `device_registration`, `get_device_data`, `is_device_registered`, `forget_device`. Also the attributes the flow sets: `access_token`, `device_group_key`, `device_key`.
- `Hive`, `session.startSession`, `session.deviceList`, `session.updateData`, `updateInterval`.
- Every `hive.<platform>.<method>(` call in the component and in core's hive platforms (`grep -hoE "hive\.[a-z_]+\.[A-Za-z_]+\(" ...`). Confirm each exists on the target with a compatible signature, at runtime rather than by reading code.
- How devices are accessed (`device["hiveType"]`, `.get("deviceData")`). 2.x devices are dataclasses with a legacy-key mapping.
- `PRODUCTS`, `DEVICES` and the sensor dispatch. A new or renamed `hiveType` needs a matching `SENSOR_TYPES` entry in `sensor.py`, or core's platform descriptions, before it shows up in HA.
- Exceptions in `helper/hive_exceptions.py`, especially anything `startSession` can now raise. `async_setup_entry` must map it: connection problems to `ConfigEntryNotReady`, credential or config problems to `ConfigEntryAuthFailed`.

Also check whether the version HA core pins (`homeassistant/components/hive/manifest.json` in `.venv`) differs a lot. Core's re-exported platforms are written against that version.
Also check that `pyhive-integration` isn't in HA's `homeassistant/package_constraints.txt`. If it ever is, HA won't install a different version, and principle 2 needs rethinking.

## 3. Apply and verify
- Change only the `pyhive-integration==` pin in `manifest.json`. Don't change the component `version`; that happens at release.
- Install and test:
```bash
VIRTUAL_ENV=.venv uv pip install "pyhive-integration==<target>"
VIRTUAL_ENV=.venv uv pip install --refresh-package pyhive-integration "pyhive-integration==<target>"
script/test
script/lint --check
python3 .github/scripts/hassfest.py # needs Docker; the pyhive line should be a warning
```
(`--refresh-package` matters for a release published in the last few hours.)
- The tests mock the library. On 2.x, also run an offline session from the bundled `data/data.json` (username `use@file.com`) and call the real getters for every platform.
- Where the diff showed behaviour changes, add tests for them.
- For a beta or dev build, suggest also releasing a component beta (see `/release`) so users can try it.
- For a pre-release target, the next step is a component beta (`/release`). For a stable target, it can go in the next stable release.

## 4. Report
Report:
- the version change
- the version change, and how it compares with core's pin
- a short summary of the library changes that matter to this component, with any risks
- the test results
- whether it's ready to merge
- the test, lint and hassfest results
- whether it's ready to merge, and whether it can ship stable or only as a beta

If a bot PR has no CI runs, remind the user to close and reopen it, or to set up the `BOT_TOKEN` secret. Don't commit, push, or merge unless asked.
34 changes: 24 additions & 10 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
---
name: release
description: Cut a beta or stable release of the Hive custom component - bump manifest.json, publish the GitHub release with a matching tag, and verify the Release workflow. Use when the user asks to release, ship, tag, or publish a version or beta.
description: Cut a beta or stable release of the Hive custom component - check for the newest pyhive-integration (betas included) when releasing a beta, bump manifest.json, publish the GitHub release with a matching tag, and verify the Release workflow. Use when the user asks to release, ship, tag, or publish a version or beta.
---

# Release

Follow MAINTAINING.md → "Releasing". The rules that matter:
- The tag must equal `version` in `custom_components/hive/manifest.json`, for example `2026.10.0` or `2026.10.0b1`.
Follow MAINTAINING.md → "Releasing", and the principles in `CLAUDE.md`. The rules that matter:
- **Betas are the fast, opt-in channel.** They're how new `pyhive-integration` builds and fixes reach users before core HA ships them. Stable releases go to every HACS user.
- The tag must equal `version` in `custom_components/hive/manifest.json`, for example `2026.10.0` or `2026.10.0b1`. Use a lowercase `b`, with no dot before it.
- Beta tags (`bN`) are always published as pre-releases. Stable tags never are.
- A stable release must pin a stable `pyhive-integration`. A pre-release pin only ships in a beta. `check_release.py` fails the Release run otherwise, but by then the release is already public, so check before publishing.
- Publishing a release is visible to every HACS user, and so is deleting one. **Confirm the exact tag and whether it is a pre-release with the user before running `gh release create`.** Never commit, push, or open PRs unless the user asks.

## Steps
Expand All @@ -19,30 +21,42 @@ Follow MAINTAINING.md → "Releasing". The rules that matter:
- Same month: bump the patch number.
- Beta: add `b1`, or the next free `bN`.

2. **Check it's safe to ship.**
2. **Check pyhive-integration.**
- **Beta:** always look up the newest version on PyPI, pre-releases included:
```bash
curl -fsS https://pypi.org/pypi/pyhive-integration/json | jq -r '.releases | keys[]' \
| .venv/bin/python -c 'import sys; from packaging.version import Version; print(max(sys.stdin.read().split(), key=Version))'
jq -r '.requirements[]' custom_components/hive/manifest.json
```
If it's newer than the pin, run `/bump-pyhive` for it first, and get that merged to `master`, unless the user decides to skip it. Check for an open `deps/pyhive-integration-*` PR before starting a new one.
- **Stable:** the pin must not be a pre-release. If `master` pins a pyhive beta, stop: either wait for a stable pyhive, or move the pin to the latest stable with `/bump-pyhive` first.

3. **Check it's safe to ship.**
- `gh run list -w ci.yml -b master -L 1` and `gh run list -w compat.yml -L 1` must both be green. If either is red, stop and report. For a red Compatibility run, suggest `/triage-compat`.
- A hassfest **warning** about `pyhive-integration` differing from core hive is expected (see `CLAUDE.md`). Only errors count.
- `script/test` and `script/lint --check` must pass locally.
- Check `git log <last-tag>..origin/master --oneline`. If there's nothing to release, say so.

3. **Bump the manifest.** Change only the `version` value in `manifest.json`. Don't touch `requirements`; that's `/bump-pyhive`. The change must reach `master` before tagging:
4. **Bump the manifest.** Change only the `version` value in `manifest.json`. Don't touch `requirements`; that's `/bump-pyhive`. The change must reach `master` before tagging:
- If the user asks, make a branch `release/<version>`, commit it, and open a PR.
- Otherwise hand the change over for them to commit.
- Wait until it's merged, and check with `git fetch && git show origin/master:custom_components/hive/manifest.json | jq -r .version`.
- Wait until it's merged, then check with `git fetch && git show origin/master:custom_components/hive/manifest.json | jq -r '.version, .requirements[]'`.

4. **Publish** (only after the user confirms the tag):
5. **Publish** (only after the user confirms the tag):
```bash
gh release create <version> --target master --generate-notes --prerelease # beta
gh release create <version> --target master --generate-notes --latest # stable
```
For a beta, add the `pyhive-integration` version to the notes, and say whether it's ahead of core's.

5. **Verify.**
6. **Verify.**
- Watch the Release workflow:
```bash
gh run watch $(gh run list -w release.yml -L1 --json databaseId -q '.[0].databaseId') --exit-status
```
- Then confirm `hive.zip` is attached: `gh release view <version> --json assets -q '.assets[].name'`.
- If the check job fails, the release is already visible to HACS users. Tell the user, and offer `gh release delete <version> --cleanup-tag` (it needs their OK), then a corrected release.

6. **Clean up** after a stable release that replaces betas. Offer `gh workflow run delete_beta.yml`. It keeps the newest pre-release and never touches stable releases.
7. **Clean up** after a stable release that replaces betas. Offer `gh workflow run delete_beta.yml`. It keeps the newest pre-release and never touches stable releases.

Report the release URL, the Release run result and anything left for the user to do.
Report the release URL, the `pyhive-integration` version it ships, the Release run result and anything left for the user to do.
3 changes: 3 additions & 0 deletions .claude/skills/repo-health/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ Compare what you find with MAINTAINING.md:
- **Unreleased commits on master:** suggest a release if there are user-facing changes.
- **Branch protection:** the required checks should be `HACS validation`, `Hassfest`, `Lint` and `Tests`, with code owner review on.
- **Secrets:** `BOT_TOKEN` should be present.
- **Hassfest:** a warning that `pyhive-integration` differs from core hive is expected (see `CLAUDE.md`), so don't report it as a problem.
- **pyhive-integration:** compare the manifest pin with core hive's pin and with the newest version on PyPI, pre-releases included. A newer pyhive beta than the pin is a candidate for the next component beta (`/bump-pyhive`, then `/release`). A pre-release pin on `master` blocks stable releases until it's moved to a stable version.
- **Latest beta release:** the tag should match the `version` in its manifest (`git show <tag>:custom_components/hive/manifest.json | jq -r .version`).
- **Pinned HA:** compare the HA version in the `requirements_test.txt` comment with the latest stable on PyPI (`curl -fsS https://pypi.org/pypi/homeassistant/json | jq -r .info.version`). A gap of more than one minor release means the Dependabot PR is stuck.

Report as a short table with ✅ / ⚠️ / ❌ and one line each. Then list the actions in priority order, each with the skill to use (`/triage-compat`, `/bump-pyhive`, `/release`) or the settings page to change.
4 changes: 3 additions & 1 deletion .claude/skills/triage-compat/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ description: Diagnose and fix a failing nightly Compatibility workflow (compat.y

# Triage Compatibility

Principle 1 in `CLAUDE.md` applies: this component extends core's hive integration and reuses it wherever possible, so fixes adapt to core rather than fork it.

Background: most platform files (`binary_sensor.py`, `climate.py`, `light.py`, `switch.py`, `water_heater.py`, `entity.py`) re-export `homeassistant.components.hive.*`. `__init__.py`, `sensor.py` and `config_flow.py` are this repo's own code, but they rely on HA helpers and on core's `HiveEntity`. HA changes break things here without any commit in this repo.

## 1. Find what failed
Expand All @@ -20,7 +22,7 @@ Map it using the table in MAINTAINING.md → "Compatibility is red":
- **beta** fails: must be fixed before the next monthly HA release.
- **dev** smoke import fails: an import was renamed or removed upstream.
- The resolver step fails with "No pytest-homeassistant-custom-component release pins…": the plugin isn't published yet. Rerun later with `gh run rerun <id> --failed`. There's nothing to fix.
- **HACS/hassfest** fails: follow the validator's message.
- **HACS/hassfest** fails: follow the validator's message. A `[REQUIREMENTS]` message about `pyhive-integration` differing from core hive is expected, and `.github/scripts/hassfest.py` reports it as a warning (see `CLAUDE.md`). If it's failing on that, hassfest has reworded the message, so update the `ALLOWED` pattern in that script. Don't loosen the pin.

## 2. Reproduce locally
Use a separate venv so `.venv` (which matches CI's pinned HA) stays untouched:
Expand Down
18 changes: 16 additions & 2 deletions .github/scripts/check_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@

HACS installs by tag, so the tag must match the manifest version (compared as
versions, so tag 2026.3.1.b1 matches manifest 2026.3.1b1). A beta version must be
a GitHub pre-release, otherwise HACS offers it to every user.
a GitHub pre-release, otherwise HACS offers it to every user. A stable release
must also pin a stable pyhive-integration: pre-release libraries only ship in the
opt-in beta channel (see CLAUDE.md).
"""

import json
from pathlib import Path
import sys

from packaging.requirements import Requirement
from packaging.utils import canonicalize_name
from packaging.version import InvalidVersion, Version

MANIFEST = (
Expand All @@ -19,7 +23,8 @@


def main(tag: str, prerelease: bool) -> int:
manifest_version = json.loads(MANIFEST.read_text(encoding="utf-8"))["version"]
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
manifest_version = manifest["version"]
errors = []
try:
tag_version = Version(tag)
Expand All @@ -42,6 +47,15 @@ def main(tag: str, prerelease: bool) -> int:
f"{tag} is marked as a pre-release but isn't a beta version "
"(expected something like 2026.9.0b1)."
)
if not tag_version.is_prerelease:
for req in map(Requirement, manifest.get("requirements", [])):
if canonicalize_name(req.name) == "pyhive-integration" and any(
Version(spec.version).is_prerelease for spec in req.specifier
):
errors.append(
f"{tag} is a stable release but pins {req}. Stable releases go "
"to every HACS user, so ship pre-release pyhive in a beta instead."
)

for error in errors:
print(f"::error::{error}")
Expand Down
Loading
Loading