Skip to content

hap-fluent / FluentService

Type Alias: FluentService<T>

ts
type FluentService<T> = InterfaceForService<T> & {
  characteristics: { [K in CharacteristicNamesOf<T> as CamelCase<K>]: FluentCharacteristic<InterfaceForService<T>[K] & CharacteristicValue> };
  onGet: void;
  onSet: void;
  update: void;
};

Defined in: packages/hap-fluent/src/FluentService.ts:79

Strongly-typed, fluent view of a HAP service and all its characteristics.

Type Declaration

NameTypeDescriptionDefined in
characteristics{ [K in CharacteristicNamesOf<T> as CamelCase<K>]: FluentCharacteristic<InterfaceForService<T>[K] & CharacteristicValue> }Collection of all characteristics for this service Keys are camelCase characteristic names (e.g., 'on', 'brightness')packages/hap-fluent/src/FluentService.ts:84
onGet()(key, callback) => voidRegister an async getter for a characteristic Example service.onGet('on', async () => { const state = await getDeviceState(); return state.isOn; });packages/hap-fluent/src/FluentService.ts:103
onSet()(key, callback) => voidRegister an async setter for a characteristic Example service.onSet('on', async (value) => { await setDeviceState({ isOn: value }); });packages/hap-fluent/src/FluentService.ts:120
update()(key, value) => voidUpdate a characteristic value without triggering SET handlers Example // Update brightness from external state change service.update('brightness', newBrightness);packages/hap-fluent/src/FluentService.ts:136

Type Parameters

Type ParameterDescription
T extends typeof ServiceHAP service class constructor (e.g., typeof hap.Service.Lightbulb).

Remarks

FluentService<T> is an intersection of the generated HAP interface for service T and a set of helper methods (onGet, onSet, update) plus a characteristics map of FluentCharacteristic instances keyed by camelCase characteristic name.

Shorthand property accessors (both camelCase on and PascalCase On) are defined at runtime via Object.defineProperty so that TypeScript infers the correct value type from the service interface.

The type parameter T must be a HAP service constructor with an interface declaration (generated by hap-codegen). Without the generated interface, InterfaceForService<T> resolves to never and the type collapses.

Use When

  • You are building a Homebridge plugin that wraps one or more HAP services.
  • You want compile-time IntelliSense on characteristic names and their value types (booleans, numbers, strings) for a specific service.
  • You need to register onGet/onSet handlers or update characteristic values without manually typing characteristic display names.

Avoid When

  • You need direct hap-nodejs Service access (e.g., service.getCharacteristic(), service.setPrimaryService(true)) — FluentService does not expose all service methods; obtain the raw service via platformAccessory.getService().
  • Your accessory has a single, trivial characteristic — hap-nodejs directly may be simpler and incurs no overhead.

Pitfalls

  • NEVER register the same characteristic handler twice on the same service — hap-nodejs silently replaces the first handler, causing the original logic to be permanently dropped with no runtime error.
  • NEVER mutate characteristics after platformAccessory has been published via api.registerPlatformAccessories() — hap-nodejs caches the service state at publish time; post-publish structural changes cause a silent desync with the iOS Home controller.
  • NEVER assume characteristics contains characteristics that were not present on the service when wrapService() was called — optional HAP characteristics must be added to the service before wrapping.
  • NEVER mix PascalCase and camelCase for the same characteristic in one handler registration call — the shorthand properties are aliases; prefer consistent camelCase to avoid confusion.

Example

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

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

// Register HomeKit GET/SET handlers
lightbulb.onGet('on', async () => await getLightState());
lightbulb.onSet('on', async (value) => await setLightState(value));

// Programmatic update (no SET handler triggered, notifies HomeKit)
lightbulb.update('brightness', 75);

// Shorthand property access
lightbulb.on = false;
const brightness = lightbulb.brightness; // number

Released under the Apache-2.0 License.