Class 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.
-
Method Summary
Modifier and TypeMethodDescriptionbooleanIndicates whether some other object is "equal to" this one.booleanThe boolean.doubleA colour temperature in Kelvin.doubleThe quantity, converted into the unit you name.The name of the enum constant this value was built from.intThe ordinal of an enum value, for the codec and forLockState.of(TraitValue)and its siblings.intgetInt()The whole number.getKind()What sort of value this is, and therefore which getter works.doubleThe quantity in whatever unit it happens to be in, with no conversion and no check.intThe backend's own numeric value, where the canonical mapping was lossy.The text.getUnit()The unit aTraitValueKind.DOUBLEvalue is expressed in.inthashCode()Returns a hash code value for the object.booleanWhether the backend's own numeric value is available alongside the canonical one.static TraitValueof(boolean value) A boolean value -- a switch, a motion flag, a mute.static TraitValueA measured quantity.static TraitValueof(int value) A unitless whole number.static TraitValueFree text.static TraitValueOne of a fixed set, from one of this package's domain enums.static TraitValueofEnumOrdinal(int ordinal) Builds an enum value straight from an ordinal, for the codec.toString()Returns a string representation of the object.withRawPlatformValue(int raw) This value with the platform's own ordinal attached.
-
Method Details
-
of
A boolean value -- a switch, a motion flag, a mute.
Parameters
value: the boolean
Returns
a value of kind
TraitValueKind.BOOLEAN -
of
-
of
A measured quantity.
Parameters
-
value: the quantity -
unit: the unit it is expressed in
Returns
a value of kind
TraitValueKind.DOUBLEThrows
-
IllegalArgumentException: whenunitisnull. 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: whenvalueis 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
Free text.
Parameters
value: the text;nullbecomes the empty string
Returns
a value of kind
TraitValueKind.STRING -
ofEnum
One of a fixed set, from one of this package's domain enums.
Parameters
value: the constant
Returns
a value of kind
TraitValueKind.ENUMThrows
IllegalArgumentException: whenvalueisnull
-
ofEnumOrdinal
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
ordinal: the ordinal of a constant in this package's domain enum for the trait in question
Returns
a value of kind
TraitValueKind.ENUM -
withRawPlatformValue
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
raw: the backend's own numeric value
Returns
a copy carrying the raw value; this instance is unchanged
-
getKind
What sort of value this is, and therefore which getter works.
Returns
the kind, never
null -
getUnit
The unit a
TraitValueKind.DOUBLEvalue is expressed in.TraitUnit.NONEfor every other kind.Returns
the unit, never
null -
getBoolean
public boolean getBoolean()The boolean.
Returns
the value
Throws
IllegalStateException: when this is not aTraitValueKind.BOOLEAN
-
getInt
public int getInt()The whole number.
Returns
the value
Throws
IllegalStateException: when this is not anTraitValueKind.INT
-
getString
The text.
Returns
the value, never
nullThrows
IllegalStateException: when this is not aTraitValueKind.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
the ordinal
Throws
IllegalStateException: when this is not anTraitValueKind.ENUM
-
getEnumName
The name of the enum constant this value was built from.
nullonly for a value built straight from an ordinal withofEnumOrdinal(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 letsTrait.acceptsEnumValue(TraitValue)tell one domain enum from another -- ParparVM'sEnum.getDeclaringClass()returnsnull, so the type itself is not available to check.Returns
the constant's name, or
nullThrows
IllegalStateException: when this is not anTraitValueKind.ENUM
-
getDouble
The quantity, converted into the unit you name.
Parameters
in: the unit you want the answer in
Returns
the quantity expressed in
inThrows
-
IllegalStateException: when this is not aTraitValueKind.DOUBLE -
IllegalArgumentException: wheninmeasures 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 readgetUnit()and are doing something with both.Returns
the raw numeric component
Throws
IllegalStateException: when this is not aTraitValueKind.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 aTraitUnitpair; seeTraitUnit.miredToKelvin(double).Returns
the colour temperature in Kelvin
Throws
-
IllegalStateException: when this is not aTraitValueKind.DOUBLE -
IllegalArgumentException: when this value is not a colour temperature, or is not positive
-
-
hasRawPlatformValue
public boolean hasRawPlatformValue()Whether the backend's own numeric value is available alongside the canonical one.
Returns
truewhengetRawPlatformValue()is meaningful -
getRawPlatformValue
public int getRawPlatformValue()The backend's own numeric value, where the canonical mapping was lossy.
Meaningful only when
hasRawPlatformValue()answerstrue, and meaningful only in terms of the backend that produced it -- readSmartHome.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
the platform's own ordinal, or zero
-
equals
Description copied from class:ObjectIndicates whether some other object is "equal to" this one. The equals method implements an equivalence relation: It is reflexive: for any reference value x, x.equals(x) should return true. It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true. It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true. It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified. For any non-null reference value x, x.equals(null) should return false. The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any reference values x and y, this method returns true if and only if x and y refer to the same object (x==y has the value true). -
hashCode
public int hashCode()Description copied from class:ObjectReturns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable. The general contract of hashCode is: Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application. If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result. It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables. As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.) -
toString
Description copied from class:ObjectReturns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method. The toString method for class Object returns a string consisting of the name of the class of which the object is an instance, the at-sign character `@', and the unsigned hexadecimal representation of the hash code of the object. In other words, this method returns a string equal to the value of: getClass().getName() + '@' + Integer.toHexString(hashCode())
-