public final class TraitValue
- Object
- TraitValue
One value of one Trait: immutable, typed, and carrying its unit.
TraitValue on = TraitValue.of(true);
TraitValue dim = TraitValue.of(40, TraitUnit.PERCENT);
TraitValue warm = TraitValue.of(370, TraitUnit.MIRED);
A tagged union, not forty subclasses
Home values are genuinely heterogeneous – a switch is a boolean, a dimmer is a percentage, a lock is one of a fixed set – and both native layers hand them over as raw numbers. Something has to give them types back.
A class per trait would mean a cast at every read site, and this codebase
has a hard rule against a cast whose failure you expect to handle: ParparVM
does not check CHECKCAST, so on iOS a wrong cast does not throw, it hands
the wrong object to the next instruction and reads the target type’s fields
out of it. One class with kind-checked getters turns the same mistake into
an IllegalStateException naming both kinds, on every platform.
There is no zero-argument getDouble
getDouble(TraitUnit) makes you name the unit you expect and converts,
or throws if the dimensions disagree. That is inherited straight from
com.codename1.health.HealthQuantity, and the reason is the same: a bare
getDouble() is how a Celsius setpoint gets rendered as Fahrenheit and how
a colour temperature in mireds gets treated as Kelvin. Neither mistake
raises anything at the time.
getRawDouble() is the escape hatch, named to be awkward enough that it
is not reached for by habit.
The raw platform value
Some mappings in this API are judgment calls – HomeKit has six air-quality
levels and Matter has seven, and Matter has five thermostat modes HomeKit
cannot express. Where the canonical answer is lossy,
getRawPlatformValue() carries the platform’s own ordinal alongside it.
Without it every lossy mapping decision would be a permanent lie; with it,
an app that must be exact can be.
Methods
Inherited methods
Method details
of
public static TraitValue of(boolean value)Parameters
valueboolean- the boolean
Returns
TraitValueKind.BOOLEANof
public static TraitValue of(int value)Parameters
valueint- the number
Returns
TraitValueKind.INTof
public static TraitValue of(double value, TraitUnit unit)Parameters
valuedouble- the quantity
unitTraitUnit- the unit it is expressed in
Returns
TraitValueKind.DOUBLEThrows
IllegalArgumentException- when
unitisnull. A quantity with no unit is the bug this class exists to prevent, so it is refused at construction rather than defaulted to something plausible. IllegalArgumentException- when
valueis NaN or an infinity. Nothing downstream can carry one: the wire encodes a number as text and its decoder refuses these as INVALID_DATA, so a write that got this far was accepted, stored by the local backend, and then read back as a failure – and on a device it would be a number no accessory could act on.
of
public static TraitValue of(String value)Parameters
valueString- the text;
nullbecomes the empty string
Returns
TraitValueKind.STRINGofEnum
public static TraitValue ofEnum(Enum<?> value)Parameters
valueEnum<?>- the constant
Returns
TraitValueKind.ENUMThrows
IllegalArgumentException- when
valueisnull
ofEnumOrdinal
public static TraitValue ofEnumOrdinal(int ordinal)Builds an enum value straight from an ordinal, for the codec.
Application code should use ofEnum(java.lang.Enum); this exists
because the wire carries an ordinal and the decoder has no constant to
hand.
Parameters
ordinalint- the ordinal of a constant in this package’s domain enum for the trait in question
Returns
TraitValueKind.ENUMwithRawPlatformValue
public TraitValue withRawPlatformValue(int raw)This value with the platform’s own ordinal attached.
Used by the ports where the canonical mapping is lossy, so an app can
see what the accessory actually said. See getRawPlatformValue().
Parameters
rawint- the backend’s own numeric value
Returns
getKind
public TraitValueKind getKind()Returns
nullgetUnit
public TraitUnit getUnit()TraitValueKind.DOUBLE value is expressed in.
TraitUnit.NONE for every other kind.Returns
nullgetBoolean
public boolean getBoolean()Returns
Throws
IllegalStateException- when this is not a
TraitValueKind.BOOLEAN
getInt
public int getInt()Returns
Throws
IllegalStateException- when this is not an
TraitValueKind.INT
getString
public String getString()Returns
nullThrows
IllegalStateException- when this is not a
TraitValueKind.STRING
getEnumOrdinal
public int getEnumOrdinal()The ordinal of an enum value, for the codec and for
LockState.of(TraitValue) and its siblings.
Application code should go through those lookups rather than reading the ordinal: they are total, they name the constant, and they document which ones a given backend can never produce.
Returns
Throws
IllegalStateException- when this is not an
TraitValueKind.ENUM
getEnumName
public String getEnumName()The name of the enum constant this value was built from.
null only for a value built straight from an ordinal with
ofEnumOrdinal(int) and never resolved against a trait. Present for
every value an application builds and for every reading the codec
decodes – the trait names the ordinal there – which
is what lets Trait.acceptsEnumValue(TraitValue) tell one domain enum
from another – ParparVM’s Enum.getDeclaringClass() returns null,
so the type itself is not available to check.
Returns
nullThrows
IllegalStateException- when this is not an
TraitValueKind.ENUM
getDouble
public double getDouble(TraitUnit in)Parameters
inTraitUnit- the unit you want the answer in
Returns
inThrows
IllegalStateException- when this is not a
TraitValueKind.DOUBLE IllegalArgumentException- when
inmeasures a different dimension than this value
getRawDouble
public double getRawDouble()The quantity in whatever unit it happens to be in, with no conversion and no check.
Deliberately awkward. Reach for getDouble(TraitUnit) unless you
have already read getUnit() and are doing something with both.
Returns
Throws
IllegalStateException- when this is not a
TraitValueKind.DOUBLE
getColorTemperatureKelvin
public double getColorTemperatureKelvin()A colour temperature in Kelvin.
Separate from getDouble(TraitUnit) because mireds and Kelvin are
reciprocal rather than affine and so cannot be a TraitUnit pair; see
TraitUnit.miredToKelvin(double).
Returns
Throws
IllegalStateException- when this is not a
TraitValueKind.DOUBLE IllegalArgumentException- when this value is not a colour temperature, or is not positive
hasRawPlatformValue
public boolean hasRawPlatformValue()Returns
true when getRawPlatformValue() is meaningfulgetRawPlatformValue
public int getRawPlatformValue()The backend’s own numeric value, where the canonical mapping was lossy.
Meaningful only when hasRawPlatformValue() answers true, and
meaningful only in terms of the backend that produced it – read
SmartHome.getBackend() before interpreting it. Zero otherwise.
Deliberately outside equals(java.lang.Object): it is metadata about
where a value came from, not part of the value. Two readings that are
equal can carry different raw ordinals, which is exactly the lossiness
this exists to expose – compare these explicitly when that matters.
Returns
equals
public boolean equals(Object o)hashCode
public int hashCode()toString
public String toString()