Class TraitValue

java.lang.Object
com.codename1.home.TraitValue

public final class TraitValue extends Object

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 Details

    • of

      public static TraitValue of(boolean value)

      A boolean value -- a switch, a motion flag, a mute.

      Parameters
      • value: the boolean
      Returns

      a value of kind TraitValueKind.BOOLEAN

    • of

      public static TraitValue of(int value)

      A unitless whole number.

      Parameters
      • value: the number
      Returns

      a value of kind TraitValueKind.INT

    • of

      public static TraitValue of(double value, TraitUnit unit)

      A measured quantity.

      Parameters
      • value: the quantity

      • unit: the unit it is expressed in

      Returns

      a value of kind TraitValueKind.DOUBLE

      Throws
      • IllegalArgumentException: when unit is null. 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 value is 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)

      Free text.

      Parameters
      • value: the text; null becomes the empty string
      Returns

      a value of kind TraitValueKind.STRING

    • ofEnum

      public static TraitValue ofEnum(Enum<?> value)

      One of a fixed set, from one of this package's domain enums.

      Parameters
      • value: the constant
      Returns

      a value of kind TraitValueKind.ENUM

      Throws
      • IllegalArgumentException: when value is null
    • 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
      • 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

      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
      • raw: the backend's own numeric value
      Returns

      a copy carrying the raw value; this instance is unchanged

    • getKind

      public TraitValueKind getKind()

      What sort of value this is, and therefore which getter works.

      Returns

      the kind, never null

    • getUnit

      public TraitUnit getUnit()

      The unit a TraitValueKind.DOUBLE value is expressed in. TraitUnit.NONE for every other kind.

      Returns

      the unit, never null

    • getBoolean

      public boolean getBoolean()

      The boolean.

      Returns

      the value

      Throws
    • getInt

      public int getInt()

      The whole number.

      Returns

      the value

      Throws
    • getString

      public String getString()

      The text.

      Returns

      the value, never null

      Throws
    • 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
    • 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

      the constant's name, or null

      Throws
    • getDouble

      public double getDouble(TraitUnit in)

      The quantity, converted into the unit you name.

      Parameters
      • in: the unit you want the answer in
      Returns

      the quantity expressed in in

      Throws
      • IllegalStateException: when this is not a TraitValueKind.DOUBLE

      • IllegalArgumentException: when in measures 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

      the raw numeric component

      Throws
    • 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

      the colour temperature in Kelvin

      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()

      Whether the backend's own numeric value is available alongside the canonical one.

      Returns

      true when getRawPlatformValue() is meaningful

    • getRawPlatformValue

      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

      the platform's own ordinal, or zero

    • equals

      public boolean equals(Object o)
      Description copied from class: Object
      Indicates 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).
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Description copied from class: Object
      Returns 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.)
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      Description copied from class: Object
      Returns 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())
      Overrides:
      toString in class Object