Skip to content

Smart home: HomeKit, Matter and Google Home under com.codename1.home - #5554

Open
shai-almog wants to merge 93 commits into
masterfrom
smart-home
Open

Smart home: HomeKit, Matter and Google Home under com.codename1.home#5554
shai-almog wants to merge 93 commits into
masterfrom
smart-home

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Adds a first-class smart-home API. An app can list the accessories in a user's
home, read and write what they do, watch them for change, run scenes, and add
new Matter accessories -- with one set of code across HomeKit, Matter and
Google Home.

Paired with BuildDaemon #TBD, which carries the cloud-builder half of the
injection.

What's here

com.codename1.home, shaped like com.codename1.health: one entry point that
never returns null, an inert fallback rather than a null check at every call
site, and a flat primitives-and-strings SPI so an Objective-C port never
constructs a Java object.

  • Portable API + SPI -- the model, the canonical trait vocabulary, the
    wire format.
  • iOS -- HomeKit natives plus a Swift MatterSupport shim.
  • Android -- the delegate seam and an injected Play services Matter
    commissioning bridge.
  • Simulator, desktop, JavaScript -- a local simulated house.
  • Build-time injection -- both builders, so an app pays only for what it
    uses.
  • Docs -- a developer-guide chapter with five compiled snippets.

Decisions worth reviewing

The trait vocabulary is canonical, not pass-through. Trait.BRIGHTNESS is
one constant whether the accessory is behind HomeKit or Matter. The mapping is
where the bugs live, so it is documented on each constant rather than buried:
covering position runs the opposite way on the two backends, Matter's battery
percentage is in halves, Matter has no single thermostat setpoint, and
OUTLET_IN_USE has no Matter equivalent at all. Where a mapping is lossy the
canonical answer keeps an escape hatch to the platform's own ordinal.

The HomeKit entitlement is gated on touching accessories, not on using the
package.
It has to be granted on the App ID, so an app that merely rendered
"smart home is unavailable" would fail codesigning for a capability it never
wanted. An availability-only app links HomeKit and stops there.

Commissioning is its own package because on iOS it costs an entire
generated app-extension target. The scanner matches on a package prefix and
cannot express an exclusion, so the package boundary has to be the permission
boundary.

Android reports COMMISSIONING_ONLY by default, and that is the feature.
Play services can add a Matter accessory with no setup at all; reading or
controlling one needs the Google Home APIs plus a Google Cloud project and a
Home Developer Console registration only the developer can create. Reporting
AVAILABLE would make the constant mean something different on Android than on
iOS.

No Android permissions. Play services runs the whole add-device interaction
in its own activity and the AAR declares none -- checked, not assumed. Adding
Bluetooth "to be safe" would prompt the users of an app that never scans.

Verification

  • 93 new core unit tests; the full 4,920-test suite is green.
  • 27 new builder tests; the full 618-test plugin suite is green.
  • The whole HomeKit bridge type-checks against the iOS 26.2 SDK in four
    configurations with zero warnings under -Wall. That caught three
    HMErrorCode constants that do not exist under the names used and a
    vestigial local for a type HomeKit does not have.
  • The Swift shim type-checks against the same SDK.
  • The injected Android bridge compiles against the real
    play-services-home AAR from Google's Maven, the real android.jar and the
    port.
  • SpotBugs zero-findings gate green on core, android and ios; PMD, Checkstyle,
    package-info, since-tags, copyright and cast-semantics all pass.
  • Vale and the paragraph-capitalization gate pass on the new chapter.

Not claimed, and said so in the API

Automations and triggers; background accessory events; topology writes;
cameras; alarm panels; Matter events (which is why LockState.JAMMED is
unreachable outside HomeKit); and the Google Home APIs accessory graph on
Android -- its artifacts are not publicly resolvable from Google's Maven, so
writing that bridge would have been guesswork. Each has a query that answers
honestly rather than a silence.

🤖 Generated with Claude Code

shai-almog and others added 9 commits August 16, 2026 22:17
The framework has no smart-home API, so an app that wants to talk to a light,
a lock or a thermostat has to drop into native code per platform. This is the
portable half of fixing that: the model, the facade, the bridge interface each
port will implement, and the wire format between them. No native code yet, and
nothing in the builders, so this changes no behaviour on any platform -- the
base CodenameOneImplementation returns no bridge and SmartHome answers
NOT_SUPPORTED everywhere.

The shape follows com.codename1.health: one entry point that never returns
null, an inert fallback rather than a null check at every call site, and a
flat primitives-and-strings SPI so an Objective-C port never constructs a Java
object.

Three decisions worth defending.

The capability vocabulary is canonical rather than pass-through. Trait.
BRIGHTNESS is one constant whether the accessory is behind HomeKit or Matter,
and the port maps it. The alternative -- exposing HMCharacteristicType strings
and Matter cluster ids -- would have meant every app writing platform branches
inside a cross-platform API. The cost is that each mapping had to be decided,
and the interesting ones are documented on the constant rather than buried:
covering position runs the opposite way on the two backends, Matter's battery
percentage is in halves, Matter has no single thermostat setpoint, and
OUTLET_IN_USE has no Matter equivalent at all. Where a mapping is lossy --
air quality has six levels on one side and seven on the other -- the canonical
answer keeps an escape hatch to the platform's own ordinal, because otherwise
every judgment call becomes a permanent lie.

