Commit Graph
24 Commits
Author SHA1 Message Date
Marc Billow 13f1214790 Merge branch 'main' into contrib/cooktop-range-hood 2026-07-22 22:39:19 -05:00
themaanda e7daa21931 Merge remote-tracking branch 'upstream/main' into contrib/cooktop-range-hood
# Conflicts:
#	README.md
#	custom_components/localthings/registry/by_type/__init__.py
#	tests/test_by_type.py
#	tests/test_golden_regression.py
2026-07-22 21:53:45 -05:00
Marc Billow f783aa72b9 Add range/cooktop-oven combo to supported appliance types 2026-07-23 02:47:12 +00:00
Marc Billow c0298d57d9 fix: normalize washer dosing-select codes; stop blocking the loop in diagnostics (#9)
Two fixes for issue #9 (WW90T634DHE washer):

- washer dosing selects: the four detergent/softener dosing selects read their
  current value from `<Prefix>LevelCtrl_<code>` (un-padded, e.g. "3") but their
  options from `Supported<Prefix>LevelCtrl_<hexpairs>` (zero-padded, e.g. "03").
  HA's SelectEntity renders a select "unknown" whenever current_option is not in
  options, so all four sat "unknown" (idle and running) even though every other
  select worked -- which is why it was only those four. Normalize the current
  value to the supported code with the same integer value so it matches an
  option (and its translation); convert back to the device's native un-padded
  format on write.

- diagnostics: pkg_version("smartthings-local") reads package metadata off disk
  (listdir + open + read_text), tripping HA's event-loop blocking-call detector.
  Offload it to the executor. Audited the rest of the package: config_flow's
  socket/crypto and every coordinator DTLS call are already offloaded via
  async_add_executor_job -- this was the only blocking call left on the loop.

Also documents air-conditioner support in the README (device table, capability
module list, platform list), missed when that support landed.

Updates the washer dosing tests to the corrected value/write format and adds a
current-option-is-a-valid-option regression; adds a diagnostics test asserting
the version lookup runs off the event loop.
2026-07-21 23:49:30 +00:00
themaanda c64f58aaa5 Add cooktop and range hood support 2026-07-21 16:57:45 -05:00
Marc Billow 00c3ebaf75 feat: auto-detect DTLS port across the full 49152-49160 range
The config flow only probed 49154/49155, so appliances whose local
CoAP/DTLS API binds elsewhere in the ephemeral range (e.g. a dishwasher
answering on 49153) could never be added.

Add a fast UDP liveness sweep across 49152-49160 that uses the ICMP
port-unreachable / ECONNREFUSED asymmetry to find the live port(s)
before attempting the expensive DTLS handshake. Closed ports fall out
immediately; a live-but-silent port is kept as a candidate. The real
handshake then runs only against discovered ports, preferring the
historically known 49154/49155 when several look live.

Also drop the probe's /device/0 GET deadline from a bare 15s literal to
a named PROBE_GET_TIMEOUT_S constant (10s) — the slowest observed full
dump is ~8s, and 10s matches the per-resource read timeout used
elsewhere.

Bumps version to 0.5.0.
2026-07-20 21:52:35 +00:00
Marc Billow 9fee5b5ec0 docs: simplify smartthings-local intro blurb 2026-07-19 04:07:52 +00:00
Marc Billow f24d6a0ae6 docs: simplify and update README
Add missing washer support to the appliance table, fix stale test-count
and repo-layout claims, trim protocol-internals jargon that belongs to
the smartthings-local library rather than this integration, and point
"Adding a new appliance type" at HA's own diagnostics download instead
of a gitignored local script.
2026-07-19 04:05:37 +00:00
Marc Billow 72c5de3e8d docs: rename display name to LocalThings
"Local Things" read as two separate words; the integration is one name,
LocalThings (as in local SmartThings control).
2026-07-10 19:50:47 -05:00
Marc Billow 5b8a079fa6 chore: prepare repo for HACS listing
- Add hacs.json and MIT LICENSE required for HACS/default-store inclusion
- Fill required manifest.json keys (documentation, issue_tracker,
  codeowners) and reorder per hassfest's key-ordering rule
- Add validate.yml workflow running hassfest and HACS validation
- Update README install note now that hacs.json is checked in
2026-07-09 14:02:46 -05:00
Marc Billow d2699f0f5b Add diagnostics platform, config-flow gap warning, issue template
Completes the device-capability-diagnostics spec:

- diagnostics.py: the standard HA diagnostics hook, returning device type,
  one_ui_version, unbound hrefs, and the redacted raw resource tree, plus
  integration and smartthings-local version numbers. This is what the
  Repairs issue (added in the previous commit) points users at, and what
  they attach to a device-support issue.
- config_flow.py: _probe_and_validate now also reports oneUiVersion and
  whether the device type is recognized. Recognized types are unaffected;
  an unrecognized type shows a new confirm_unknown_type step explaining
  that only common capabilities will be available before creating the
  entry, so expectations are set at setup time rather than only after the
  fact via Repairs.
- .github/ISSUE_TEMPLATE/device-support.yml: structured template for
  filing a capability gap, linked from both the Repairs issue and the
  config-flow confirmation step.
- README: short section pointing at this whole mechanism.
2026-07-08 00:10:30 -05:00
Marc Billow b67e0e5cf5 Rename ocf/ to registry/, it's a capability registry now
The DTLS/CoAP transport code that made "ocf" an accurate name moved out to
the smartthings-local package. What's left here (capability.py, entities.py,
discovery.py, adapter.py, identity.py, capabilities/, by_type/, plus the
/device/0 batch parser) is entirely the device capability registry, so name
the package for what it does.

