From 87026ac56265ba7a74f2f055522324692dcbc162 Mon Sep 17 00:00:00 2001 From: Zoe Codez Date: Sat, 25 May 2024 17:52:07 -0500 Subject: [PATCH] text --- src/extensions/domains/index.ts | 1 + src/extensions/domains/text.extension.ts | 121 +++++++++++++++++++++++ src/helpers/domains/select.ts | 12 +-- src/helpers/domains/text.ts | 86 +++++----------- src/synapse.module.ts | 2 + 5 files changed, 152 insertions(+), 70 deletions(-) create mode 100644 src/extensions/domains/text.extension.ts diff --git a/src/extensions/domains/index.ts b/src/extensions/domains/index.ts index 94d68d9..379fc6c 100644 --- a/src/extensions/domains/index.ts +++ b/src/extensions/domains/index.ts @@ -7,3 +7,4 @@ export * from "./scene.extension"; export * from "./select.extension"; export * from "./sensor.extension"; export * from "./switch.extension"; +export * from "./text.extension"; diff --git a/src/extensions/domains/text.extension.ts b/src/extensions/domains/text.extension.ts new file mode 100644 index 0000000..de37ccd --- /dev/null +++ b/src/extensions/domains/text.extension.ts @@ -0,0 +1,121 @@ +import { is, TServiceParams } from "@digital-alchemy/core"; + +import { RemovableCallback, VIRTUAL_ENTITY_BASE_KEYS } from "../../helpers"; +import { + SynapseTextParams, + SynapseVirtualText, + TextConfiguration, +} from "../../helpers/domains/text"; +import { TRegistry } from "../registry.extension"; + +export function VirtualText({ context, synapse }: TServiceParams) { + const registry = synapse.registry.create({ + context, + // @ts-expect-error it's fine + domain: "select", + }); + + // #MARK: create + return function < + STATE extends string = string, + ATTRIBUTES extends object = object, + >(entity: SynapseTextParams) { + const proxy = new Proxy({} as SynapseVirtualText, { + // #MARK: get + get(_, property: keyof SynapseVirtualText) { + // > common + // * name + if (property === "name") { + return entity.name; + } + // * unique_id + if (property === "unique_id") { + return unique_id; + } + // * onUpdate + if (property === "onUpdate") { + return loader.onUpdate(); + } + // * _rawConfiguration + if (property === "_rawConfiguration") { + return loader.configuration; + } + // * _rawAttributes + if (property === "_rawAttributes") { + return loader.attributes; + } + // * attributes + if (property === "attributes") { + return loader.attributesProxy(); + } + // * configuration + if (property === "configuration") { + return loader.configurationProxy(); + } + // * state + if (property === "state") { + return loader.state; + } + // > domain specific + // * onActivate + if (property === "onSetValue") { + return (callback: RemovableCallback) => + synapse.registry.removableListener(SET_VALUE, callback); + } + return undefined; + }, + + ownKeys: () => [...VIRTUAL_ENTITY_BASE_KEYS, "onSetValue"], + + // #MARK: set + set(_, property: string, value: unknown) { + // > common + // * state + if (property === "state") { + loader.setState(value as STATE); + return true; + } + // * attributes + if (property === "attributes") { + loader.setAttributes(value as ATTRIBUTES); + return true; + } + return false; + }, + }); + + // - Add to registry + const unique_id = registry.add(proxy, entity); + + // - Initialize value storage + const loader = synapse.storage.wrapper< + STATE, + ATTRIBUTES, + TextConfiguration + >({ + load_keys: ["mode", "max", "min", "pattern"], + name: entity.name, + registry: registry as TRegistry, + unique_id, + }); + + // - Attach bus events + const SET_VALUE = synapse.registry.busTransfer({ + context, + eventName: "set_value", + unique_id, + }); + + // - Attach static listener + if (is.function(entity.set_value)) { + proxy.onSetValue(entity.set_value); + } + + if (entity.managed !== false) { + proxy.onSetValue(({ value }) => (proxy.state = value)); + } + + // - Done + return proxy; + }; +} diff --git a/src/helpers/domains/select.ts b/src/helpers/domains/select.ts index 3f53b3d..2749852 100644 --- a/src/helpers/domains/select.ts +++ b/src/helpers/domains/select.ts @@ -5,7 +5,6 @@ import { RemovableCallback, } from "../base-domain.helper"; import { EntityConfigCommon } from "../common-config.helper"; -import { SensorDeviceClasses } from "./sensor"; export type SynapseSelectParams = BaseEntityParams & SelectConfiguration & { @@ -16,15 +15,12 @@ export type SynapseSelectParams = BaseEntityParams & managed?: boolean; }; -// supposed to be the same thing -export type SelectDeviceClasses = SensorDeviceClasses; type SetValueData = { value: string }; -export type SelectConfiguration = EntityConfigCommon & - SelectDeviceClasses & { - current_option?: string; - options?: string[]; - }; +export type SelectConfiguration = EntityConfigCommon & { + current_option?: string; + options?: string[]; +}; export type SynapseVirtualSelect = BaseVirtualEntity< string, diff --git a/src/helpers/domains/text.ts b/src/helpers/domains/text.ts index 5c1f2ad..cbd0ff7 100644 --- a/src/helpers/domains/text.ts +++ b/src/helpers/domains/text.ts @@ -1,72 +1,34 @@ -import { TContext } from "@digital-alchemy/core"; -import { PICK_ENTITY } from "@digital-alchemy/hass"; +import { + BaseEntityParams, + BaseVirtualEntity, + CreateRemovableCallback, + RemovableCallback, +} from "../base-domain.helper"; +import { EntityConfigCommon } from "../common-config.helper"; +import { SensorDeviceClasses } from "./sensor"; -import { BASE_CONFIG_KEYS, EntityConfigCommon } from "../common-config.helper"; -import { UpdateCallback } from "../event"; -import { TSynapseId } from "../utility.helper"; +export type SynapseTextParams = BaseEntityParams & + TextConfiguration & { + set_value: RemovableCallback; + /** + * default: true + */ + managed?: boolean; + }; -export type TText< - STATE extends TextValue, - ATTRIBUTES extends object = object, -> = { - context: TContext; - defaultState?: STATE; - defaultAttributes?: ATTRIBUTES; - name: string; -} & TextConfiguration; +// supposed to be the same thing +export type TextDeviceClasses = SensorDeviceClasses; +type SetValueData = { value: string }; export type TextConfiguration = EntityConfigCommon & { - /** - * Defines how the text should be displayed in the UI. - * It's recommended to use the default `auto`. - * Can be `box` or `slider` to force a display mode. - */ mode?: "text" | "password"; max?: number; min?: number; - step?: string; pattern?: string; }; -export type TextValue = string; - -export const TEXT_CONFIGURATION_KEYS = [ - ...BASE_CONFIG_KEYS, - "mode", - "max", - "min", - "step", - "pattern", -] as (keyof TextConfiguration)[]; - -export type HassTextEvent = { data: { unique_id: TSynapseId } }; - -export type TVirtualText< - STATE extends TextValue = TextValue, - ATTRIBUTES extends object = object, - CONFIGURATION extends TextConfiguration = TextConfiguration, - // @ts-expect-error its fine - ENTITY_ID extends PICK_ENTITY<"text"> = PICK_ENTITY<"text">, -> = { - /** - * Do not define attributes that change frequently. - * Create new sensors instead - */ - attributes: ATTRIBUTES; - configuration: CONFIGURATION; - _rawAttributes: ATTRIBUTES; - _rawConfiguration: ATTRIBUTES; - name: string; - /** - * look up the entity id, and - */ - onUpdate: UpdateCallback; - /** - * the current state - */ - state: STATE; - /** - * Used to uniquely identify this entity in home assistant - */ - unique_id: string; -}; +export type SynapseVirtualText = BaseVirtualEntity< + string, + object, + TextConfiguration +> & { onSetValue: CreateRemovableCallback }; diff --git a/src/synapse.module.ts b/src/synapse.module.ts index 870f8c6..c7bbca9 100644 --- a/src/synapse.module.ts +++ b/src/synapse.module.ts @@ -18,6 +18,7 @@ import { VirtualSelect, VirtualSensor, VirtualSwitch, + VirtualText, } from "./extensions"; import { HassDeviceMetadata } from "./helpers"; @@ -122,6 +123,7 @@ export const LIB_SYNAPSE = CreateLibrary({ storage: ValueStorage, switch: VirtualSwitch, + text: VirtualText, }, });