Values are a tagged union, not a class per trait. A class per trait means a
cast at every read site, and a failed cast does not throw under ParparVM. One
TraitValue with kind-checked getters turns the same mistake into an
IllegalStateException on every platform. It also keeps HealthQuantity's best
idea: there is no zero-argument getDouble, so a Celsius setpoint cannot be
read as Fahrenheit by accident. Mireds and Kelvin are reciprocal rather than
affine, so Kelvin is deliberately not a TraitUnit and the conversion is a
named method instead of a table entry that would be quietly wrong.

Commissioning lives in its own package. The build server decides what native
machinery an app gets by scanning bytecode for package prefixes and has no way
to express an exclusion, so the package boundary has to be the permission
boundary. Commissioning costs an entire generated Xcode target on iOS and the
Play services dependency plus Bluetooth permissions on Android; an app that
only reads its lights should get none of that.

EdtResult and OneShot moved from com.codename1.impl.health to a new
com.codename1.impl.async and are shared rather than copied. Their javadoc
records three concurrency bugs a copy would have silently re-acquired.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Almost all of a smart-home feature is code that has nothing to do with
accessories -- laying out a room, wiring a switch to a write, handling a
failure, deciding what to show while a covering is moving. A desktop port that
answered NOT_SUPPORTED would make all of that testable only on a phone with
real hardware attached, which in practice means testable rarely.

So the simulator, the desktop ports and the JavaScript port get a real
implementation rather than a stub: LocalHomeBridge, an app-private home that
reads, writes, runs scenes and commissions. It reports LOCAL_ONLY, not
AVAILABLE, because nothing outside the app can see these accessories.

Two rules it follows that a mock would not, both of them about not letting an
app be written against behaviour it cannot rely on:

It never completes inline, and adds a few milliseconds of latency on purpose.
Code written against a store that answers synchronously races the moment it
meets one that does not, and that asymmetry has already shipped once here --
it is why EdtResult exists.

It does not push changes. isPushDelivery() answers false and changes wait for
drainChanges(), matching every backend except HomeKit in the foreground. A
simulator that pushed would leave the polling path -- the one that actually
runs on Android -- exercised nowhere.