Flattened the redundant ocf/registry/ nesting into a single top-level
registry/ package and updated every import across the platform modules and
test suite accordingly. Verified: full test suite (80/80) passes, and the
Docker dev container reconnects to both live appliances and rediscovers
their entities cleanly after the rename.
2026-07-07 22:54:57 -05:00
Marc Billow 4c1497f5ba Bake smartthings-local into the dev Docker image
The dev container was relying on HA's runtime pip-install of manifest.json
requirements, which only fires when the integration is set up and needs
outbound network access at that exact moment; a container that had been
running since before the smartthings-local migration kept the old code
loaded in memory and never went through that install path, so restarting
it failed once it picked up the new manifest.json.

Repurpose the stale MQTT-bridge-era Dockerfile (its own code was already
deleted from this repo) to build on the official HA image with
smartthings-local pre-installed, and point docker-compose.yml at it via
`build: .`. Verified end-to-end: rebuilt the image, recreated the
container, and confirmed both live appliances (fridge, dishwasher)
reconnect and discover entities with no runtime install needed.
2026-07-07 22:38:38 -05:00
Marc Billow fd61e882d1 chore: update readme phrasing and instructions 2026-07-07 22:33:00 -05:00
Marc Billow ee068ed78d Rewrite README prose to remove AI-writing tropes
Replaced all em-dashes with plain punctuation, broke up the mechanical
bold-lead-in bullet list in "What you get" into prose paragraphs, and
removed the "X, not Y" contrastive framing and decorative arrows.
2026-07-07 22:25:01 -05:00
Marc Billow 12d60c295e Frame linked setup_cert.py as a reference example, not an instruction
Part 2 pointed readers to run smartthings-local's setup_cert.py directly;
reword it as an example of how to obtain the CA cert/key rather than a
step to execute, since this repo makes no claim about how that script
behaves or is maintained upstream.
2026-07-07 22:20:15 -05:00
Marc Billow 44afe4f00a Point README at upstream setup_cert.py instead of vendoring a copy
Getting the AC14K_M CA cert+key is a protocol-layer concern, not an
HA-integration concern, and the two copies here had already drifted from
each other. Drop root setup_cert.py + requirements-bootstrap.txt and have
Part 2 explain why the CA cert is needed, then link to the smartthings-local
project's setup_cert.py as the canonical way to obtain it.
2026-07-07 22:19:22 -05:00
Marc Billow 4782878583 Depend on published smartthings-local package, rewrite README
Now that mbillow/localthings#8's protocol fixes are merged upstream and
smartthings-local 0.1.0 is on PyPI, drop the vendored ocf/coap_dtls.py +
ocf/observe_refresh.py transport (dead code, never instantiated) and the
duplicated ocf_root_ca.pem in favor of the real package. manifest.json and
requirements-dev.txt now pin smartthings-local>=0.1.0 instead of the
unmerged git branch.

