From f18c6d4e3d27658aa062ca56cc8f2f1e531db9be Mon Sep 17 00:00:00 2001 From: abearxiong Date: Tue, 30 Dec 2025 02:57:24 +0800 Subject: [PATCH] update --- package.json | 6 +++++- readme.md | 21 ++++++++++++++++++++- src/core.ts | 12 ++++++++---- src/light.ts | 46 +++++++++++++++++++++++++++++++++++++++++++++- test/common.ts | 8 +++++--- test/speaker.ts | 27 +++++++++++++++++++++++++++ 6 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 test/speaker.ts diff --git a/package.json b/package.json index 59b38c8..57f8199 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,15 @@ { "name": "@kevisual/ha-api", - "version": "0.0.5", + "version": "0.0.6", "description": "", "main": "src/index.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, + "app": { + "type": "pm2-system-app", + "entry": "src/index.ts" + }, "files": [ "src" ], diff --git a/readme.md b/readme.md index bb62f02..0323990 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# 灯光管理 +## 灯光管理 该模块提供对Home Assistant中灯光设备的管理功能,包括获取灯光列表、搜索特定灯光设备以及控制灯光的开关状态。 @@ -25,3 +25,22 @@ if(searchResult.hasMore) { console.log('没有找到匹配的灯光设备。'); } ``` + +## 音箱管理 + +该模块提供对Home Assistant中音箱设备的管理功能,执行文本指令等。 + +```ts +import { TextHA } from '@kevisual/ha-api'; + +const tts = new TextHA({ + host: 'http://your-home-assistant:8123', + token: 'your-long-lived-access-token', +}); +// 获取所有音箱设备 +const entities = await text.getSpeakerEntities(); +// 执行文本指令 +const res2 = await text.executeTextDirective('text.xiaomi_lx06_9e08_execute_text_directive', '关闭阳台灯'); +console.log(res2); +``` + diff --git a/src/core.ts b/src/core.ts index de2b1c4..ba04f5d 100644 --- a/src/core.ts +++ b/src/core.ts @@ -4,6 +4,7 @@ export type HACoreOptions = { token: string; homeassistantURL?: string; ttl?: number; + ha?: HACore; } /** * https://developers.home-assistant.io/docs/api/rest/ @@ -14,7 +15,10 @@ export class HACore { static serviceName = ''; cache: LRUStorage; constructor(options: HACoreOptions) { - this.token = options?.token; + this.token = options?.token!; + if (!this.token && options.ha) { + this.token = options.ha?.token; + } this.homeassistantURL = options?.homeassistantURL || 'http://localhost:8123'; this.cache = new LRUStorage({ max: 200, ttl: options?.ttl || 1000 * 60 * 30 }); // 30分钟缓存 } @@ -91,9 +95,9 @@ export class HACore { }); return infoList; } - async runService(opts: { entity_id?: string, name?: string, service?: string, data?: Record }) { + async runService(opts: { entity_id?: string, name?: string, serverName?: string, service?: string, data?: Record }) { // e.g., service: 'turn_on', 'turn_off', 'toggle' - const serviceName = (this.constructor as typeof HACore).serviceName; + const serviceName = opts?.serverName || (this.constructor as typeof HACore).serviceName; let { entity_id, service = '', data } = opts; if (!entity_id && opts.name) { const entities = await this.getServiceEntities(); @@ -156,7 +160,7 @@ export const entitiesTypes = [ { "type": "number", "desc": "可调整的数值控件,有上下限,如温度设定、定时器" } ] -type EntityItem = { +export type EntityItem = { entity_id: string; attributes: { friendly_name?: string; diff --git a/src/light.ts b/src/light.ts index b88b8af..f7d9a9b 100644 --- a/src/light.ts +++ b/src/light.ts @@ -1,5 +1,5 @@ import Fuse from "fuse.js"; -import { HACore, HACoreOptions, InfoItem } from "./core.ts"; +import { HACore, HACoreOptions, InfoItem, EntityItem } from "./core.ts"; export class LightHA extends HACore { static serviceName = 'light'; constructor(options: HACoreOptions) { @@ -59,4 +59,48 @@ export class ButtonHA extends HACore { constructor(options: HACoreOptions) { super(options); } +} + + +export class SpeakerHA extends HACore { + static serviceName = 'notify'; + constructor(options: HACoreOptions) { + super(options); + } + /** + * 获取音箱实体列表 + * @returns + */ + getSpeakerEntities(): Promise { + return this.getEntities((entity: EntityItem) => entity.entity_id.startsWith(`notify.`) && entity.attributes?.friendly_name?.includes?.('音箱')); + } +} + +export class TextHA extends HACore { + static serviceName = 'text'; + constructor(options: HACoreOptions) { + super(options); + } + /** + * 获取音箱实体列表 + * @returns + */ + getSpeakerEntities(): Promise { + return this.getEntities((entity: EntityItem) => entity.entity_id.startsWith(`text.`) && entity.attributes?.friendly_name?.includes?.('音箱')); + } + /** + * 执行文本指令 + * @param entity_id + * @param value + * @returns + */ + executeTextDirective(entity_id: string, value: string): Promise { + return this.runService({ + entity_id, + service: 'set_value', + data: { + value + } + }); + } } \ No newline at end of file diff --git a/test/common.ts b/test/common.ts index 39ac6ae..8de76cf 100644 --- a/test/common.ts +++ b/test/common.ts @@ -1,4 +1,4 @@ -import { LightHA, AutoHA, EventHA, ScriptHA, ButtonHA } from "../src/index.ts"; +import { LightHA, AutoHA, EventHA, ScriptHA, ButtonHA, HACore, SpeakerHA, TextHA } from "../src/index.ts"; import util from 'node:util'; import dotenv from 'dotenv'; @@ -7,13 +7,15 @@ export const showMore = (obj: any) => { return util.inspect(obj, { depth: null, colors: true }); } -export const hacore = new LightHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); +export const hacore = new HACore({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); +export const light = new LightHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); export const auto = new AutoHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); export const event = new EventHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); export const script = new ScriptHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); export const button = new ButtonHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); - +export const speaker = new SpeakerHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); +export const text = new TextHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL }); // const enti = await hacore.getEntities((item) => item.attributes?.friendly_name?.includes?.('电视')); // console.log(showMore(enti), enti.length); diff --git a/test/speaker.ts b/test/speaker.ts new file mode 100644 index 0000000..b25f959 --- /dev/null +++ b/test/speaker.ts @@ -0,0 +1,27 @@ +import { speaker, showMore, text } from './common.ts'; + +// const entities = await speaker.getSpeakerEntities(); +const entities = await text.getSpeakerEntities(); + + + +console.log(showMore(entities)); + +console.log(`实体数量: ${entities.length}`); + +// const res = await speaker.runService({ +// entity_id: 'text.xiaomi_lx06_9e08_execute_text_directive', +// service: 'set_value', +// serverName: 'text', +// data: { +// value: '关闭阳台灯' +// } +// }); + +// console.log(showMore(res)); + +const res2 = await text.executeTextDirective('text.xiaomi_lx06_9e08_execute_text_directive', '关闭阳台灯'); + +console.log(showMore(res2)); + +const call = { "type": "call_service", "domain": "text", "service": "set_value", "target": { "entity_id": "text.xiaomi_lx06_9e08_execute_text_directive" }, "return_response": false, "service_data": { "value": "关闭阳台灯" }, "id": 101 } \ No newline at end of file