The synthetic house is deliberately awkward rather than tidy. It has a
two-gang switch (one accessory, two services, both exposing ON_OFF, so
"write to the accessory" is ambiguous and finds out here rather than in
someone's hallway), a bridged pair of lights behind a hub, an unreachable
socket that fails every operation, a thermostat in auto mode where
TARGET_TEMPERATURE genuinely has no value, a sensor that has never reported,
and a dimmer with a real 10 percent floor so a slider built from the trait's
nominal range offers values the accessory refuses. Every availability state is
scriptable, which is the only way most people will ever reach their app's
COMMISSIONING_ONLY branch.

93 tests over the value layer, the wire codec, the Matter setup-payload
parser (checked against the specification's own worked example) and the whole
stack against the simulated home. The degradation suite is the one that earns
its keep: it pins that a port with no backend answers empty rather than null
and fails fast rather than hanging, which is the contract that lets
application code drop the platform branch.

One behaviour change fell out of writing them: reading or writing an empty
batch now succeeds with nothing before the bridge is consulted rather than
after. A request that asks for nothing has no platform component, and a
caller whose filter emptied a list should not get a different answer on the
desktop than on a phone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each returns the local simulated house rather than nothing, so a smart-home
screen can be built and exercised on a laptop. JavaSEPort also exposes it
statically, which is what the simulator's Smart Home panel will drive to
script an accessory changing, an accessory going offline, or the availability
reporting something other than LOCAL_ONLY -- the last of which is the only
practical way to reach an app's COMMISSIONING_ONLY branch.

The getters are class-guarded for the reason the health ones are: the bridge
owns the graph, the current trait values and the undelivered-change queues, so
two of them coordinate on nothing and a write through one would be invisible
to a subscription registered against the other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Implements the SPI against HMHomeManager: the accessory graph, characteristic
reads and writes, live notifications, action sets, and the MatterSupport
add-device flow. Compiled only when the builder flips CN1_INCLUDE_HOMEKIT, and
the #else branch supplies a trampoline for every native so a home-free app
still links and carries no HomeKit symbols.

Verified by type-checking the whole file against the iOS 26.2 SDK, in both the
HomeKit-on and HomeKit-off configurations and with and without Matter setup:
zero warnings under -Wall. That caught four things that would otherwise have
failed a cloud build: three HMErrorCode constants that do not exist under the
names I used, and a vestigial HMCharacteristicProperties local for a type
HomeKit does not have.

Three decisions worth recording.

The graph is an encoded snapshot rebuilt on the main queue, not a live view.
HMHomeManager must be created on the main thread and Apple documents no thread
safety for HMHome, HMAccessory or HMCharacteristic -- but the SPI's graph
getters are synchronous and are called from the EDT, and on iOS the EDT IS the
main thread, so hopping and blocking would deadlock outright. Keeping encoded
strings behind a lock means the getters never see a HomeKit object. It also
happens to be what com.codename1.home already assumes.

primaryHome is gone. Apple deprecated it in iOS 16.1 as "no longer supported"
with no replacement, so isPrimary() now reports false for every home on iOS and
getPrimaryStructure() falls back to the first. Guessing would have been a claim
about what the user prefers.

Commissioning goes through Swift, and both directions are looked up by name.
MatterSupport has no Objective-C interface -- MatterAddDeviceRequest is a Swift
struct with an async perform() -- so the flow lives in a Swift file. Bridging it
the normal way needs a bridging header and a generated <Module>-Swift.h, both
named after the application's module, which does not exist when a file inside
the port is written. So each side finds the other with NSClassFromString and
passes one dictionary. The Swift half type-checks clean against the SDK too.

One bug fixed before it shipped: the batch read counted how many live reads to
wait for in a pass that had not yet applied the error cases, so a batch
containing an unreachable accessory counted a read it never issued and the
caller waited forever. The two passes are now split so the count is fixed by
the same logic that decides what to skip.

Known gap, documented on the method: below iOS 17.6 a setup code the app
already scanned is not passed through, because the only API those releases have
was dropped from the Swift interface when Apple deprecated it. Apple's sheet
asks the user to scan again and the accessory still commissions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…idge

The port cannot call Google's smart-home libraries: it compiles against a
fixed, old android.jar with no Play services, no AndroidX, no Kotlin and no
coroutines. It does not need to -- the port ships to app builds as source that
the app's own Gradle compiles -- so this follows the Health Connect and Android
Auto pattern exactly: a SmartHomeDelegate interface speaking only primitives
and Strings, a registry the injected implementation publishes itself through,
and a bridge that forwards and decodes nothing.

The injected half is plain Java rather than Kotlin, because the Play services
Matter commissioning API is plain Java. That drops the Kotlin plugin, the
stdlib and coroutines from what an app has to carry to add an accessory. It is
verified rather than written blind: it compiles against the real
play-services-home 16.0.0-beta1 AAR pulled from Google's Maven, against the
real android.jar, and against the port itself.

Android reports COMMISSIONING_ONLY, and that is the substance of the change
rather than a placeholder. Play services can add a Matter accessory to the
user's Google Home with no setup at all -- no account linking, no Cloud
project, no console registration. What it cannot do is let the app then SEE or
CONTROL that accessory: the graph belongs to Google Home, and reading it needs
the Google Home APIs plus a Google Cloud project and a Home Developer Console
registration carrying the developer's own signing-key SHA-1. None of that can
be shipped on their behalf.

So the graph is empty here and the availability says so, rather than reporting
AVAILABLE and rendering an empty house with no explanation.
getConfigurationProblems() spells out why, in text aimed at the developer.

What is NOT in this change, stated plainly: the Google Home APIs graph. Its
artifacts (play-services-home 17.x and play-services-home-types) are not
publicly resolvable from Google's Maven -- I checked, they 404 -- which is
consistent with the SDK being gated behind Developer Console access. Writing a
Kotlin bridge against an API surface I cannot inspect or compile would have
been guesswork dressed as a feature. Commissioning works today; the graph needs
the SDK in hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The framework half is only half the feature. This is the other half: an app
that never references com.codename1.home gets no HomeKit framework, no
entitlement, no Play services dependency, no manifest change and no extra Xcode
target -- and one that does gets exactly the pieces it asked for.

The gating runs off the existing bytecode scanner in both builders, plus a
PlatformFeatureCatalog entry pair. Three things fall out of it that are worth
recording.

The HomeKit entitlement is gated on touching accessories, NOT on using the
package. This is the trap the HealthKit block already documents at length:
com.apple.developer.homekit has to be granted on the App ID, so handing it to
an app that merely rendered "smart home is unavailable on this device" would
fail its codesigning for a capability it never wanted -- and the failure
surfaces as an opaque codesign error minutes into a cloud build. So a scan that
sees only SmartHome.getAvailability() and the capability enums links HomeKit and
stops there. The classifier lives in a shared, tested helper rather than an
inline list, and a parity test asserts the injection is guarded by the narrow
flag rather than the broad one.

Commissioning is separated because it is expensive. On iOS it costs
MatterSupport, a second entitlement, an app group, a background mode, Bonjour
service declarations and an entire generated app-extension target -- Apple
requires a MatterAddDeviceExtension before an app may add an accessory, and an
app without one gets a runtime failure with nothing at build time to warn it.
Generating and signing that is the builder's job, because the point of the
framework is that nobody opens Xcode. Since the scanner matches on a package
prefix and cannot express an exclusion, the package boundary IS the permission
boundary, which is why com.codename1.home.commissioning is its own package.

There are deliberately no Android permissions. The obvious expectation is
Bluetooth and local-network, because commissioning involves both. It does not
need them: Play services runs the entire add-device interaction in its own
activity holding its own permissions, and the play-services-home AAR declares
no permissions at all -- which I checked rather than assumed. Adding them "to
be safe" would put three Android 12 Bluetooth prompts in front of the users of
an app that never scans. What IS needed is the <queries> entry, without which
package-visibility filtering makes openEcosystemApp() silently do nothing.

An existing guard earned its keep: WatchNativeBuilderTest failed because both
new frameworks were classified for neither watchOS availability list. HomeKit
is present on watchOS and the watch slice uses it; MatterSupport is present and
the watch slice compiles the flow out, since Apple's sheet is iOS-only. Both
now say which and why.

27 new tests over the fragments, the generated extension and scanner parity;
the full 618-test plugin suite is green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follows External-Surfaces rather than a feature tour: it opens with the model,
then spends its length on the four things that will otherwise bite somebody.

Android's default answer is not "available" gets its own section, because it is
the one thing worth understanding before designing a screen. An app written
against the iOS meaning of AVAILABLE renders an empty house on Android with no
explanation.

A reading has three outcomes, not two. The example branches on all of them,
because "no value yet" is the one people skip and it renders as zero degrees.

Nothing wakes your app, so isPushDelivery() has to be asked and drainChanges()
has to be wired to the foreground -- an app that assumes push looks exactly
like a sensor that never fires.

Commissioning success does not always mean you got a device.

The build-hint section says what is automatic in both directions: an app that
never references the package carries none of it, and the HomeKit entitlement
arrives only for an app that touches accessories, which is why an
availability-only app does not need the App ID capability.

Five snippets, all compiled as part of docs/demos rather than pasted, so an API
change breaks them. Vale is clean and the paragraph-capitalization gate passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nine anonymous SmartHomeDelegate.Callback classes became named static ones.
Anonymous callbacks capture the enclosing bridge and pin it for as long as the
platform holds them, which is what SIC_INNER_SHOULD_BE_STATIC_ANON reports --
and the names read better in a stack trace than AndroidHomeBridge$7.

AndroidSmartHomeSupport gets a spotbugs-exclude entry alongside the two
identical registries already there, AndroidCarSupport and AndroidHealthSupport.
Storing the injected delegate in a static field is the whole point: the port
compiles against an android.jar with no Play services on it, so it cannot hold
the bridge any other way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The edit that added getHomeBridge() went through a script that rewrote the
whole file, and AndroidImplementation.java is CRLF with 26 mixed lone-LF lines.
Normalizing it turned a 22-line addition into a 14,260-line diff that would
have buried the change in review and conflicted with every other branch
touching the file.

Restored from origin/master and re-applied byte-wise. The diff is now the 22
lines it should always have been.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 525 total, 0 failed, 54 skipped

Benchmark Results

  • Execution Time: 20558 ms

  • Hotspots (Top 20 sampled methods):

    • 18.86% com.codename1.tools.translator.Parser.addToConstantPool (351 samples)
    • 7.47% java.util.ArrayList.indexOf (139 samples)
    • 4.03% java.lang.StringBuilder.append (75 samples)
    • 3.60% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (67 samples)
    • 3.49% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (65 samples)
    • 2.90% com.codename1.tools.translator.ByteCodeClass.fillVirtualMethodTable (54 samples)
    • 2.31% org.objectweb.asm.tree.analysis.Analyzer.analyze (43 samples)
    • 2.15% java.lang.System.identityHashCode (40 samples)
    • 1.77% com.codename1.tools.translator.Parser.classIndex (33 samples)
    • 1.77% java.util.HashMap.hash (33 samples)
    • 1.67% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (31 samples)
    • 1.40% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (26 samples)
    • 1.34% java.lang.String.equals (25 samples)
    • 1.29% org.objectweb.asm.ClassReader.readCode (24 samples)
    • 1.24% java.lang.Object.hashCode (23 samples)
    • 1.24% com.codename1.tools.translator.BytecodeMethod.optimize (23 samples)
    • 1.07% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (20 samples)
    • 1.07% com.codename1.tools.translator.bytecodes.Invoke.addDependencies (20 samples)
    • 1.02% java.lang.StringCoding.encode (19 samples)
    • 0.97% com.codename1.tools.translator.BytecodeMethod.equals (18 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog
shai-almog marked this pull request as ready for review August 17, 2026 01:51
@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 8.09% (7868/97198 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.07% (41636/515701), branch 2.88% (1402/48723), complexity 3.18% (1663/52272), method 4.90% (1355/27642), class 9.97% (367/3680)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 8.09% (7868/97198 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.07% (41636/515701), branch 2.88% (1402/48723), complexity 3.18% (1663/52272), method 4.90% (1355/27642), class 9.97% (367/3680)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 233ms / native 133ms = 1.7x speedup
SIMD float-mul (64K x300) java 154ms / native 123ms = 1.2x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 76.000 ms
Base64 CN1 decode 85.000 ms
Base64 native encode 333.000 ms
Base64 encode ratio (CN1/native) 0.228x (77.2% faster)
Base64 native decode 284.000 ms
Base64 decode ratio (CN1/native) 0.299x (70.1% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 63ms / native 4ms = 15.7x speedup
SIMD float-mul (64K x300) java 63ms / native 5ms = 12.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 205.000 ms
Base64 CN1 decode 136.000 ms
Base64 SIMD encode 99.000 ms
Base64 encode ratio (SIMD/CN1) 0.483x (51.7% faster)
Base64 SIMD decode 100.000 ms
Base64 decode ratio (SIMD/CN1) 0.735x (26.5% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 27.000 ms
Image createMask (SIMD on) 21.000 ms
Image createMask ratio (SIMD on/off) 0.778x (22.2% faster)
Image applyMask (SIMD off) 64.000 ms
Image applyMask (SIMD on) 55.000 ms
Image applyMask ratio (SIMD on/off) 0.859x (14.1% faster)
Image modifyAlpha (SIMD off) 60.000 ms
Image modifyAlpha (SIMD on) 238.000 ms
Image modifyAlpha ratio (SIMD on/off) 3.967x (296.7% slower)
Image modifyAlpha removeColor (SIMD off) 75.000 ms
Image modifyAlpha removeColor (SIMD on) 63.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.840x (16.0% faster)

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3cc23a11b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m Outdated
Comment thread CodenameOne/src/com/codename1/impl/home/SubscriptionState.java
Comment thread CodenameOne/src/com/codename1/home/SmartHome.java Outdated
Comment thread CodenameOne/src/com/codename1/home/commissioning/Commissioner.java Outdated
@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 78ms / native 4ms = 19.5x speedup
SIMD float-mul (64K x300) java 82ms / native 5ms = 16.4x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 210.000 ms
Base64 CN1 decode 141.000 ms
Base64 SIMD encode 116.000 ms
Base64 encode ratio (SIMD/CN1) 0.552x (44.8% faster)
Base64 SIMD decode 90.000 ms
Base64 decode ratio (SIMD/CN1) 0.638x (36.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 131.000 ms
Image createMask (SIMD on) 20.000 ms
Image createMask ratio (SIMD on/off) 0.153x (84.7% faster)
Image applyMask (SIMD off) 64.000 ms
Image applyMask (SIMD on) 36.000 ms
Image applyMask ratio (SIMD on/off) 0.563x (43.8% faster)
Image modifyAlpha (SIMD off) 35.000 ms
Image modifyAlpha (SIMD on) 36.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.029x (2.9% slower)
Image modifyAlpha removeColor (SIMD off) 62.000 ms
Image modifyAlpha removeColor (SIMD on) 40.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.645x (35.5% faster)

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 57ms / native 3ms = 19.0x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 247.000 ms
Base64 CN1 decode 128.000 ms
Base64 SIMD encode 65.000 ms
Base64 encode ratio (SIMD/CN1) 0.263x (73.7% faster)
Base64 SIMD decode 63.000 ms
Base64 decode ratio (SIMD/CN1) 0.492x (50.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 8.000 ms
Image createMask ratio (SIMD on/off) 0.667x (33.3% faster)
Image applyMask (SIMD off) 25.000 ms
Image applyMask (SIMD on) 19.000 ms
Image applyMask ratio (SIMD on/off) 0.760x (24.0% faster)
Image modifyAlpha (SIMD off) 113.000 ms
Image modifyAlpha (SIMD on) 12.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.106x (89.4% faster)
Image modifyAlpha removeColor (SIMD off) 21.000 ms
Image modifyAlpha removeColor (SIMD on) 13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

PMD and Checkstyle fixes across the new packages, plus four defects the
review found:

- HomeKit authorization answered from the delegate. Creating HMHomeManager
  is what prompts, and the prompt is asynchronous, so reading
  authorizationStatus straight afterwards reported NOT_DETERMINED with the
  sheet still on screen. requestAuthorization() now waits for
  didUpdateAuthorizationStatus:/homeManagerDidUpdateHomes: and answers with
  the user's actual choice; a second request while one is open is answered
  UNKNOWN rather than left hanging.

- Per-write credentials. writeTraits carried one authorization string for
  the whole batch, filled by whichever write had one last, so a batch with
  two locks sent the second lock's PIN to the first. It is now a
  positionally aligned array like every other field.

- A stopped subscription delivers nothing. The initial-values branch
  dispatched without checking disposed, and the EDT hop did not re-check it
  when it ran, so a torn-down form could still be handed a batch.

- Commissioner's unsupported path returned a plain already-failed
  AsyncResource, which calls back inline on whichever thread attached the
  listener. It is an EdtResult now, like the rest of the API.

The catalog no longer names iOS frameworks for smart home or for the broad
health entry. Entries match on a prefix and their frameworks are linked for
real, so the health entry linked HealthKit into sensor-only apps -- which
Apple rejects without health purpose strings -- and the home entries were a
second copy of a decision IPhoneBuilder makes under gates the table cannot
express, including the ios.home.commissioning opt-out.

The MatterSupport Swift shim moves out of the port's nativeSources into the
builder resources, injected only for a commissioning build. Everything in
nativeSources is unpacked into every iOS build, and an unused .swift does
not compile away the way an #ifdef'd .m does -- its presence alone flips the
project into Swift mode, and it reached the watch and tv slices where
MatterAddDeviceRequest does not exist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c98e011b23

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m
Comment thread CodenameOne/src/com/codename1/home/commissioning/SetupPayload.java
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

The rendered guide turned the `Type`+s+ plural idiom into a two-letter
token LanguageTool could not spell, four times over; the sentence names the
types in parentheses instead. Plus a noun-form 'a write', two ranges
written with the .. operator, and one British spelling. 'mired' and
'setpoint' are the terms the platforms and the HVAC industry use, so they
join the accept list rather than the prose being reworded around them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f781f094e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/home/SmartHome.java Outdated
Comment thread CodenameOne/src/com/codename1/home/commissioning/SetupPayload.java
Microsoft.Contractions is an error-severity rule in this guide, and
'are *not* supported' matched it. The emphasis moves onto the whole
clause rather than being dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5631f0c828

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m
iOS builds:

- needsXcodeProjectMutation now includes matterExtensionEnabled. Without
  it, a commissioning app with no pods and no other extension skipped the
  whole xcodeproj mutation block, so the CN1MatterSetup target was never
  generated or embedded -- the ordinary commissioning-only build.

- The Swift shim is staged from appendMatterExtensionTarget, while the
  schemes ruby is still being assembled. Copied after that script has run
  it was on disk and in no target, so NSClassFromString found nothing and
  every commissioning call failed as NOT_CONFIGURED.

Android commissioning:

- restoreIntentResultListener() on every path out of the flow. The activity
  latches waitingForResult when a listener registers and ignores every later
  registration until that clears, so skipping it did not merely leak this
  listener -- it broke the camera, the scanner and the file picker for the
  rest of the process.

Framework:

- A written value is converted to the trait's own unit once, here, rather
  than in each bridge. TraitValue.of(68, FAHRENHEIT) for a Celsius trait
  reached HomeKit's write path, which reads the number and ignores the unit,
  as 68 degrees Celsius. Scene actions get the same treatment, being writes
  that happen later.

- CommissioningRequest.setTimeoutMillis is honoured. Neither platform flow
  takes a timeout, so a caller who asked for one waited forever on a sheet
  that could not come back.

- refresh() during startup is deferred and answered with the start's own
  result. Setting started before start() had answered sent a second caller
  to the bridge's refresh(), which on iOS rebuilds a snapshot of an
  HMHomeManager that has not loaded and resolves with no homes.

- SetupPayload validates each base-38 group against the bytes it stands for
  and each manual-code chunk against its bit width. A five-character group
  holds more than three bytes and a five-digit chunk more than 16 bits, so
  malformed codes aliased valid ones -- accepted here and rejected by the
  platform, which is the failure this parser exists to prevent.

iOS scenes are all-or-nothing: an action that cannot be built or added now
fails the request and the half-built action set is removed. A "Good night"
that silently drops the lock is worse than one that failed, because the user
is told it exists and runs it every night. The retained arguments are also
released on the two early exits that leaked them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Running, creating or deleting a scene went straight to the bridge, so a scene
an app kept -- in its own storage, or across a stop() -- named a home the
native cannot resolve until the HomeKit database has loaded, and every cold
launch answered INVALID_ARGUMENT for a scene that was there. All three go
through the same start path reads, writes, subscriptions and identify use.

And the vendor id is emitted as the number it parsed to. "0XFFF1" parses
happily in Java and is not a Swift integer literal at all -- Swift wants a
lowercase x -- so echoing back what was written produced an extension that
did not compile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb6d41fda5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m Outdated
A write batch binds a single thermostat setpoint to the caller's own mode
write, and it bound it to the first entry that was not AUTO. "HEAT, then
AUTO, and a target" therefore applied the target behind the HEAT write while
the AUTO write left the thermostat in AUTO, holding a target nothing can read
back -- the outcome the refusal exists to prevent. It binds to what the batch
actually asks for now, and refuses outright when the batch names two
different modes for one thermostat: HomeKit runs those writes independently,
so which one the accessory ends in is whichever landed last, and no answer
here would be honest.

On the builder side, injectedPlistString answers null both for a privacy key
ios.plistInject does not carry and for one it gives <false/>. The two need
opposite handling -- an absent key takes the generated default, a present one
suppresses it in the renderer -- so <false/> passed validation as
absent-but-defaultable and shipped a commissioning app whose Bluetooth
purpose string was the boolean false. injectedPlistValueTag reports the kind
of value the fragment gives a key, and anything that is not a nonblank string
is refused with the rest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f1c37bffdc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…uest

Two builder checks looked for a key by searching the ios.plistInject fragment
for its name. A comment that mentions NSBonjourServices -- or an unrelated
string that contains it -- was therefore a declaration: the Bonjour check read
the array of whatever key came after the comment and refused a build whose
plist listed both Matter services correctly, and the query-schemes check
skipped its own hint, so the plist shipped with no com.apple.Home entry at all
and openEcosystemApp reported Apple Home missing on a device that has it.
Both now find the real key element and read the array that belongs to it,
through one comment- and CDATA-aware reader shared with the purpose-string
validation.

On Android, a commissioning request whose limit expires is failed in the
framework, which knows nothing about the delegate's parked callback. When Play
services answered neither of its listeners -- no IntentSender, no failure --
nothing cleared it, and every later commission() was refused with BUSY for the
life of the process with no screen in front of the user. An expired request
that never launched is reclaimed now. A launched one is not: the sheet is the
user's to finish, and its answer arrives through the activity result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4530b33f64

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/home/TraitUnit.java Outdated
…6.4 extension

miredToKelvin and kelvinToMired guarded with `<= 0`, and NaN compares false
against every operator -- so it went straight through a check whose contract
says the input must be positive, and the reciprocal of NaN is NaN. One bad
reading then spread through every calculation it touched instead of failing at
the conversion. Named on its own now, with a test for each direction.

An own-fabric commissioning build compiles its extension for iOS 16.4, because
Apple's Matter framework starts there, while the app itself keeps the 16.1
floor MatterSupport asks for -- raising the whole app would cost every user on
16.1 through 16.3 the application over a feature they never asked for. Those
three releases cannot load the extension, and the host offered commissioning
anyway: the sheet opened onto nothing. Commissioner.getStyle() answers
ECOSYSTEM_APP_HANDOFF there and the call itself is refused with a message, so
the app hands off to the Home app exactly as a build with no extension does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c779b8776

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m Outdated
HomeKit has no request-and-callback API -- creating the manager is what
prompts, and it prompts once. A second requestAuthorization() while that sheet
was on screen was answered UNKNOWN on the spot, so an app that asks at startup
and again from its first screen, which is the ordinary shape, showed one of
them a refusal moments before the user granted access, and that request was
never corrected. All of them wait now, and all of them get the decision.

The plist-fragment reader also answers with the key's OWN value. Scanning
forward for the next <string> anywhere after the key, a key given <false/>
answered with an unrelated later key's string -- so the HomeKit purpose-string
check passed on a value the renderer keeps as the boolean false, and the app
was terminated on the device for a disclosure the build had just approved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6ffcb9886

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m Outdated
The single-setpoint refusal only ran when the thermostat was ALREADY in AUTO,
so a thermostat in HEAT given "AUTO, and 21" passed both writes: the mode
landed, the target was reported as applied, and an immediate read answered
absent because a thermostat in AUTO has no single target. The local backend
refused the same batch, because it applies mode writes first and then asks
what the thermostat is. The check runs from any starting state now and asks
what the batch LEAVES it in -- and a target that waits on a mode write says so
when that write fails, whatever mode it was going to.

The Info.plist renderer also asks what the injected fragment DECLARES rather
than what its text contains. A comment mentioning NSBluetoothAlwaysUsageDescription
suppressed the generated default, so a commissioning build shipped with no
Bluetooth purpose string and iOS killed it the moment the flow touched
Bluetooth -- while the validation, which parses the fragment properly, had
just approved the default it was about to drop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6be43dd64

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/home/TraitValue.java
The wire carries an ordinal and nothing else, so a decoded enum value arrived
with no constant name while the value an app builds with ofEnum carries one --
and the two were unequal. Comparing a reading against a desired state is the
most ordinary thing anyone does with one, and it was false every time: a lock
that reported SECURED did not equal ofEnum(LockState.SECURED), and a map keyed
on values missed. The trait knows what its ordinals are called, so the codec
names the value once the ordinal is known to be in the trait's domain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 20f3280bcd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/impl/home/LocalHomeBridge.java Outdated
A scripted TARGET_TEMPERATURE on a thermostat in AUTO went out to subscribers
as a value, while a read of the same trait answers absent -- there are two
thresholds in AUTO and no single target, which is what the device says too. A
UI drove itself off the change and showed a number the very next read denied
the existence of. The value is still stored, because leaving AUTO brings it
back; the change is announced as absent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b12666cc54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/home/TraitValue.java Outdated
Every iOS LOCK_STATE and AIR_QUALITY reading carries the backend's own ordinal
alongside the canonical value, and no value an app builds ever does -- so
equals() comparing that metadata kept a reading unequal to the value it plainly
is, even once the enum name was there. It is what getRawPlatformValue() exists
to expose, deliberately: two equal values can have come from different platform
states, and an app that cares reads them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51d4f3569d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/home/commissioning/SetupPayload.java Outdated
SetupPayload uppercased the QR payload with String.toUpperCase(), which
follows the device's locale: in Turkish a lowercase i becomes a dotted capital
that is not in the base-38 alphabet, so a code that scans everywhere else was
reported as malformed in one language, on the phone the user actually owns.
The alphabet is ASCII by specification, so the normalization is now ASCII too
-- toUpperCase(Locale) is not in the profile this code targets.

The iOS builder also validates the purpose string the RENDERER will use.
ios.plistInject wins there -- a generated value is emitted only for a key the
fragment does not declare -- so an app with a perfectly good
ios.NSHomeKitUsageDescription beside a fragment carrying that key as <false/>
passed the build and shipped the false.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51ebffa288

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…ile gate

commission() and stop() arrive on the Codename One EDT while the Play services
listeners and the activity result arrive on Android's main thread, and the
state between them -- the parked callback, its generation, its deadline,
whether a screen went up -- was neither volatile nor locked. The two threads
had no ordering at all: a task finishing as a new request started could read a
stale generation and answer, or clear, the callback that had just been parked
for somebody else. The check-then-claim in commission() was a race of its own,
where two callers could both find the slot free.

One lock covers every access now, the generation check and the hand-off happen
in a single step, and no callback is invoked while it is held.

The injected bridge also gets a compile gate. It is a .javas resource copied
into a generated Android project, so nothing in this repository compiled it:
a broken edit shipped and failed in a customer's Gradle build, naming a file
they never wrote. It now compiles in the plugin's own tests against the port's
real SmartHomeDelegate and minimal stubs for the Android and Play services
types.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8bb1cab0ab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m Outdated
shai-almog and others added 2 commits August 18, 2026 19:23
…unch

CodenameOne_GLViewController.h undefines CN1_INCLUDE_MATTER_SETUP on watchOS,
tvOS, Catalyst and macOS, and its own comment says getStyle() reports NONE
there. It reported ECOSYSTEM_APP_HANDOFF, which is the answer for an iOS build
that merely left the extension out -- so an app took the advertised handoff and
offered an Apple Home fallback these slices cannot perform. The two cases are
distinguished now.

On Android the sheet is launched under the same lock that decides it may be.
Decided and then acted on, a stop() landing in between still opened the sheet:
the result channel was taken for a request that no longer existed, so whatever
started next was refused BUSY by a screen nobody was waiting on, and the
sheet's own answer was discarded when it came back. The result listener moved
into a method of its own so the body is not four levels deep.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
check-copyright-headers refused the twenty stub declarations the injected
Matter bridge is compiled against, and they should not carry that header:
each mimics the shape of somebody else's published API -- a signature and a
return of the right type -- and stamping our licence on a facsimile of
android.app.Activity is a claim we have no business making. They are .javas
now, like the bridge itself, which is the extension this repository already
uses for Java that nothing here compiles; the test copies them under their
real names into its own temporary tree. A README beside them says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2de0f5adc6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m Outdated
FanMode.OFF means "not running", and HomeKit keeps that in the power
characteristic: TargetFanState stays manual or auto while the fan stands
still. A read of the mode therefore answered ON or AUTO for a fan that was
not running -- the one state OFF is defined as was unreachable -- and a
subscription to FAN_MODE watched only TargetFanState, so switching the fan off
was a mode change no listener ever heard.

The mode is derived from the power characteristic now, and the power
characteristic is registered as the subscription's dependency, exactly as a
thermostat's mode already is for its setpoint. Both pairs go through one map:
a governing characteristic whose movement is delivered as the trait it
governs, on the push path and on the polling recovery path alike.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc55317b61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m Outdated
Creating HomeKit's manager is what prompts, so a process that has not created
one cannot tell a user who authorized months ago from one who refused from one
with no home set up -- and getAvailability() must not put a permission sheet on
screen to find out. It answered PERMISSION_REQUIRED for all of them on every
cold launch, so an app following the documented branch called
requestAuthorization(), which iOS answers once and never again: no sheet, no
graph, and the quick start in SmartHome's own javadoc returned having done
nothing. NOT_STARTED says what is true, and the example now refreshes first and
branches on what the connection found.

The iOS header's constants are checked against the enums that define them.
Nothing crosses this wire as a name except errors -- an availability, an
authorization status, a value kind, a structure-change kind and a commissioning
style all travel as ordinals a human copied into CN1SmartHome.h -- so appending
a constant silently repoints a define, and the failure is a device reporting the
wrong state, which no build and no simulator run can show. The defines carry
their Java names now so the comparison is exact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7361ae05e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1SmartHome.m
An accessory can come back with a characteristic that no longer advertises
event notifications, and the rebind took a fresh polling baseline and then
dropped the entry from the recovery set. The subscription had already been
handed to the caller as push -- isPushDelivery() is a property of that handle
and cannot be taken back -- so nobody was ever going to call drainChanges()
for it: one resyncRequired went out, and after that the listener sat on the
value it had for as long as the screen stayed open, with no pusher and no
poller. It stays on the recovery path now, which polls it and keeps retrying
the registration, so it heals if the accessory comes back advertising
notifications.

The retry asks only where notifications are advertised. A HomeKit that
answered a pointless request without an error would have ended the polling and
left the watch with nothing; and a characteristic that can neither notify nor
be read is dropped instead of keeping the recovery timer alive for the life of
the process to ask something that will never answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant