Skip to content

hap-fluent / FluentCharacteristic

Class: FluentCharacteristic<T>

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:85

Type-safe, chainable wrapper around a single HAP-NodeJS Characteristic.

Remarks

Every HomeKit characteristic has a HAP-specified UUID, a value type (boolean, number, string, or object), and optional min/max/step constraints defined in the HAP specification. FluentCharacteristic enforces the TypeScript-level type via the T type parameter and delegates all runtime mutations to the underlying hap-nodejs characteristic so the HomeKit protocol contract is never broken.

Interceptors (logging, rate-limiting, clamping, codec, transform) are executed only inside onGet/onSet handler pipelines — not when you call set() or update() directly from your plugin code.

Use When

  • You need fine-grained control over a single characteristic (interceptors, value clamping, codec translation, audit trail).
  • You want to chain .clamp().log().onSet(handler) declaratively.
  • You're wrapping a characteristic returned by FluentService.characteristics.

Avoid When

  • You only need bulk initial-value assignment — use initializeAccessory() or FluentService.update() instead.
  • You need raw hap-nodejs Characteristic access (e.g., to call characteristic.getDefaultValue()) — retrieve .characteristic from the underlying service object, or use hap-nodejs directly.

Pitfalls

  • NEVER call set() with a value outside the HAP-specified range for that characteristic — iOS silently discards accessories that violate type constraints, causing the Home app to show the device as "Not Responding".
  • NEVER register more than one onGet or onSet handler on the same characteristic — hap-nodejs replaces the previous handler without warning, and the first handler's logic is silently dropped.
  • NEVER pass null to set()isCharacteristicValue rejects null and throws FluentCharacteristicError; use update() only with typed values.
  • NEVER add interceptors after registering onGet/onSet handlers — the interceptor chain is captured at onGet/onSet call time; later .clamp() or .log() calls will not apply to already-registered handlers.
  • NEVER assume interceptors apply to direct set() calls — they only wrap onGet/onSet handlers triggered by HomeKit polling or user actions.

Example

typescript
import { getOrAddService } from 'hap-fluent';

const lightbulb = getOrAddService(accessory, hap.Service.Lightbulb);

lightbulb.characteristics.brightness
  .clamp(0, 100)          // clamp incoming HomeKit values to valid range
  .log()                  // log all get/set operations
  .onGet(async () => currentBrightness)
  .onSet(async (value) => { currentBrightness = value; });

// Programmatic update (no SET handler triggered)
lightbulb.characteristics.brightness.update(75);

Type Parameters

Type ParameterDescription
T extends CharacteristicValueThe TypeScript value type for this characteristic (boolean, number, string, or CharacteristicValue).

Constructors

Constructor

ts
new FluentCharacteristic<T>(characteristic): FluentCharacteristic<T>;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:91

Parameters

ParameterTypeDescription
characteristicCharacteristicHAP characteristic to wrap.

Returns

FluentCharacteristic<T>

Methods

audit()

ts
audit(maxEntries?): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:398

Add audit trail interceptor that tracks all operations.

Parameters

ParameterTypeDefault value
maxEntriesnumber1000

Returns

this

This FluentCharacteristic instance for chaining

Example

typescript
characteristic.audit().onSet(handler); // Logs audit trail

clamp()

ts
clamp(min, max): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:367

Add value clamping interceptor to ensure numeric values stay within bounds.

Parameters

ParameterTypeDescription
minnumberMinimum value (inclusive)
maxnumberMaximum value (inclusive)

Returns

this

This FluentCharacteristic instance for chaining

Example

typescript
characteristic.clamp(0, 100).onSet(handler); // Ensures value is 0-100

clearInterceptors()

ts
clearInterceptors(): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:482

Remove all interceptors from this characteristic.

Returns

this

This FluentCharacteristic instance for chaining


codec()

ts
codec(encode, decode): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:469

Add a codec interceptor for two-way value transformation.

Codecs allow you to transform values when setting (encode) and retrieving (decode). This is useful for converting between different formats or units.

Parameters

ParameterTypeDescription
encode(value) => CharacteristicValueFunction to transform values when setting (to HAP format)
decode(value) => CharacteristicValueFunction to transform values when getting (from HAP format)

Returns

this

This FluentCharacteristic instance for chaining

Example

typescript
// Convert between Celsius and Fahrenheit
characteristic.codec(
  (celsius) => (celsius * 9/5) + 32,  // encode: C to F
  (fahrenheit) => (fahrenheit - 32) * 5/9  // decode: F to C
).onSet(async (fahrenheit) => {
  console.log('Temperature in F:', fahrenheit);
});

// Convert between different string formats
characteristic.codec(
  (value) => String(value).toUpperCase(),  // encode
  (value) => String(value).toLowerCase()   // decode
);

// Convert complex objects to/from JSON
characteristic.codec(
  (obj) => JSON.stringify(obj),  // encode
  (str) => JSON.parse(String(str))  // decode
);

get()

ts
get(): T | undefined;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:109

Get the current characteristic value.

Returns

T | undefined

The current characteristic value, or undefined if not set.


limit()

ts
limit(maxCalls, windowMs): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:350

Add rate limiting interceptor to prevent excessive updates.

Parameters

ParameterTypeDescription
maxCallsnumberMaximum number of calls allowed
windowMsnumberTime window in milliseconds

Returns

this

This FluentCharacteristic instance for chaining

Example

typescript
characteristic.limit(5, 1000).onSet(handler); // Max 5 calls per second

log()

ts
log(): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:333

Add logging interceptor that logs all operations (beforeSet, afterSet, beforeGet, afterGet, errors).

Returns

this

This FluentCharacteristic instance for chaining

Example

typescript
characteristic.log().onSet(async (value) => {
  console.log('Value from HomeKit:', value);
});

onGet()

ts
onGet(handler): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:223

Register an async getter for the characteristic.

Parameters

ParameterTypeDescription
handler() => Promise<T>Async getter returning the current value.

Returns

this

This FluentCharacteristic instance for chaining.


onSet()

ts
onSet(handler): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:267

Register an async setter for the characteristic.

Parameters

ParameterTypeDescription
handler(value) => Promise<void>Async setter receiving the new value.

Returns

this

This FluentCharacteristic instance for chaining.


set()

ts
set(value): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:130

Set the characteristic value.

Parameters

ParameterTypeDescription
valueTNew value to set.

Returns

this

This FluentCharacteristic instance for chaining.

Throws

If value is invalid or setValue fails

Remarks

This method is for direct programmatic value setting. Interceptors are applied in onSet handlers, which are triggered when HomeKit accesses the characteristic.


setProps()

ts
setProps(props): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:99

Update the characteristic's metadata properties.

Parameters

ParameterTypeDescription
propsPartialAllowingNull<CharacteristicProps>Partial characteristic properties to apply.

Returns

this

This FluentCharacteristic instance for chaining.


transform()

ts
transform(transformFn): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:383

Add value transformation interceptor that applies a custom function.

Parameters

ParameterTypeDescription
transformFn(value) => CharacteristicValueFunction to transform the value

Returns

this

This FluentCharacteristic instance for chaining

Example

typescript
characteristic.transform(v => Math.round(v as number)).onSet(handler);

update()

ts
update(value): this;

Defined in: packages/hap-fluent/src/FluentCharacteristic.ts:178

Update the characteristic value without calling SET handlers.

Parameters

ParameterTypeDescription
valueTNew value to apply.

Returns

this

This FluentCharacteristic instance for chaining.

Throws

If value is invalid or updateValue fails

Released under the Apache-2.0 License.