diff --git a/futurehome/CHANGELOG.md b/futurehome/CHANGELOG.md index b556444..8cf60b3 100644 --- a/futurehome/CHANGELOG.md +++ b/futurehome/CHANGELOG.md @@ -6,6 +6,7 @@ - Set 'battery' entity category to 'diagnostic' to hide it from the main HA view. - Do not expose 'battery' entity twice if it supports both level and low/high binary state. - Changed the default 'sensor_lumin' unit from 'Lux' to 'lx'. +- Added support for 'indicator_ctrl' service (identify devices). # 0.1.0 (24.07.2025) diff --git a/futurehome/README.md b/futurehome/README.md index c42ee0c..f1ecc8c 100644 --- a/futurehome/README.md +++ b/futurehome/README.md @@ -156,15 +156,15 @@ todo: links to the .ts service implementations below ## System or meta, not essential services -| Service | Implementation status | -| --- | --- | -| gateway | | -| association | | -| diagnostic | | -| indicator_ctrl | | -| ota | | -| parameters | | -| technology_specific | | -| time | | -| version | | -| dev_sys | | +| Service | Implementation status | Description | +| --- | --- | --- | +| gateway | | | +| association | | | +| diagnostic | | | +| indicator_ctrl | ✅ | Identify devices | +| ota | | | +| parameters | | | +| technology_specific | | | +| time | | | +| version | | | +| dev_sys | | | diff --git a/futurehome/src/fimp/fimp.ts b/futurehome/src/fimp/fimp.ts index ed470f8..5b8e910 100644 --- a/futurehome/src/fimp/fimp.ts +++ b/futurehome/src/fimp/fimp.ts @@ -43,6 +43,7 @@ export async function sendFimpMsg({ cmd, val, val_t, + props = {}, timeoutMs = 10000, }: { address: string; @@ -50,6 +51,7 @@ export async function sendFimpMsg({ cmd: string; val: unknown; val_t: FimpValueType; + props?: any; timeoutMs?: number; }): Promise { const uid = uuidv4(); @@ -57,7 +59,7 @@ export async function sendFimpMsg({ const message = JSON.stringify({ corid: null, ctime: new Date().toISOString(), - props: {}, + props: props, resp_to: 'pt:j1/mt:rsp/rt:app/rn:ha-futurehome/ad:addon', serv: service, src: 'ha-futurehome', diff --git a/futurehome/src/ha/publish_device.ts b/futurehome/src/ha/publish_device.ts index f0535fb..088a6c1 100644 --- a/futurehome/src/ha/publish_device.ts +++ b/futurehome/src/ha/publish_device.ts @@ -8,6 +8,7 @@ import { basic__components } from '../services/basic'; import { battery__components } from '../services/battery'; import { color_ctrl__components } from '../services/color_ctrl'; import { fan_ctrl__components } from '../services/fan_ctrl'; +import { indicator_ctrl__components } from '../services/indicator_ctrl'; import { out_bin_switch__components } from '../services/out_bin_switch'; import { out_lvl_switch__components } from '../services/out_lvl_switch'; import { scene_ctrl__components } from '../services/scene_ctrl'; @@ -161,6 +162,7 @@ const serviceHandlers: { battery: battery__components, color_ctrl: color_ctrl__components, fan_ctrl: fan_ctrl__components, + indicator_ctrl: indicator_ctrl__components, out_bin_switch: out_bin_switch__components, out_lvl_switch: out_lvl_switch__components, scene_ctrl: scene_ctrl__components, diff --git a/futurehome/src/services/indicator_ctrl.ts b/futurehome/src/services/indicator_ctrl.ts new file mode 100644 index 0000000..38115da --- /dev/null +++ b/futurehome/src/services/indicator_ctrl.ts @@ -0,0 +1,54 @@ +import { sendFimpMsg } from '../fimp/fimp'; +import { + VinculumPd7Device, + VinculumPd7Service, +} from '../fimp/vinculum_pd7_device'; +import { HaMqttComponent } from '../ha/mqtt_components/_component'; +import { + CommandHandlers, + ServiceComponentsCreationResult, +} from '../ha/publish_device'; + +export function indicator_ctrl__components( + topicPrefix: string, + device: VinculumPd7Device, + svc: VinculumPd7Service, +): ServiceComponentsCreationResult | undefined { + const components: Record = {}; + const commandHandlers: CommandHandlers = {}; + + if (svc.intf?.includes('cmd.indicator.set_visual_element')) { + const commandTopic = `${topicPrefix}${svc.addr}/cmd.indicator.set_visual_element/command`; + + components[`${svc.addr}/set_visual_element`] = { + unique_id: `${svc.addr}/set_visual_element`, + platform: 'button', + entity_category: 'diagnostic', + device_class: 'identify', + command_topic: commandTopic, + }; + + commandHandlers[commandTopic] = async (_payload: string) => { + await sendFimpMsg({ + address: svc.addr, + service: 'indicator_ctrl', + cmd: 'cmd.indicator.set_visual_element', + val_t: 'null', + val: null, + props: { + duration: '3', + }, + }); + }; + } + + // cmd.indicator.set_text is not used anymore + + // since cmd.indicator.set_visual_element should always be present when cmd.indicator.identify is, + // there's no need to handle cmd.indicator.identify separately. + + return { + components, + commandHandlers, + }; +} diff --git a/futurehome/src/services/scene_ctrl.ts b/futurehome/src/services/scene_ctrl.ts index 4c4b885..507eeb0 100644 --- a/futurehome/src/services/scene_ctrl.ts +++ b/futurehome/src/services/scene_ctrl.ts @@ -28,7 +28,7 @@ export function scene_ctrl__components( svc: VinculumPd7Service, ): ServiceComponentsCreationResult | undefined { const components: Record = {}; - const handlers: CommandHandlers = {}; + const commandHandlers: CommandHandlers = {}; // ───────────── read-only entities ───────────── if (svc.intf?.includes('evt.scene.report')) { @@ -63,7 +63,7 @@ export function scene_ctrl__components( value_template: `{{ value_json['${svc.addr}'].scene }}`, }; - handlers[commandTopic] = async (payload: string) => { + commandHandlers[commandTopic] = async (payload: string) => { if (!supScenes.includes(payload)) return; // ignore bogus payloads await sendFimpMsg({ @@ -81,6 +81,6 @@ export function scene_ctrl__components( return { components, - commandHandlers: Object.keys(handlers).length ? handlers : undefined, + commandHandlers, }; }