Class Trait
One capability an accessory can have: a light's brightness, a lock's state,
a sensor's reading. Instances are interned constants, so == is a valid
identity test and is used throughout the API.
A trait is canonical, not a platform identifier. BRIGHTNESS is the
same constant whether the accessory is behind HomeKit or Matter, and the
port maps it to HMCharacteristicTypeBrightness or to Level Control's
CurrentLevel. No HMCharacteristicType string and no Matter cluster id
ever reaches Java; the mapping tables live in the native bridges, where the
platform types are, and a test parses both of them to make sure neither has
drifted from this list.
Read the constant before you use it
The javadoc on each constant below is the specification, and most of them carry a caveat that matters. Three kinds recur:
Polarity. Some values run in opposite directions on the two backends.
COVERING_POSITION is the worst: HomeKit's 100 is fully open and Matter's
0 is. This API picks one convention, the port inverts, and the constant
says which way it runs. Do not assume.
Range and quantization. Everything proportional is normalized to
percent here, but Matter carries brightness as 0 to 254 and covering
position as 0 to 10000, so a round trip is not the identity. Where an
accessory declares a real range or step, it is on the
TraitConstraint, not here.
Absence. Several traits genuinely do not exist on one backend --
OUTLET_IN_USE and TARGET_HUMIDITY have no Matter equivalent at all.
Those report HomeError.TRAIT_NOT_SUPPORTED rather than a fabricated
value. The constant says so.
Why this is a class and not an enum
The same reason com.codename1.health.HealthDataType is: the table grows,
each constant carries metadata, and forId(java.lang.String) has to stay
total across versions so a port from a newer build naming a trait this one
does not have degrades to "unknown trait" rather than throwing.
There is deliberately no way to make a custom trait. A trait only means something because both ports agree on how to map it; one this API does not know is one no port can serve.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final TraitA summarized air-quality rating, as anAirQualityLevel.static final TraitWhether the battery is charging, as aChargingState.static final TraitHow much battery an accessory has left, as a percentage.static final TraitWhether the battery is low enough to want attention.static final TraitHow bright a light is, as a percentage.static final TraitWhether carbon monoxide is being detected, as anAlarmState.static final TraitMeasured carbon monoxide, in parts per million.static final TraitMeasured carbon dioxide, in parts per million.static final TraitThe colour temperature of a white light, in mireds.static final TraitWhether a contact sensor is closed.static final TraitWhich way a covering is moving, as aPositionState.static final TraitHow far open a blind or shade is.static final TraitHow far the slats of a blind are tilted, as a percentage.static final TraitWhat the thermostat is doing right now, as aHeatingCoolingMode.static final TraitThe relative humidity an accessory is measuring, as a percentage.static final TraitAmbient light, in lux.static final TraitThe temperature an accessory is measuring.static final TraitWhat a door or garage door is doing, as aDoorState.static final TraitHow a fan is running, as aFanMode.static final TraitHow fast a fan is running, as a percentage.static final TraitThe hue of a colour light, in degrees around the colour wheel.static final TraitWhether water is being detected.static final TraitWhat a door lock is doing, as aLockState.static final TraitWhether motion is being detected.static final TraitWhether a speaker is muted.static final TraitWhether something is blocking a door or covering.static final TraitWhether a space is occupied.static final TraitWhether the accessory is on.static final TraitWhether something is plugged into an outlet and drawing power.static final TraitCoarse particulate matter, in micrograms per cubic metre.static final TraitFine particulate matter, in micrograms per cubic metre.static final TraitHow saturated a colour light is, as a percentage.static final TraitWhether smoke is being detected, as anAlarmState.static final TraitThe temperature above which cooling runs.static final TraitMove a blind or shade.static final TraitTilt the slats of a blind.static final TraitOpen or close a door.static final TraitWhat the thermostat has been asked to do, as aHeatingCoolingMode.static final TraitThe temperature below which heating runs.static final TraitThe relative humidity a humidifier or dehumidifier is aiming for.static final TraitLock or unlock a door lock.static final TraitThe single setpoint that applies in the thermostat's current mode.static final TraitTotal volatile organic compounds.static final TraitSpeaker volume, as a percentage. -
Method Summary
Modifier and TypeMethodDescriptionbooleanacceptsEnumValue(TraitValue value) booleanacceptsEnumWrite(TraitValue value) Whether a value may be WRITTEN to this trait.booleanacceptsUnit(TraitUnit unit) Whether this trait can only ever be read.all()Every trait this release knows, in declaration order.enumValue(int ordinal) Whether a value belongs to this trait's domain enum.static TraitgetId()The token this trait crosses the native boundary as, and the stable name to use in build hints and persisted state.doubleThe top of this trait's documented range, ingetUnit().doubleThe bottom of this trait's documented range, ingetUnit().getUnit()The unit aTraitValueKind.DOUBLEtrait is expressed in.What sort of value this trait carries, and therefore whichTraitValuegetter reads it.booleanWhethergetNominalMinimum()andgetNominalMaximum()mean anything.booleanA trait that is writable in principle may still be read-only on a particular accessory; that is a property of theTraitConstraint, not of the trait.toString()Returns a string representation of the object.
-
Field Details
-
ON_OFF
Whether the accessory is on.
HomeKit spells this two ways --
PowerStateon a lightbulb or switch, andActiveon a fan v2, air purifier or heater-cooler -- and this one constant covers both; the port uses whichever characteristic the service actually has. Two traits here would be a HomeKit implementation detail leaking into portable code.Matter: On/Off cluster
0x0006,OnOffattribute.Note that on Matter this is genuinely independent of
BRIGHTNESS: level 0 does not reliably mean off, and turning a light off does not zero its level. Write this trait to turn something off. -
OUTLET_IN_USE
Whether something is plugged into an outlet and drawing power.
HomeKit:
OutletInUse.Absent on Matter and Google Home. Matter's Electrical Power Measurement cluster answers a different question -- how much current is flowing -- and it is optional and rare besides. A lamp that is plugged in but switched off draws nothing, so deriving this from power draw would report an empty socket. The port answers
HomeError.TRAIT_NOT_SUPPORTEDrather than guessing. -
BRIGHTNESS
How bright a light is, as a percentage.
HomeKit:
Brightness, already a percentage.Matter: Level Control
0x0008,CurrentLevel, which runs 1 to 254 with the real bounds inMinLevelandMaxLevel; written throughMoveToLevel. 254 steps do not divide into 100, so setting 33 percent and reading it back can answer 33 or something adjacent. Do not compare a read against a value you wrote. -
HUE
The hue of a colour light, in degrees around the colour wheel.
HomeKit:
Hue, already in arcdegrees.Matter: Color Control
0x0300,CurrentHue-- a single byte covering the whole circle, so about 1.4 degrees per step. Where the accessory supports the enhanced-hue feature the port readsEnhancedCurrentHueinstead and the resolution is much better.A Matter light in XY or colour-temperature mode reports a stale hue. The port checks
ColorModeand reports no value rather than passing on a number that does not describe the light you are looking at. -
SATURATION
-
COLOR_TEMPERATURE
The colour temperature of a white light, in mireds.
HomeKit:
ColorTemperature. Matter: Color Control0x0300,ColorTemperatureMireds, bounded by the accessory's ownColorTempPhysicalMinMiredsandColorTempPhysicalMaxMireds, which arrive on theTraitConstraint.Mireds because that is what both platforms use natively, so nothing is converted on the way through. And the trap that catches everyone once: a higher mired value is a warmer light. 153 is a cold blue-white and 400 is candlelight. Use
TraitValue.getColorTemperatureKelvin()if you would rather think in Kelvin. -
CURRENT_LIGHT_LEVEL
Ambient light, in lux.
HomeKit:
CurrentLightLevel, already in lux.Matter: Illuminance Measurement
0x0400,MeasuredValue, which is logarithmically encoded as10000 * log10(lux) + 1in a single 16-bit field -- so precision falls away sharply at the bright end. Its two sentinels, 0 for "too dark to measure" and 0xFFFF for "unknown", become no value rather than a reading of zero lux. -
CURRENT_TEMPERATURE
The temperature an accessory is measuring.
HomeKit:
CurrentTemperatureon a sensor, or the thermostat's own.Matter: Temperature Measurement
0x0402MeasuredValuefor a sensor, or Thermostat0x0201LocalTemperaturefor a thermostat. Both are hundredths of a degree with 0x8000 meaning null, which becomes no value. -
TARGET_TEMPERATURE
The single setpoint that applies in the thermostat's current mode.
The largest structural mismatch in this table
HomeKit has one
TargetTemperaturecharacteristic. Matter has no such thing -- it hasOccupiedHeatingSetpointandOccupiedCoolingSetpointand nothing that unifies them.So this trait is defined as the setpoint that applies right now: when
TARGET_HEATING_COOLINGisHeatingCoolingMode.HEATit is the heating setpoint, when it isHeatingCoolingMode.COOLit is the cooling one, and when the mode isHeatingCoolingMode.AUTOthis trait reports no value on either backend. In auto there are two setpoints and answering with either one would silently be the wrong one half the time; useTARGET_HEATING_TEMPERATUREandTARGET_COOLING_TEMPERATURE, which always mean exactly what they say.A thermostat UI that only ever offers one number will be wrong on an auto-mode thermostat no matter what this API does. Offer two.
-
TARGET_HEATING_TEMPERATURE
The temperature below which heating runs.
HomeKit:
HeatingThreshold. Matter: Thermostat0x0201,OccupiedHeatingSetpoint. Clean on both sides. -
TARGET_COOLING_TEMPERATURE
The temperature above which cooling runs.
HomeKit:
CoolingThreshold. Matter: Thermostat0x0201,OccupiedCoolingSetpoint. Clean on both sides. -
CURRENT_HEATING_COOLING
What the thermostat is doing right now, as a
HeatingCoolingMode.HomeKit:
CurrentHeatingCooling. Matter: Thermostat0x0201,ThermostatRunningModewhere the accessory has it, falling back toSystemMode.Never
HeatingCoolingMode.AUTO: at any instant a thermostat is heating, cooling or idle. Auto is a policy, and it lives onTARGET_HEATING_COOLING. -
TARGET_HEATING_COOLING
What the thermostat has been asked to do, as a
HeatingCoolingMode.HomeKit:
TargetHeatingCooling, four values. Matter: Thermostat0x0201,SystemMode, nine.The five Matter modes HomeKit cannot express -- emergency heat, precooling, fan-only, dry and sleep -- arrive as
HeatingCoolingMode.OTHERwith the platform's own ordinal onTraitValue.getRawPlatformValue(), rather than being flattened intoHeatingCoolingMode.OFF. WritingOTHERis refused. -
CURRENT_HUMIDITY
The relative humidity an accessory is measuring, as a percentage.
HomeKit:
CurrentRelativeHumidity. Matter: Relative Humidity Measurement0x0405,MeasuredValue, in hundredths of a percent with 0xFFFF meaning null. -
TARGET_HUMIDITY
The relative humidity a humidifier or dehumidifier is aiming for.
HomeKit:
TargetRelativeHumidity.Absent on Matter and Google Home. There is no standard target-humidity cluster in the Matter revisions the shipping ecosystems support, so the port answers
HomeError.TRAIT_NOT_SUPPORTED. -
LOCK_STATE
What a door lock is doing, as a
LockState.HomeKit:
CurrentLockMechanismState. Matter: Door Lock0x0101,LockState, which is nullable and whose null becomes no value.Note
LockState.JAMMEDis unreachable outside HomeKit andLockState.PARTIALLY_LOCKEDis unreachable on HomeKit; the enum explains both. -
TARGET_LOCK_STATE
Lock or unlock a door lock. Only
LockState.SECUREDandLockState.UNSECUREDmay be written.HomeKit:
TargetLockMechanismState, an ordinary characteristic write.Matter has no such attribute -- locking is the
LockDoorcommand and unlocking isUnlockDooron Door Lock0x0101. That difference is invisible from here by design, with one exception that is not: a Matter lock configured withRequirePINforRemoteOperationwill refuse an unlock without a credential and the write fails withHomeError.PIN_REQUIRED. Supply one throughTraitWrite.setAuthorizationData(java.lang.String). HomeKit never takes a PIN and ignores the field. -
DOOR_STATE
What a door or garage door is doing, as a
DoorState.HomeKit:
CurrentDoorState. Google Home: its own garage-door trait. Matter has no standard garage cluster, so this is unavailable on an Android build limited toHomeAvailability.COMMISSIONING_ONLY. -
TARGET_DOOR_STATE
Open or close a door. Only
DoorState.OPENandDoorState.CLOSEDmay be written.HomeKit:
TargetDoorState. Same backend availability asDOOR_STATE. -
OBSTRUCTION_DETECTED
Whether something is blocking a door or covering.
HomeKit:
ObstructionDetected, on both doors and coverings.Matter: the obstacle bit of Window Covering
0x0102SafetyStatus-- coverings only, since there is no garage cluster to carry it. -
COVERING_POSITION
How far open a blind or shade is. 100 is fully open, 0 is fully closed.
The inversion to watch
This is the likeliest source of a shipped bug in the whole package. HomeKit's
CurrentPositionruns the way this constant does -- 100 is open. Matter'sCurrentPositionLiftPercent100thsruns the other way, where 0 is fully open, and it is in hundredths of a percent besides, so the raw numbers differ by both a flip and a factor of a hundred.This API takes the HomeKit convention because it is what a slider labelled "open" wants, and the Android bridge inverts. Nothing in application code should ever see Matter's polarity -- but if you are reading a Matter trace next to a Codename One log, that is why the two disagree.
-
TARGET_COVERING_POSITION
Move a blind or shade. 100 is fully open, matching
COVERING_POSITION; read that constant's note.HomeKit:
TargetPosition. Matter:TargetPositionLiftPercent100thsto read back what was asked for, and theGoToLiftPercentagecommand to write. -
COVERING_TILT
How far the slats of a blind are tilted, as a percentage.
HomeKit models tilt as two characteristics in arcdegrees,
CurrentHorizontalTiltandCurrentVerticalTilt, running -90 to 90. Matter models it as one percentage,CurrentPositionTiltPercent100ths.This API takes Matter's single-axis percentage. On iOS the port uses whichever axis the service exposes and maps the accessory's real degree range onto 0 to 100, publishing the degrees on the
TraitConstraintso a UI can still label them. Two-axis tilt is not supported: an accessory offering both gets its horizontal axis, and there is no way to reach the other one from portable code. -
TARGET_COVERING_TILT
Tilt the slats of a blind. Same single-axis model as
COVERING_TILT; read that constant's note.HomeKit:
TargetHorizontalTiltorTargetVerticalTilt. Matter:TargetPositionTiltPercent100thsand theGoToTiltPercentagecommand. -
COVERING_MOTION
Which way a covering is moving, as a
PositionState.HomeKit:
PositionState. Matter: the global bits of Window Covering0x0102OperationalStatus.Reported in terms of opening and closing, not of the position number rising or falling, because the two backends disagree about which direction that number runs.
-
MOTION_DETECTED
Whether motion is being detected.
HomeKit:
MotionDetected, a characteristic of its own.Matter has no motion cluster. A Matter motion sensor is the Occupancy Sensor device type with a PIR sensor type, so on Matter and Google Home this trait and
OCCUPANCY_DETECTEDread the same bit and always agree.Both are exposed anyway, rather than hiding one, because an app written against HomeKit's motion sensor should not have its feature vanish on Android. Just do not treat the two as independent signals.
-
OCCUPANCY_DETECTED
Whether a space is occupied.
HomeKit:
OccupancyDetected. Matter: Occupancy Sensing0x0406, bit 0 ofOccupancy. SeeMOTION_DETECTEDfor why the two coincide on Matter. -
CONTACT_DETECTED
Whether a contact sensor is closed. True means the door or window is shut.
The polarity is worth stating twice because the platforms differ: HomeKit's
ContactStateis an inverted integer where 0 means closed, and Matter's Boolean State0x0045StateValueis a boolean where true means closed. This API follows Matter and the iOS port inverts. -
LEAK_DETECTED
Whether water is being detected.
HomeKit:
LeakDetected. Matter: the Water Leak Detector device type over Boolean State0x0045, where true means a leak.Note that Boolean State means the opposite thing here than it does for
CONTACT_DETECTED, because its polarity is fixed by the device type rather than by the cluster. That is why the ports' mapping tables key on device type, cluster and attribute together, never on the cluster alone. -
SMOKE_DETECTED
Whether smoke is being detected, as an
AlarmState.HomeKit:
SmokeDetected, two-state, so it can only ever reportAlarmState.NORMALorAlarmState.CRITICAL.Matter: Smoke CO Alarm
0x005C,SmokeState, three-state, soAlarmState.WARNINGis reachable there.Read
AlarmState's note before building anything on this. It is not a fire-alarm channel. -
CO_DETECTED
Whether carbon monoxide is being detected, as an
AlarmState.HomeKit:
CarbonMonoxideDetected, two-state. Matter: Smoke CO Alarm0x005C,COState, three-state. Same asymmetry asSMOKE_DETECTED, and the same warning. -
CO_LEVEL
Measured carbon monoxide, in parts per million.
HomeKit:
CarbonMonoxideLevel, already ppm.Matter: Carbon Monoxide Concentration Measurement
0x040C, whoseMeasurementUnitis chosen by the accessory and may be a mass concentration. Where the accessory reports mass per volume the port reports no value, because converting needs the gas's molar mass and the ambient temperature and pressure, and inventing those would produce a number that looks like a measurement. -
CO2_LEVEL
-
AIR_QUALITY
A summarized air-quality rating, as an
AirQualityLevel.HomeKit:
AirQuality, six levels. Matter: Air Quality0x005B, seven. The enum documents how they are reconciled and which constant HomeKit can never produce. Do not hard-code thresholds against it and expect them to describe the same air on both platforms. -
PM2_5_DENSITY
-
PM10_DENSITY
Coarse particulate matter, in micrograms per cubic metre.
HomeKit:
PM10Density. Matter: PM10 Concentration0x042D. -
VOC_DENSITY
Total volatile organic compounds.
HomeKit reports these in micrograms per cubic metre; Matter's Total VOC Concentration
0x042Eis commonly in parts per billion. The unit therefore varies by accessory, which is exactly whyTraitValue.getDouble(TraitUnit)makes you name the one you expect and refuses to convert between the two dimensions. ReadTraitValue.getUnit()before you render this one. -
BATTERY_LEVEL
How much battery an accessory has left, as a percentage.
HomeKit:
BatteryLevel, already 0 to 100. Matter: Power Source0x002F,BatPercentRemaining, which is in half percent and runs 0 to 200. The port halves it; application code sees percent. -
BATTERY_CHARGING
Whether the battery is charging, as a
ChargingState.HomeKit:
ChargingState. Matter: Power Source0x002F,BatChargeState. The enum documents which constants each backend can never produce -- notably that HomeKit cannot tell a full battery on a charger from one that is running down. -
BATTERY_LOW
Whether the battery is low enough to want attention.
HomeKit:
StatusLowBattery, two-state. Matter: Power Source0x002F,BatChargeLevel, whose warning and critical levels both arrive here as true -- a small loss, and the only one on this trait. -
FAN_SPEED
How fast a fan is running, as a percentage.
HomeKit:
RotationSpeed, one characteristic for both reading and writing.Matter splits it: Fan Control
0x0202hasPercentSettingfor what was asked andPercentCurrentfor what the motor is actually doing. Writes go to the former and reads come from the latter, so a read immediately after a write can still show the old speed while the fan ramps. That is the accessory being honest, not a stale cache. -
FAN_MODE
How a fan is running, as a
FanMode.Matter: Fan Control
0x0202,FanMode, seven values. HomeKit has no real equivalent, and the enum documents the translation -- including that low, medium and high are written as speeds on iOS and never read back. If your UI shows a speed, readFAN_SPEEDinstead; it behaves the same everywhere. -
VOLUME
Speaker volume, as a percentage.
HomeKit:
Volumeon a speaker service. Matter: the speaker device type's Level Control, 0 to 254, with the same quantization asBRIGHTNESS.Expect this to be unsupported on almost every real accessory. HomeKit's speaker service is in practice only exposed by cameras and doorbells -- AirPlay speakers and HomePods are not HomeKit accessories and do not appear in the graph at all -- and Matter speakers barely exist in the field. This is not a media-playback API.
-
MUTE
-
-
Method Details
-
getId
The token this trait crosses the native boundary as, and the stable name to use in build hints and persisted state.
Returns
the identifier, never
null -
getValueKind
What sort of value this trait carries, and therefore which
TraitValuegetter reads it.Returns
the kind, never
null -
getUnit
The unit a
TraitValueKind.DOUBLEtrait is expressed in.TraitUnit.NONEfor every other kind.Returns
the canonical unit, never
null -
acceptsUnit
Whether this trait can only ever be read.
Whether a reading of this trait may arrive in the given unit.
The trait's own unit always may. So may one of the alternates, which exist for the traits whose javadoc says the unit varies by accessory -- VOC arrives in micrograms per cubic metre from HomeKit and in parts per billion from Matter, and refusing the second would refuse a reading this API promises to deliver.
Parameters
unit: the unit the value carries
Returns
truewhen a value in that unit is a valid reading of this trait -
enumValue
Whether a value belongs to this trait's domain enum.
Checked by constant name where the value has one, which is every value an application builds, and by ordinal range otherwise -- the codec decodes a wire ordinal with no constant in hand.
A value out of the wrong enum is not a curiosity: every enum crosses the wire as an ordinal, and
AlarmState.WARNINGandLockState.UNSECUREDare both 1. Asking a door lock for an alarm state, unguarded, unlocks the door.Parameters
value: the value to check, ornull
Returns
truewhen this is not a choice trait, or the value fits its domain The value an ordinal names in this trait's domain.The codec's way of turning a wire ordinal into the same value an application would have built for that constant: named, so it is equal to one built with
TraitValue.ofEnum(java.lang.Enum). Reading it back unnamed made every reading unequal to the value a caller was comparing it against.Parameters
ordinal: an ordinal in this trait's domain
Returns
the named value, or
nullwhen this trait has no enum domain or the ordinal is outside it -
acceptsEnumValue
-
acceptsEnumWrite
Whether a value may be WRITTEN to this trait.
Narrower than
acceptsEnumValue(TraitValue)for a target enum that carries states an accessory reports and cannot be asked for: a door that is OPENING, a thermostat mode HomeKit cannot express. Each enum says the same thing with its ownisWritable(); this is the form the write path can check when it has an ordinal rather than a constant.Parameters
value: the value to check, ornull
Returns
truewhen this is not a choice trait, or the value may be written -
isReadOnly
public boolean isReadOnly()A trait that is writable in principle may still be read-only on a particular accessory; that is a property of the
TraitConstraint, not of the trait. This answers the stronger question of whether writing it could ever mean anything.Returns
truewhen no accessory can accept a write of this trait -
hasNominalRange
public boolean hasNominalRange()Whether
getNominalMinimum()andgetNominalMaximum()mean anything.Returns
truewhen this trait has a documented range -
getNominalMinimum
public double getNominalMinimum()The bottom of this trait's documented range, in
getUnit().Documentation, not validation. What a given accessory will actually accept is on its
TraitConstraint, which is where a write is checked; a dimmer whose real floor is 10 percent says so there.Returns
the nominal minimum, or zero when
hasNominalRange()isfalse -
getNominalMaximum
public double getNominalMaximum()The top of this trait's documented range, in
getUnit(). SeegetNominalMinimum().Returns
the nominal maximum, or zero when
hasNominalRange()isfalse -
all
-
forId
Resolves a trait by its
getId(), total: an unknown ornullid answersnullrather than throwing.Total because ids arrive from outside -- a persisted favourite, a build hint, a port built against a newer version of this table. An unrecognized one is a value to skip, not a reason to fail a decode that still has good rows in it.
Parameters
id: a trait identifier, ornull
Returns
the trait, or
nullwhen this release has no such trait -
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())
-