diff --git a/.github/workflows/hassfest.yml b/.github/workflows/hassfest.yml new file mode 100644 index 0000000..cce8e34 --- /dev/null +++ b/.github/workflows/hassfest.yml @@ -0,0 +1,18 @@ +name: Validate with hassfest + +on: + push: + pull_request: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: home-assistant/actions/hassfest@master diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..16a31a3 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,22 @@ +name: Tests + +on: + push: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install test runner + run: python -m pip install --upgrade pytest + - name: Run release-readiness tests + run: python -m pytest -q diff --git a/.github/workflows/validate-hacs.yml b/.github/workflows/validate-hacs.yml new file mode 100644 index 0000000..ef21b47 --- /dev/null +++ b/.github/workflows/validate-hacs.yml @@ -0,0 +1,19 @@ +name: Validate with HACS + +on: + push: + pull_request: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +permissions: {} + +jobs: + validate-hacs: + runs-on: ubuntu-latest + steps: + - name: HACS validation + uses: hacs/action@main + with: + category: integration diff --git a/AUTHENTICATION_ARCHITECTURE.md b/AUTHENTICATION_ARCHITECTURE.md new file mode 100644 index 0000000..2c06344 --- /dev/null +++ b/AUTHENTICATION_ARCHITECTURE.md @@ -0,0 +1,231 @@ +# Authentication Distribution Research + +This document records release-blocker research only. It does not change the +working authentication implementation. + +## Current blocker + +The working integration relies on application authentication material recovered +from the official Sharp Kitchen application, including a Cognito application +client credential and a shared mTLS client certificate/private key. The formal +HACS release must not embed the extracted private key. + +## Options under consideration + +### 1. User-provided Sharp application credential + user-provided mTLS PEM files + +Home Assistant can store user-entered OAuth client ID/client secret values +through its Application Credentials framework. The mTLS certificate/private key +would still need a separate mechanism, such as paths under the Home Assistant +configuration directory or values entered during a setup/reconfigure flow. + +Pros: +- public repository contains no extracted private key; +- no hosted relay service; +- closest to normal local secret management. + +Cons: +- ordinary Sharp users do not appear to have a supported developer portal for + creating their own Sharp Kitchen OAuth client; +- obtaining the same vendor-app credentials and mTLS private key currently + requires reverse-engineering/extraction work and is not a realistic normal + installation experience. + +### 2. User-supplied credential bundle produced by a separate extraction helper + +Keep extracted credential material out of the HACS repository. A separate tool +could produce a local bundle that the integration imports. + +Pros: +- HACS repository remains credential-free; +- integration setup could validate the bundle before saving paths/material. + +Cons: +- extraction of the mTLS private key remains technically difficult; +- the current research indicates the app's bundled PKCS#12 material is not a + simple user-exportable credential; +- adds a substantial prerequisite and support burden. + +### 3. Supported Sharp provisioning/pairing flow that generates per-installation mTLS credentials + +This is the preferred architecture if Sharp exposes such a flow. + +Pros: +- cleanest security model; +- no shared private key distribution; +- comparable to integrations that pair and generate/store a unique client + certificate for each Home Assistant installation. + +Cons: +- no such Sharp provisioning endpoint has been identified yet; +- requires further protocol research before it can be considered viable. + +### 4. User-provided certificate/key files obtained independently + +The integration would accept certificate and private-key paths or pasted PEM +material, but would not prescribe or automate extraction. + +Pros: +- technically straightforward for the Home Assistant integration; +- public repository stays free of the private key. + +Cons: +- poor user experience; +- most users cannot realistically obtain the material; +- support/documentation would still need to address how legitimate users obtain + it. + +### 5. Project-operated relay/proxy service holding the shared mTLS key + +Home Assistant would authenticate users locally, but Sharp API calls requiring +mTLS would transit a service controlled by the project. + +Pros: +- HACS package contains no Sharp private key; +- easiest end-user setup. + +Cons: +- creates a new hosted service, operating cost, privacy/security responsibility, + availability dependency, and trust boundary; +- routes appliance/account traffic through a third party; +- not recommended for this project unless all direct-client approaches fail. + +## Home Assistant Application Credentials + +Application Credentials is suitable for storing/selecting a user-provided +OAuth client ID and client secret. It does not store or provision a TLS client +certificate/private key. + +Sharp also uses a hosted-login flow tied to the official application's OAuth +client/redirect behavior. Application Credentials therefore does not, by +itself, make the current Sharp login flow a standard Home Assistant OAuth flow. +A separate compatibility investigation is required before refactoring login to +Home Assistant's OAuth helpers. + +## Additional provisioning research + +Public Sharp material reviewed during release preparation continues to direct +SMD2489ES users through the Sharp Kitchen app for appliance pairing. The +published pairing flow covers Wi-Fi/app/account pairing but does not document +issuance of a new client TLS certificate or private key to third-party clients. + +Sharp also publishes an Alexa account-linking path using the user's Sharp +account. This proves that Sharp supports account linking outside the mobile app, +but it does not expose a public device-control API, a user-created OAuth client, +or a per-installation mTLS credential flow that Home Assistant could currently +reuse. + +Public GitHub/code searches for the Sharp Kitchen backend host, API path, +application package, and bundled PKCS#12 filename did not uncover an independent +implementation or a documented certificate-registration endpoint. + +## 2026 SHARP HOME migration findings + +Sharp introduced a newer **SHARP HOME** app in 2025. Current 2026 Sharp pairing +documentation distinguishes appliance revisions by model suffix rather than +migrating every existing SMD2489ES automatically: + +- `SMD2489ESF` uses the SHARP HOME app. +- `SMD2489ESA/B/D/E` use the older Sharp Kitchen app. + +The SHARP HOME Android package is `jp.co.sharp.chglobal.na`. Sharp's current +documentation also shows **SHARP HOME - Kitchen** account linking through Google +Home and Alexa using a Sharp account, with Google Home relinking required every +180 days. + +This is important because it demonstrates a newer Sharp cloud/account-linking +stack that supports third-party smart-home ecosystems. However, the public +documentation does **not** expose: + +- a third-party developer API for direct appliance control; +- a way for Home Assistant users to register their own OAuth application; +- a method to issue a Home Assistant-specific client TLS certificate/key; +- evidence that older Sharp Kitchen SMD2489ES revisions can migrate to the + SHARP HOME backend. + +Therefore the newer SHARP HOME ecosystem is worth separate future protocol +research, especially for `SMD2489ESF` and later models, but it does not yet +remove the credential blocker for the existing Sharp Kitchen implementation. + +### Best viable public-safe fallback at this checkpoint + +If no Sharp provisioning endpoint is discovered, the most practical direct +client design is: + +1. Remove vendor-app secrets/key material from the future public release only + after a tested replacement exists. +2. Let the user supply OAuth application values locally. +3. Store/select the OAuth client ID and client secret through Home Assistant + Application Credentials where compatible with Sharp's hosted-login behavior. +4. Let the user supply the mTLS certificate and private key as local files, + preferably under `/config/sharp_kitchen/`. +5. Store only file paths/references in the config entry, not private-key text. +6. Validate certificate/key parsing and matching during setup/reconfigure. +7. Keep extraction/import outside the HACS package, ideally through a separate + local helper if a repeatable extraction method can be made practical. + +This is not as user-friendly as true vendor provisioning, but it keeps the HACS +repository free of the extracted private key and avoids a project-operated +cloud relay. + +## Home Assistant architecture implications + +Home Assistant's current Application Credentials framework assumes that users +can obtain an OAuth client ID and client secret from the provider. It supports +standard authorization-code flows, PKCE, and custom OAuth implementations, but +it does not provision or store a TLS client certificate/private key as part of +that framework. + +Home Assistant Cloud Account Linking can provide a smoother OAuth experience +when the provider cooperates with Nabu Casa, but that would require Sharp to +support/approve an appropriate OAuth/API integration. It is not something this +project can manufacture from the existing mobile-app credentials. + +The Bosch SHC integration remains the strongest architectural comparison: +during registration it generates a unique SSL certificate/key pair and +registers that client with the controller. Sharp's published pairing flow does +not currently expose an equivalent client-registration step. + +### Recommended public-safe design if Sharp offers no provisioning API + +The best implementable design with the information currently available is: + +1. Keep the public HACS repository free of the extracted shared mTLS private key + and vendor-app client secret. +2. Provide a separate, local-only extraction/import helper that contains no + Sharp secret material itself and operates on a user's legitimately obtained + app installation/package. +3. Have that helper write the resulting credential bundle locally under a + dedicated Home Assistant configuration directory such as + `/config/sharp_kitchen/`. +4. Have the integration validate and consume the local certificate/key files + without copying them into `custom_components`. +5. Use Home Assistant Application Credentials for client ID/secret only if the + Sharp hosted-login implementation can be made compatible and users can + realistically obtain/provide those values. +6. Do not use a project-operated proxy unless direct-client approaches are + exhausted, because a proxy creates a new privacy/security/availability + dependency. + +This approach is less convenient than vendor-supported provisioning, but it is +the cleanest currently viable way to separate secret distribution from the +public HACS package while preserving direct communication between Home +Assistant and Sharp. + +## Current recommendation + +Do not change the working integration yet. + +Research in this order: + +1. Determine whether the Sharp backend/app has an undocumented supported + registration/provisioning operation capable of issuing a new client + certificate/key or otherwise removing the need for the shared app key. +2. If not, prototype a credential-free public integration that accepts + user-supplied client ID/secret and mTLS certificate/key material without + committing those values. +3. Evaluate whether a separate extraction/import helper can make that + user-supplied model realistic. +4. Treat a hosted relay as a last resort. + +No release, merge, tag, or HACS submission is authorized by this document. diff --git a/BRANDING.md b/BRANDING.md new file mode 100644 index 0000000..28e618b --- /dev/null +++ b/BRANDING.md @@ -0,0 +1,37 @@ +# Branding Preparation + +Home Assistant 2026.3 and later allows custom integrations to ship branding +inside `custom_components/sharp_kitchen/brand/`. + +## Current approved icon + +The user approved a brand-neutral Sharp Kitchen integration icon and it is now +present on the release-preparation branch at: + +`custom_components/sharp_kitchen/brand/icon.png` + +The current icon is a square 256×256 PNG created specifically for this +integration. It shows an original stainless-steel microwave drawer viewed +straight-on with the drawer slightly open, a subtle warm interior glow, a dark +navy/charcoal rounded-square background, a black control panel, and a small +digital display. + +The icon intentionally contains: + +- no manufacturer logo; +- no Sharp wordmark; +- no model number; +- no Wi-Fi symbol; +- no product photography; +- no copied corporate artwork. + +The design is intended to remain recognizable at small Home Assistant/HACS icon +sizes while being completely original and brand-neutral. + +## Optional future assets + +- `brand/[email protected]` — optional higher-resolution square icon. +- `brand/logo.png` — optional horizontal logo. +- Dark-mode variants only if needed for legibility. + +Branding preparation does not authorize a release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d2f2e0f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 aevans0001 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PRIVACY_REVIEW.md b/PRIVACY_REVIEW.md new file mode 100644 index 0000000..4f7bdec --- /dev/null +++ b/PRIVACY_REVIEW.md @@ -0,0 +1,72 @@ +# Privacy and Confidential-Information Review + +Last reviewed: 2026-10-06 (after approved brand-neutral icon replacement) + +Scope: release/hacs-readiness tracked release-preparation files and repository +metadata prepared for the future Sharp Kitchen 0.9.0 release. + +## Personal-information sweep + +No release-preparation file introduced: + +- personal email addresses; +- home/street addresses; +- IPv4 addresses; +- MAC addresses; +- Windows user-profile paths; +- UNC/network paths; +- personal credentials, refresh tokens, or account-specific identifiers. + +Release-preparation commits expose the GitHub account identity through normal +Git commit metadata; no personal email address was returned by the connected +GitHub tooling. + +A fresh scan of the full PR diff after replacing the earlier temporary icon with the approved brand-neutral icon found +no personal email addresses, IP addresses, MAC addresses, local user paths, +street address, personal name, bearer tokens, or AWS-style access keys in the +release-preparation changes. + +## Known intentional authentication material in the preserved working code + +The pre-existing working integration still contains Sharp application +authentication material recovered from the official application, including the +Cognito application credential and shared mTLS certificate/private-key +material. + +This material is the explicit hard blocker for formal HACS release. It was not +introduced by the HACS-readiness work and has not been removed, rotated, or +modified because doing so would break the preserved working implementation. + +## Release rule + +Repeat this review after any future authentication-architecture change and +before freezing the release commit. No tag, merge, release, or HACS submission +is authorized by this review. + + +## Current release-preparation diff verification + +The current `main...release/hacs-readiness` file list was reviewed after +branding, license, tests, and documentation updates. + +The release-preparation diff does **not** modify the Sharp authentication +implementation files `api.py`, `const.py`, or `config_flow.py`. The +working credential values and authentication protocol therefore remain +unchanged by this preparation work. + +A fresh pattern scan of the PR diff found no personal email address, IP address, +MAC address, local user path, street address, personal name, bearer token, or +AWS-style access key introduced by the release-preparation changes. + + +## Final safe-preparation sweep before authentication work + +After the final brand-neutral icon replacement and authentication-research +documentation updates, the complete PR diff was scanned again. + +Result: no newly introduced personal email address, IP address, MAC address, +Windows/UNC path, street address, personal name, bearer token, or AWS-style +access key was found. + +The authentication implementation itself remains unchanged and is still the +explicit hard blocker for public release. diff --git a/README.md b/README.md index 4e72097..933fed6 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,76 @@ # Sharp Kitchen Integration -A Home Assistant custom integration for Sharp Kitchen-connected appliances -(built and tested against a Sharp SMD2489ES microwave), talking directly -to Sharp's real cloud API. +Unofficial Home Assistant custom integration for Sharp Kitchen-connected appliances, built and tested with the Sharp SMD2489ES Microwave Drawer. -## Features +## Supported and verified behavior -- Real Cognito Hosted-UI OAuth2 login (with automatic token refresh) and - mutual TLS client-certificate authentication to Sharp's device API. -- Manual cook control: cook time (minutes/seconds), power level, - Start/Pause/Stop, Open Drawer. -- Smart Cook presets (built-in automatic cook programs), with a - preset selector, weight/quantity slider, and Start button. -- Device settings exposed as switches: Easy Wave, Sound, Reminder Sound. -- Sensors: status, door state, time remaining, power, temperature. +- Sharp Kitchen account sign-in through the same hosted login flow used by the official app. +- Automatic token refresh and cloud communication. +- Manual microwave controls: cook time, power level, start, pause, stop, and open drawer. +- Smart Cook support for the full 29-program SMD2489ES catalog. + - Numeric programs expose the applicable quantity/weight control. + - Option programs expose the applicable option selector. + - Sensor programs use the appliance's sensor-cook behavior. +- Device settings: Easy Wave, Sound, and Reminder Sound. +- Sensors for status, drawer/door state, remaining time, power, and temperature. + +The currently verified appliance is the Sharp SMD2489ES. Other Sharp Kitchen-connected appliances may use related cloud APIs, but their controls are not claimed as supported until they are specifically verified. + +## Release status + +Formal HACS release is currently **blocked** while the project investigates a +public-safe authentication architecture. The working implementation depends on +Sharp application authentication material, including shared mTLS client-key +material recovered from the official app. Those working values are being +preserved while the replacement architecture is researched. + +There is no published `0.9.0` release yet, and this repository should be +treated as development/testing software until that blocker is resolved. ## Installation -1. Copy the `custom_components/sharp_kitchen` folder into your Home - Assistant `config/custom_components/` directory. +### HACS + +HACS packaging is being prepared. Until the credential-distribution blocker is +resolved and a formal release is published, custom-repository installation is +for development/testing only. + +1. In HACS, add this repository as a custom repository with category **Integration**. +2. Install **Sharp Kitchen**. +3. Restart Home Assistant. +4. Go to **Settings > Devices & services > Add integration** and search for **Sharp Kitchen**. +5. Sign in with the same account used by the Sharp Kitchen app. + +### Manual + +1. Copy `custom_components/sharp_kitchen` into your Home Assistant `config/custom_components/` directory. 2. Restart Home Assistant. -3. Go to Settings > Devices & Services > Add Integration, search for - "Sharp Kitchen", and log in with your Sharp Kitchen account - credentials. +3. Go to **Settings > Devices & services > Add integration** and search for **Sharp Kitchen**. + +## Smart Cook + +The integration contains the 29-program SMD2489ES Smart Cook catalog. The Home Assistant entities adapt to the selected program: + +- **Weight/quantity** for numeric programs such as Beverage Reheat. +- **Option** for programs such as Soften Ice Cream. +- No secondary value for sensor programs such as Fish / Seafood. + +A low-level `sharp_kitchen.start_smart_cook` action remains available for backward compatibility, but normal use should go through the validated entities. + +## Notes + +- This integration communicates with Sharp's cloud service and therefore requires internet access. +- The project is reverse engineered from observed behavior of the official Sharp Kitchen app because no public Sharp Kitchen API is known. +- Appliance writes should be treated like physical appliance controls: verify the selected program/settings before starting a cook cycle. + +## Development and validation + +Release preparation includes HACS validation and Home Assistant hassfest validation. The repository intentionally contains only the Home Assistant integration; Appliance Control Center/dashboard presentation logic is maintained separately. -## Status +## License -Core cook/control functionality is fully working. Only 3 of Sharp's -~30 built-in Smart Cook presets are currently mapped (Defrost Ground -Meat, Beverage Reheat, Hot Water) — more will be added as they're -captured from the real app. +MIT License. See `LICENSE`. ## Disclaimer -This is an unofficial, community-reverse-engineered integration. It is -not affiliated with or endorsed by Sharp Corporation. +This project is unofficial and is not affiliated with or endorsed by Sharp Corporation. diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md new file mode 100644 index 0000000..856d587 --- /dev/null +++ b/RELEASE_CHECKLIST.md @@ -0,0 +1,57 @@ +# Sharp Kitchen 0.9.0 Release Checklist + +This checklist is intentionally conservative. Completing preparation items does +not authorize a release. + +## Hard blockers + +- [ ] Replace the current public-distribution model for the Sharp application + credential and shared mTLS private key. +- [ ] Confirm the replacement authentication design without breaking the + currently working integration. +- [ ] Repeat privacy/confidential-data review after the authentication change. + +## Repository / HACS + +- [x] Public GitHub repository +- [x] One Home Assistant integration under `custom_components/sharp_kitchen` +- [x] `hacs.json` +- [x] README +- [x] MIT LICENSE (also applied to default `main` branch) +- [x] Issue tracker URL in manifest +- [x] Code owner in manifest +- [x] Versioned custom integration manifest +- [x] HACS validation workflow +- [x] hassfest workflow +- [x] Release-readiness tests +- [x] Apply GitHub repository description +- [x] Apply GitHub repository topics +- [x] Add approved brand-neutral icon +- [x] HACS validation passes with no failed checks + +## Validation + +- [x] Offline release-readiness tests pass +- [x] hassfest passes after manifest/schema cleanup +- [x] HACS validation passes +- [ ] Re-run all checks at final release commit +- [x] Review current release diff against working code baseline +- [x] Confirm no unintended dashboard/frontend files are included + +## Release + +- [ ] Freeze exact release commit +- [ ] Confirm proposed version +- [x] Keep draft release notes current (final publication wording still blocked) +- [x] Final pre-auth privacy/confidential-data sweep +- [ ] User approves tag/release +- [ ] Create tag and GitHub release +- [ ] Verify HACS install from released artifact +- [ ] User approves HACS default-store submission +- [ ] Submit to HACS + +## Current prohibition + +Do not merge the draft release PR, tag `0.9.0`, publish a GitHub release, +remove or rotate the working Sharp credentials, or submit to HACS while the +authentication architecture remains unresolved. diff --git a/RELEASE_NOTES_0.9.0.md b/RELEASE_NOTES_0.9.0.md new file mode 100644 index 0000000..70f0998 --- /dev/null +++ b/RELEASE_NOTES_0.9.0.md @@ -0,0 +1,56 @@ +# Sharp Kitchen 0.9.0 — Draft Release Notes + +> **Not for publication yet.** The formal release is blocked until the Sharp +> application credential and shared mTLS private-key distribution problem has +> an acceptable public-release architecture. + +## Highlights + +- Home Assistant support for the Sharp SMD2489ES Microwave Drawer. +- Manual cook controls for cook time, power, start, pause, and stop. +- Drawer open control. +- Full 29-program SMD2489ES Smart Cook catalog. +- Adaptive Smart Cook controls: + - numeric quantity/weight controls where required; + - option selectors where required; + - sensor-cook programs without an unnecessary secondary value. +- Easy Wave, Sound, and Reminder Sound settings. +- Sensors for appliance status, drawer/door state, remaining time, power, + and temperature. +- UI-based Home Assistant configuration. +- HACS repository metadata and validation workflows. +- Original brand-neutral microwave-drawer icon added for HACS/Home Assistant presentation. +- Integration-wide actions registered once at Home Assistant integration + setup rather than once per config entry. +- English translations include both low-level cook actions. + +## Compatibility + +The Sharp SMD2489ES is the currently verified appliance. Support for other +Sharp Kitchen-connected appliances is not claimed until those appliances are +specifically verified. + +## Security / release blocker + +The working implementation currently depends on application authentication +material recovered from the official Sharp Kitchen application, including +mTLS client key material. Those working values remain unchanged on the +preserved implementation while a distribution architecture is investigated. + +No tag, GitHub release, or HACS default-store submission should be created +until that blocker is resolved and release validation is repeated. + +## Release preparation status + +- MIT License selected. +- Offline release-readiness tests pass. +- Home Assistant hassfest passes. +- HACS validation now passes all repository/integration checks after the GitHub description and topics were applied. +- Authentication research now documents Sharp's newer SHARP HOME ecosystem for later appliance revisions, but no public provisioning/API path has yet been found that resolves the existing Sharp Kitchen mTLS blocker. +- Draft PR #1 remains unmerged. +- No `0.9.0` tag or GitHub release exists. + +## Disclaimer + +Sharp Kitchen Integration is an unofficial community project and is not +affiliated with or endorsed by Sharp Corporation. diff --git a/REPOSITORY_METADATA.md b/REPOSITORY_METADATA.md new file mode 100644 index 0000000..668c35b --- /dev/null +++ b/REPOSITORY_METADATA.md @@ -0,0 +1,36 @@ +# Repository Metadata — Pending GitHub Settings Update + +Proposed GitHub repository description: + +> Home Assistant integration for Sharp Kitchen-connected appliances, including the SMD2489ES Microwave Drawer. + +Proposed repository topics: + +- home-assistant +- homeassistant +- hacs +- custom-integration +- sharp +- sharp-kitchen +- microwave +- smd2489es +- smart-home + +These values are release-preparation metadata only. They do not authorize a +release or HACS submission. + + +## Exact GitHub CLI command + +The connected GitHub toolset used during release preparation cannot mutate +repository About/Topics settings. If GitHub CLI is already authenticated, the +remaining metadata can be applied with: + +```powershell +Clear-Host +gh repo edit aevans0001/Sharp-Kitchen-Integration --description "Home Assistant integration for Sharp Kitchen-connected appliances, including the SMD2489ES Microwave Drawer." --add-topic home-assistant --add-topic homeassistant --add-topic hacs --add-topic custom-integration --add-topic sharp --add-topic sharp-kitchen --add-topic microwave --add-topic smd2489es --add-topic smart-home + +``` + +Applying repository description/topics does not merge the release PR, tag a +release, change authentication, or publish to HACS. diff --git a/custom_components/sharp_kitchen/__init__.py b/custom_components/sharp_kitchen/__init__.py index 46d9a1d..8a05abc 100644 --- a/custom_components/sharp_kitchen/__init__.py +++ b/custom_components/sharp_kitchen/__init__.py @@ -16,6 +16,7 @@ import voluptuous as vol from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -25,6 +26,8 @@ _LOGGER = logging.getLogger(__name__) +CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) + PLATFORMS = ["sensor", "switch", "button", "number", "select"] SERVICE_START_COOK = "start_cook" @@ -50,25 +53,30 @@ ) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - session = async_get_clientsession(hass) - client = SharpKitchenClient( - session, - refresh_token=entry.data[CONF_REFRESH_TOKEN], - phone_id=entry.data[CONF_PHONE_ID], - username=entry.data[CONF_COGNITO_USERNAME], - ) - - coordinator = SharpKitchenCoordinator(hass, client) - await coordinator.async_refresh_devices() - await coordinator.async_config_entry_first_refresh() +def _coordinator_for_device( + hass: HomeAssistant, device_id: int +) -> SharpKitchenCoordinator: + """Return the loaded coordinator that owns a Sharp backend device ID.""" + matches = [ + coordinator + for coordinator in hass.data.get(DOMAIN, {}).values() + if device_id in coordinator.devices + ] + if not matches: + raise HomeAssistantError(f"Sharp Kitchen device ID {device_id} is not loaded") + if len(matches) > 1: + raise HomeAssistantError( + f"Sharp Kitchen device ID {device_id} is ambiguous across configured accounts" + ) + return matches[0] - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) +async def async_setup(hass: HomeAssistant, config: dict) -> bool: + """Register integration-wide actions once, independent of config entries.""" async def _handle_start_cook(call: ServiceCall) -> None: - await client.async_start_cook( + coordinator = _coordinator_for_device(hass, call.data["device_id"]) + await coordinator.client.async_start_cook( call.data["device_id"], mode=call.data["mode"], cook_time=call.data["cook_time"], @@ -78,25 +86,43 @@ async def _handle_start_cook(call: ServiceCall) -> None: ) await coordinator.async_request_refresh() - hass.services.async_register( - DOMAIN, SERVICE_START_COOK, _handle_start_cook, schema=START_COOK_SCHEMA - ) - async def _handle_start_smart_cook(call: ServiceCall) -> None: - await client.async_start_smart_cook( + coordinator = _coordinator_for_device(hass, call.data["device_id"]) + await coordinator.client.async_start_smart_cook( call.data["device_id"], call.data["auto_number"], call.data["auto_weight"], ) await coordinator.async_request_refresh() + hass.services.async_register( + DOMAIN, SERVICE_START_COOK, _handle_start_cook, schema=START_COOK_SCHEMA + ) hass.services.async_register( DOMAIN, SERVICE_START_SMART_COOK, _handle_start_smart_cook, schema=START_SMART_COOK_SCHEMA, ) + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + session = async_get_clientsession(hass) + client = SharpKitchenClient( + session, + refresh_token=entry.data[CONF_REFRESH_TOKEN], + phone_id=entry.data[CONF_PHONE_ID], + username=entry.data[CONF_COGNITO_USERNAME], + ) + + coordinator = SharpKitchenCoordinator(hass, client) + await coordinator.async_refresh_devices() + await coordinator.async_config_entry_first_refresh() + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/custom_components/sharp_kitchen/brand/icon.png b/custom_components/sharp_kitchen/brand/icon.png new file mode 100644 index 0000000..84fcf2d Binary files /dev/null and b/custom_components/sharp_kitchen/brand/icon.png differ diff --git a/custom_components/sharp_kitchen/manifest.json b/custom_components/sharp_kitchen/manifest.json index b53b2b4..fd03ce1 100644 --- a/custom_components/sharp_kitchen/manifest.json +++ b/custom_components/sharp_kitchen/manifest.json @@ -4,9 +4,8 @@ "codeowners": ["@aevans0001"], "config_flow": true, "documentation": "https://github.com/aevans0001/Sharp-Kitchen-Integration", - "issue_tracker": "https://github.com/aevans0001/Sharp-Kitchen-Integration/issues", "integration_type": "hub", "iot_class": "cloud_polling", - "requirements": ["cryptography"], - "version": "0.8.2" + "issue_tracker": "https://github.com/aevans0001/Sharp-Kitchen-Integration/issues", + "version": "0.9.0" } diff --git a/custom_components/sharp_kitchen/strings.json b/custom_components/sharp_kitchen/strings.json deleted file mode 100644 index 03871ec..0000000 --- a/custom_components/sharp_kitchen/strings.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "config": { - "step": { - "user": { - "title": "Connect to Sharp Kitchen", - "description": "Enter the email and password for your Sharp Kitchen (na-kitchen) account -- the same one used in the Sharp Kitchen phone app.", - "data": { - "email": "Email", - "password": "Password" - } - } - }, - "error": { - "invalid_auth": "Login failed. Check your email and password. If they're correct, Sharp's login may require a login method this integration doesn't support yet (see the README).", - "cannot_connect": "Logged in, but could not reach the Sharp Kitchen device service to verify your account. Please try again." - }, - "abort": { - "already_configured": "This Sharp Kitchen account is already set up." - } - }, - "services": { - "start_cook": { - "name": "Start Cook", - "description": "Start a manual cook on a Sharp Kitchen device.", - "fields": { - "device_id": { - "name": "Device ID", - "description": "The numeric device ID from Sharp's backend." - }, - "mode": { - "name": "Mode", - "description": "Cook mode name (\"microwave\" is confirmed working)." - }, - "cook_time": { - "name": "Cook Time", - "description": "Cook duration as H:MM:SS." - }, - "power": { - "name": "Power Level", - "description": "Microwave power level." - }, - "temperature": { - "name": "Temperature", - "description": "Target temperature for modes that use one." - }, - "with_preheat": { - "name": "With Preheat", - "description": "Whether to preheat before cooking." - } - } - } - } -} diff --git a/custom_components/sharp_kitchen/translations/en.json b/custom_components/sharp_kitchen/translations/en.json index 03871ec..77dccce 100644 --- a/custom_components/sharp_kitchen/translations/en.json +++ b/custom_components/sharp_kitchen/translations/en.json @@ -48,6 +48,24 @@ "description": "Whether to preheat before cooking." } } + }, + "start_smart_cook": { + "name": "Start Smart Cook", + "description": "Run a low-level Sharp Smart Cook command. Normal use should use the Smart Cook entities.", + "fields": { + "device_id": { + "name": "Device ID", + "description": "The numeric device ID from Sharp's backend." + }, + "auto_number": { + "name": "Preset Code", + "description": "Raw Smart Cook preset identifier from the Sharp app catalog." + }, + "auto_weight": { + "name": "App Parameter Value", + "description": "Exact app parameter value for the selected Smart Cook preset." + } + } } } } diff --git a/hacs.json b/hacs.json new file mode 100644 index 0000000..b3eca99 --- /dev/null +++ b/hacs.json @@ -0,0 +1,3 @@ +{ + "name": "Sharp Kitchen" +} diff --git a/tests/test_release_readiness.py b/tests/test_release_readiness.py new file mode 100644 index 0000000..a44c526 --- /dev/null +++ b/tests/test_release_readiness.py @@ -0,0 +1,102 @@ +"""Release-readiness tests that do not contact Sharp or a live appliance.""" + +from __future__ import annotations + +import json +from pathlib import Path +import runpy + +ROOT = Path(__file__).resolve().parents[1] +INTEGRATION = ROOT / "custom_components" / "sharp_kitchen" + + +def _constants() -> dict: + return runpy.run_path(INTEGRATION / "const.py") + + +def test_manifest_identity_and_proposed_version() -> None: + manifest = json.loads((INTEGRATION / "manifest.json").read_text(encoding="utf-8")) + assert manifest["domain"] == "sharp_kitchen" + assert manifest["name"] == "Sharp Kitchen" + assert manifest["version"] == "0.9.0" + assert manifest["config_flow"] is True + assert manifest["iot_class"] == "cloud_polling" + + +def test_hacs_metadata_is_valid_json() -> None: + hacs = json.loads((ROOT / "hacs.json").read_text(encoding="utf-8")) + assert hacs["name"] == "Sharp Kitchen" + + +def test_smart_cook_catalog_has_all_29_programs() -> None: + presets = _constants()["SMART_COOK_PRESETS"] + assert len(presets) == 29 + assert len({preset["name"] for preset in presets.values()}) == 29 + assert {preset["parameter_type"] for preset in presets.values()} == { + "numeric", + "option", + "sensor", + } + + +def test_smart_cook_catalog_parameter_rules() -> None: + presets = _constants()["SMART_COOK_PRESETS"] + + for preset_id, preset in presets.items(): + parameter_type = preset["parameter_type"] + assert preset_id + assert preset["name"] + assert preset["category"] + + if parameter_type == "numeric": + minimum = float(preset["min_value"]) + maximum = float(preset["max_value"]) + default = float(preset["default_value"]) + step = float(preset["step"]) + assert minimum <= default <= maximum + assert step > 0 + assert preset.get("unit") + elif parameter_type == "option": + options = preset["options"] + assert options + assert str(preset["default_value"]) in options + assert all(str(key) and str(label) for key, label in options.items()) + else: + assert parameter_type == "sensor" + assert str(preset["default_value"]) == "0" + + +def test_english_translation_covers_both_actions() -> None: + translation = json.loads( + (INTEGRATION / "translations" / "en.json").read_text(encoding="utf-8") + ) + services = translation["services"] + assert "start_cook" in services + assert "start_smart_cook" in services + + +def test_custom_integration_does_not_ship_core_strings_json() -> None: + assert not (INTEGRATION / "strings.json").exists() + + +def test_mit_license_is_present() -> None: + license_text = (ROOT / "LICENSE").read_text(encoding="utf-8") + assert license_text.startswith("MIT License") + assert "Permission is hereby granted, free of charge" in license_text + assert "Copyright (c) 2026 aevans0001" in license_text + + +def test_release_blocker_is_documented() -> None: + readme = (ROOT / "README.md").read_text(encoding="utf-8") + assert "Formal HACS release is currently **blocked**" in readme + assert "There is no published `0.9.0` release yet" in readme + + +def test_brand_icon_is_256_square_png() -> None: + icon = INTEGRATION / "brand" / "icon.png" + data = icon.read_bytes() + assert data[:8] == b"\x89PNG\r\n\x1a\n" + assert data[12:16] == b"IHDR" + width = int.from_bytes(data[16:20], "big") + height = int.from_bytes(data[20:24], "big") + assert (width, height) == (256, 256)