README rewritten from scratch — it still described the old standalone
MQTT-bridge (samsung_appliance/, main.py, docker-compose bridge) that this
repo moved off of; it now documents the real architecture: a native HA
custom component with config-flow-driven cert minting and a per-device-type
capability registry, with the protocol layer split out to smartthings-local.
2026-07-07 22:08:02 -05:00
Jack Nagy 00ff961d33 Drop dangling local-tools/ references from README 2026-06-30 19:51:36 +01:00
Quite Yellow 6f703fc148 Update README.md 2026-06-30 19:49:02 +01:00
Jack Nagy caf195ea1a Ship setup_cert.py to repo root, auto-fetch all CA materials
Closes #2.

Previously the cert minting script lived in local-tools/ (gitignored)
and the README pointed at a cert-only source that didn't include the
private key or upstream chain.

setup_cert.py now lives at the repo root and live-fetches both the
peer UUID (from the relevant TLS server cert subject DN) and the
full AC14K_M + upstream chain bundle (RemoteAccessCA + CECA + ROOTCA)
from a public mirror. Each fetch has an inline workaround if the
network is restricted (UUID=..., AC14K_M_CERT_BUNDLE=...,
BRAYSTORM_URL=...). Modulus-pair check catches a wrong-key mistake
before signing. bootstrap.py removed -- imported a package that was
renamed in commit b00c2fd.

Output files use neutral client.* names. README, .env.example,
docker-compose.yml, deploy.sh, and config.py updated to match.

Provenance receipts in local-tools/cert_provenance.md.
2026-06-30 19:27:24 +01:00
Jack Nagy b00c2fd90b Refactor to polling-first architecture with OBSERVE as accelerator
State freshness now comes from a tiered PollScheduler over the persistent
DTLS session; OBSERVE registrations are kept as an opportunistic
acceleration layer. Behaviour is identical online vs air-gapped except
for worst-case freshness latency.

Adds three modules:
- StateCache: single source of truth, source-tagged change events
- PollScheduler: hot/warm/cold + sweep tiers, write-defer past the
  fetchback-revert window, per-window RTT/slow-poll tracking
- KeepaliveTask: CoAP empty-CON ping with consecutive-fail detection
  driving MQTT availability

Bridge publishes per-appliance diagnostic entities (Push Active, Last
Update Source, Poll Max RTT, Slow Polls, Poll Errors, Stalest Resource
Age, Last OBSERVE Age) under HA's Diagnostic section. Tier cadences
are descriptor-declared, calibrated against measured per-firmware
ceilings (dryer ~14 req/s, oven ~8 req/s via probe_poll_rate_combined.py).

Drops HEARTBEAT_INTERVAL_S in favour of the descriptor-declared sweep
tier; PING_INTERVAL_S now consumed by KeepaliveTask inside the bridge
rather than driven from main.py.

README explains the push/poll split and what happens when the appliance
is blocked from internet.
2026-06-03 18:38:04 +01:00
Quite Yellow f577a32c50 Update README.md 2026-05-31 15:51:43 +01:00
Jack Nagy c99ef324fc Initial commit 2026-05-31 15:50:15 +01:00