feat(tools): eight_sleep tool (unofficial cloud API)#6471
Open
theonlyhennygod wants to merge 1 commit intomasterfrom
Open
feat(tools): eight_sleep tool (unofficial cloud API)#6471theonlyhennygod wants to merge 1 commit intomasterfrom
theonlyhennygod wants to merge 1 commit intomasterfrom
Conversation
Adds an `eight_sleep` Tool that drives an 8Sleep Pod via 8Sleep's unofficial cloud API, following the auth and endpoint conventions of the open-source pyEight library. v1 supports `get_bed_state`, `get_metrics`, and per-side `set_temperature` (-100 cools, 0 holds, +100 warms). Alarm scheduling and prime-cycle control are deferred to follow-ups: alarms have a wider API surface and higher safety stakes (a wrong wake time is bad UX), and prime endpoints differ between Pod generations. `EightSleepConfig` is disabled by default and carries `#[integration(category = "ToolsAutomation", display_name = "8Sleep", ...)]` so the schema-driven registry surfaces it automatically. The `password` field is `#[secret]`-encrypted at rest and falls back to `EIGHT_SLEEP_PASSWORD`. The session token is cached in memory only and refreshed on `401`. `set_temperature` is gated by the `allowed_sides` allowlist (defaults to `["left", "right"]`); empty allowlist blocks all mutations while leaving reads available. Setup-guide doc carries an explicit "unofficial API" disclaimer and documents the Cognito-account limitation (newer accounts on the mandatory OAuth flow are not supported in v1). Closes #6450
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
mastereight_sleepTool that drives an 8Sleep Pod via 8Sleep's unofficial cloud API. The integration was previously a placeholder; this gives operators agent-driven temperature control and read-only access to bed state and sleep metrics.{email, password}→<api_base_url>/login, cachesession.tokenin memory (no on-disk persistence), retry once with a fresh token on401. Token sent viaSession-Tokenheader per pyEight conventions.get_bed_state,get_metrics,set_temperature(-100..=100per side).set_temperatureis gated byToolOperation::ActAND a configurableallowed_sidesallowlist enforced server-side inexecute().#[integration(category = "ToolsAutomation", display_name = "8Sleep", description = "Smart mattress (unofficial API)", status_field = "enabled")]onEightSleepConfig.set_alarmin the proposal, but alarm scheduling has wider API surface (recurring vs one-off, light vs sound vs vibration) and high safety stakes (wrong wake = bad UX). Deferred to a follow-up where the wire format can be verified against an active reverse-engineered library./loginflow that pyEight relies on.zeroclaw-config(additive[eight_sleep]block, default disabled),zeroclaw-tools(new module + tokioMutexfor in-memory token cache),zeroclaw-runtime/src/tools/mod.rs(config-gated registration + tool re-export),zeroclaw-runtime/src/integrations/mod.rs(setup-hint arm), docs.Closes #6450. Sibling tracking issues: [Feature]: Home Assistant integration tool #6448 (HA, in PR feat(tools): home_assistant tool for HA REST API #6464), [Feature]: Philips Hue integration tool #6449 (Hue, in PR feat(tools): philips_hue tool for local Hue Bridge #6470).Validation Evidence (required)
cargo fmt --all -- --check cargo clippy -p zeroclaw-config -p zeroclaw-tools -p zeroclaw-runtime --all-targets -- -D warnings cargo test -p zeroclaw-config -p zeroclaw-tools -p zeroclaw-runtimecargo fmt --all -- --check→ clean (no diff aftercargo fmt --all).cargo clippy ...→Finisheddevprofile [unoptimized + debuginfo] target(s) in 9.77s. No warnings.cargo test -p zeroclaw-config -p zeroclaw-tools -p zeroclaw-runtime→test result: ok. 620 passed; 0 failed; 0 ignored(zeroclaw-config)test result: ok. 1630 passed; 0 failed; 1 ignored(zeroclaw-tools)test result: ok. 1162 passed; 0 failed; 0 ignored(zeroclaw-runtime)eight_sleep::tests::*— name, description (asserts the unofficial-API warning is present), schema shape (action enum + level bounds), URL/base normalization, side-allowlist trimming and case-folding,build_temperature_bodyshape (correct per-side fields, no leakage to the other side, negative-level handling), missing-action / unknown-action / missing-side / invalid-side / missing-level / out-of-range / disallowed-side / empty-allowlist paths,spec()reflection.8Sleepautomatically — confirmed by reading the newall_integrations(&Config)loop, which consumesConfig::integration_descriptors()(auto-generated from the#[integration(...)]attribute onEightSleepConfig).zeroclaw integrations info "8Sleep"prints the[eight_sleep]config example with the unofficial-API disclaimer (new arm inshow_integration_info).set_temperaturerequests. Unit tests cover gating logic exhaustively (allowlist, side validation, level bounds, security policy) and request-body assembly (build_temperature_body), but the wire format on the actual cloud endpoint is verified only against pyEight conventions, not a live cluster../dev/ci.sh allfor the same reason as feat(tools): home_assistant tool for HA REST API #6464 / feat(tools): philips_hue tool for local Hue Bridge #6470: change is scoped to three crates + docs + a changelog entry; targeted clippy+test runs already gated those crates with-D warnings.Security & Privacy Impact (required)
https://client-api.8slp.net/v1(or whateverapi_base_urlresolves to). Disabled by default; operator opts in.email/passwordfields onEightSleepConfig.passwordcarries#[secret](encrypted at rest when[secrets] encrypt = true) and falls back toEIGHT_SLEEP_PASSWORDenv var. The minted session token is held intokio::sync::Mutex<Option<SessionToken>>and never written to disk.user@example.invalid, neutral synthetic credentials, andhttps://example.invalid/v1for URL normalization.#[secret]storage, env-var fallback so the password never has to land in a config file, in-memory-only token cache, and anallowed_sidesallowlist that lets operators throttle the agent to one side of a shared bed.Compatibility (required)
enabled: false.Config::default()includeseight_sleep: EightSleepConfig::default().[eight_sleep]block; newEIGHT_SLEEP_PASSWORDenv var fallback. No CLI changes.[eight_sleep] enabled = true,email, and eitherpasswordorEIGHT_SLEEP_PASSWORD. Optionally narrowallowed_sides.Rollback (required for
risk: mediumandrisk: high)[eight_sleep] enabled = falsein~/.zeroclaw/config.toml(or remove the block) and restart the agent. The tool is unregistered on next boot.eight_sleep.enabled(master switch).eight_sleep.allowed_sides(per-mutation throttle; empty disables allset_temperaturecalls while keeping reads).eight_sleep.api_base_url(in case 8Sleep moves endpoints — operator can pin without a code change).eight_sleep: enabled but no password found ...or... email is empty — skipping registrationindicate config issues.8Sleep login failed (401)indicates wrong creds or a Cognito-mandated account.8Sleep {VERB} {path} failed (4xx/5xx)indicates upstream API issues — most likely 8Sleep changed an endpoint and the integration needs a follow-up./api/toolswill not includeeight_sleepif email or password is missing.