@thingmate/entity
v0.12.1
Published
Universal API around property/action/event
Readme
@thingmate/entity
Universal API around property/action/event.
📦 Installation
yarn add @thingmate/entity
# or
npm install @thingmate/entity --save📜 Documentation
Entity
interface Entity {
readonly properties: EntityPropertyMap;
readonly actions: EntityActionMap;
readonly events: EntityEventMap;
}An Entity is a collection of properties, actions and events: whose goal is to create a universal representation of a thing.
Example: define a smart light bulb
interface SmartLightBulb extends Entity {
readonly properties: {
readonly onoff: EntityProperty<boolean>;
readonly color: EntityProperty<{ r: number, g: number, b: number }>;
};
readonly actions: {
readonly toggle: EntityAction<[], void>;
};
readonly events: {
readonly overheat: EntityEvent<void>;
};
}Every async API may be expressed using this universal API.
EntityProperty
interface EntityProperty<GValue> {
readonly get: EntityPropertyGet<GValue>;
readonly set: EntityPropertySet<GValue>;
readonly observer: EntityPropertyObserver<GValue>;
}This represents a property of an entity: a value that can be read, written and/or observed.
EntityPropertyGet
interface EntityPropertyGet<GValue> {
(options?: Abortable): Promise<GValue>;
}Fetches and returns the current value of a property.
Example: read the current color of a light bulb
console.log('color', await lightBulb.properties.color.get());EntityPropertySet
interface EntityPropertySet<GValue> {
(value: GValue, options?: Abortable): Promise<void>;
}Sets the current value of a property.
Example: set the color of a light bulb to red
await lightBulb.properties.color.set({ r: 255, g: 0, b: 0 });EntityPropertyObserver
type EntityPropertyObserver<GValue> = ReadableFlow<GValue, [options?: PushToPullOptions]>;A readable flow that observes the value of a property.
Example: observe the color of a light bulb
const controller = new AbortController();
for await (const color of lightBulb.properties.color.observer.open(controller.signal)) {
console.log('color', color);
}EntityAction
interface EntityAction<
GArguments extends readonly any[],
GReturn,
GAbortable extends Abortable = Abortable,
> {
(...args: [...GArguments, options?: GAbortable]): Promise<GReturn>;
}This represents an action of an entity: something that can be invoked to perform some action and produce a result.
Example: toggle on/off a light bulb when clicking on a button
document.getElementById('toggle-button').addEventListener('click', async () => {
await lightBulb.actions.toggle();
});EntityEvent
type EntityEvent<GValue> = ReadableFlow<GValue, [options?: PushToPullOptions]>This represents an event emitted by an entity: something that may happen any time and can be observed.
Example: toggle off a light bulb when it overheats
const controller = new AbortController();
for await (const _ of lightBulb.events.overheat.open(controller.signal)) {
await lightBulb.properties.onoff.set(false);
}Helpers
DEFAULT_ENTITY_PROPERTY
declare const DEFAULT_ENTITY_PROPERTY: EntityProperty<any>;A default implementation of an EntityProperty that can be used to implement only a subset of the API.
NOTE: all members of an
EntityPropertyare always defined, but, by default, calling these members will throw a "Not implemented" error.
Example: implement only the get method of a property
const lightBulb: SmartLightBulb = {
properties: {
onoff: {
...DEFAULT_ENTITY_PROPERTY,
get: (options?: Abortable) => fetch('http://localhost:3000/onoff', options).then(res => res.json()),
},
// ...
},
// ...
};
await lightBulb.properties.onoff.get(); // => true/false
await lightBulb.properties.onoff.set(false); // => throws an error, because `set` is not implementedmapEntityProperty
declare function mapEntityProperty<GIn, GOut>(
property: EntityProperty<GIn>,
{ inOut, outIn }: BidirectionalMapFunctions<GIn, GOut>,
): EntityProperty<GOut>;Transforms the input and output values of an EntityProperty.
Example: converts a temperature from celcius to fahrenheit
const temperatureSensor = {
properties: {
// converts the temperature from celcius to fahrenheit
temperature: mapEntityProperty(
temperatureProperty, // assumes this is a property of a temperature sensor in celcius
{
inOut: (value: number) => value * 9 / 5 + 32,
outIn: (value: number) => (value - 32) * 5 / 9,
},
),
},
// ...
};
await temperatureSensor.properties.temperature.get(); // => 32°F if the sensor reported 0°C