diff --git a/CHANGELOG.md b/CHANGELOG.md index ac0c000..ba23fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 _Changes in the next release_ +### Added + +- Add select-entity support. + --- ## 0.4.4-beta - 2026-04-07 diff --git a/index.ts b/index.ts index 1d1ef50..2c67362 100644 --- a/index.ts +++ b/index.ts @@ -1146,3 +1146,4 @@ export * from "./lib/entities/media_player.js"; export * from "./lib/entities/remote.js"; export * from "./lib/entities/sensor.js"; export * from "./lib/entities/switch.js"; +export * from "./lib/entities/select.js"; diff --git a/lib/entities/entity.ts b/lib/entities/entity.ts index 82f50e5..ff4babd 100644 --- a/lib/entities/entity.ts +++ b/lib/entities/entity.ts @@ -30,7 +30,9 @@ export enum EntityType { /** A sensor entity provides measured values from devices or dedicated hardware sensors. */ Sensor = "sensor", /** A switch entity can turn something on or off. */ - Switch = "switch" + Switch = "switch", + /** A select entity can choose an option from a list of options. */ + Select = "select" } export type EntityName = string | { [key: string]: string }; diff --git a/lib/entities/select.ts b/lib/entities/select.ts new file mode 100644 index 0000000..7c99191 --- /dev/null +++ b/lib/entities/select.ts @@ -0,0 +1,133 @@ +/** + * Select-entity definitions. + * See for more information. + * + * @copyright (c) 2026 by Unfolded Circle ApS. + * @license Apache License 2.0, see LICENSE for more details. + */ + +import { CommandHandler, Entity, EntityType, EntityName, EntityDescription, EntityOptions } from "./entity.js"; +import log from "../loggers.js"; + +/** + * Select entity states. + */ +export enum SelectStates { + /** + * The entity is currently not available. + */ + Unavailable = "UNAVAILABLE", + /** + * The entity is available, but the current state is unknown. + */ + Unknown = "UNKNOWN", + /** + * The select is on. + */ + On = "ON" +} + +/** + * Select entity features. + */ +export enum SelectFeatures {} + +/** + * Select entity attributes. + */ +export enum SelectAttributes { + /** + * State of the select entity. + */ + State = "state", + /** + * The current selected option. + */ + CurrentOption = "current_option", + /** + * The list of available options. + */ + Options = "options" +} + +/** + * Select entity commands. + */ +export enum SelectCommands { + /** + * Select an option from the list of options. + */ + SelectOption = "select_option", + /** + * Select the first option. + */ + SelectFirst = "select_first", + /** + * Select the last option. + */ + SelectLast = "select_last", + /** + * Select the next option. + */ + SelectNext = "select_next", + /** + * Select the previous option. + */ + SelectPrevious = "select_previous" +} + +/** + * Select entity device classes. + */ +export enum SelectDeviceClasses {} + +/** + * Select entity options. + */ +export enum SelectOptions {} + +export interface SelectParams { + icon?: string; + description?: EntityDescription; + features?: string[]; + attributes?: Partial>; + deviceClass?: string; + options?: EntityOptions; + area?: string; + cmdHandler?: CommandHandler; +} + +/** + * A select entity can choose an option from a list of options. + * + * See {@link https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_select.md select entity documentation} + * for more information. + */ +export class Select extends Entity { + /** + * Constructs a new select entity. + * + * @param id The entity identifier. Must be unique inside the integration driver. + * @param name The human-readable name of the entity. + * @param params Select-entity parameters. + * @throws AssertionError if invalid parameters are specified. + */ + constructor( + id: string, + name: EntityName, + { icon, description, features, attributes, deviceClass, options, area, cmdHandler }: SelectParams = {} + ) { + super(id, name, EntityType.Select, { + icon, + description, + features, + attributes, + deviceClass, + options, + area, + cmdHandler + }); + + log.debug(`Select entity created with id: ${this.id}`); + } +} diff --git a/test/select.test.ts b/test/select.test.ts new file mode 100644 index 0000000..c84a70d --- /dev/null +++ b/test/select.test.ts @@ -0,0 +1,47 @@ +import test from "ava"; +import { EntityType } from "../lib/entities/entity.js"; +import { Select, SelectAttributes, SelectStates } from "../lib/entities/select.js"; + +test("Select constructor without parameter object creates default Select class", (t) => { + const entity = new Select("test", "Test Select"); + + t.is(entity.id, "test"); + t.deepEqual(entity.name, { en: "Test Select" }); + t.is(entity.entity_type, EntityType.Select); + t.is(entity.icon, undefined); + t.is(entity.description, undefined); + t.is(entity.device_id, undefined); + t.deepEqual(entity.features, []); + t.deepEqual(entity.attributes, { [SelectAttributes.State]: SelectStates.Unknown }); + t.is(entity.device_class, undefined); + t.is(entity.options, undefined); + t.is(entity.area, undefined); + t.is(entity.hasCmdHandler, false); +}); + +test("Select constructor with parameter object", (t) => { + const attributes: Partial> = { + [SelectAttributes.State]: SelectStates.On, + [SelectAttributes.CurrentOption]: "option1", + [SelectAttributes.Options]: ["option1", "option2"] + }; + + const entity = new Select("test", "Test Select", { + icon: "uc:star", + description: "unit test", + attributes, + area: "Test lab" + }); + + t.is(entity.id, "test"); + t.deepEqual(entity.name, { en: "Test Select" }); + t.is(entity.entity_type, EntityType.Select); + t.is(entity.icon, "uc:star"); + t.deepEqual(entity.description, { en: "unit test" }); + t.deepEqual(entity.attributes, { + state: "ON", + current_option: "option1", + options: ["option1", "option2"] + }); + t.is(entity.area, "Test lab"); +});