更新版本号,重构核心类,移除未使用的自动化和事件功能,优化灯光管理,调整测试用例以适应新结构
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kevisual/ha-api",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"description": "",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
|
||||
13
src/auto.ts
13
src/auto.ts
@@ -1,13 +0,0 @@
|
||||
import Fuse from "fuse.js";
|
||||
import { HACore, HACoreOptions } from "./core";
|
||||
|
||||
export class AutoHA extends HACore {
|
||||
constructor(options: HACoreOptions) {
|
||||
super(options);
|
||||
}
|
||||
getAutos() {
|
||||
const auto = this.getEntities((entity: any) => entity.entity_id.startsWith('automation.'));
|
||||
return auto;
|
||||
}
|
||||
|
||||
}
|
||||
69
src/core.ts
69
src/core.ts
@@ -8,9 +8,10 @@ export type HACoreOptions = {
|
||||
export class HACore {
|
||||
token: string;
|
||||
homeassistantURL?: string;
|
||||
static serviceName = '';
|
||||
constructor(options: HACoreOptions) {
|
||||
this.token = options.token;
|
||||
this.homeassistantURL = options.homeassistantURL || 'http://localhost:8123';
|
||||
this.token = options?.token;
|
||||
this.homeassistantURL = options?.homeassistantURL || 'http://localhost:8123';
|
||||
}
|
||||
async get(opts: { url: string, params?: Record<string, any> }): Promise<any> {
|
||||
const _u = new URL(opts.url, this.homeassistantURL);
|
||||
@@ -46,14 +47,14 @@ export class HACore {
|
||||
return response.json();
|
||||
});
|
||||
}
|
||||
async getEntities(filter?: (entity: any) => boolean): Promise<any[]> {
|
||||
async getEntities(filter?: (entity: EntityItem) => boolean): Promise<EntityItem[]> {
|
||||
const entities = await this.get({ url: '/api/states' });
|
||||
if (filter) {
|
||||
return entities.filter(filter);
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
async getState(entity_id?: string): Promise<any> {
|
||||
async getState(entity_id?: string): Promise<EntityItem> {
|
||||
return this.get({ url: `/api/states/${entity_id}` });
|
||||
}
|
||||
async getEntityTypes(): Promise<any[]> {
|
||||
@@ -65,9 +66,54 @@ export class HACore {
|
||||
});
|
||||
return Array.from(types);
|
||||
}
|
||||
async getServiceEntities(): Promise<EntityItem[]> {
|
||||
const serviceName = (this.constructor as typeof HACore).serviceName;
|
||||
return this.getEntities((entity: EntityItem) => entity.entity_id.startsWith(`${serviceName}.`));
|
||||
}
|
||||
async getInfoList(): Promise<InfoItem[]> {
|
||||
const lights = await this.getServiceEntities();
|
||||
const infoList = lights.map((light: any) => {
|
||||
return {
|
||||
entity_id: light.entity_id,
|
||||
name: light.attributes?.friendly_name || '',
|
||||
state: light.state,
|
||||
};
|
||||
});
|
||||
return infoList;
|
||||
}
|
||||
async runService(opts: { entity_id?: string, name?: string, service?: string, data?: Record<string, any> }) {
|
||||
// e.g., service: 'turn_on', 'turn_off', 'toggle'
|
||||
const serviceName = (this.constructor as typeof HACore).serviceName;
|
||||
let { entity_id, service = '', data } = opts;
|
||||
if (!entity_id && opts.name) {
|
||||
const entities = await this.getServiceEntities();
|
||||
const target = entities.find((entity: any) => entity.attributes?.friendly_name.includes(opts.name || ''));
|
||||
if (target) {
|
||||
entity_id = target.entity_id;
|
||||
} else {
|
||||
throw new Error(`${serviceName} 服务中名为为 "${opts.name}" 的不存在.`);
|
||||
}
|
||||
}
|
||||
if (!service) {
|
||||
const state = await this.getState(entity_id!);
|
||||
service = state.state === 'on' ? 'turn_off' : 'turn_on';
|
||||
}
|
||||
return this.post({
|
||||
url: `/api/services/${serviceName}/${service}`,
|
||||
body: {
|
||||
entity_id,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type InfoItem = {
|
||||
entity_id: string;
|
||||
name: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
export const entitiesTypes = [
|
||||
{ "type": "conversation", "desc": "处理自然语言对话指令,如语音助手" },
|
||||
@@ -98,4 +144,17 @@ export const entitiesTypes = [
|
||||
{ "type": "light", "desc": "控制照明设备,支持开关、亮度、颜色、色温" },
|
||||
{ "type": "notify", "desc": "发送通知,如推送、邮件、语音播报" },
|
||||
{ "type": "number", "desc": "可调整的数值控件,有上下限,如温度设定、定时器" }
|
||||
]
|
||||
]
|
||||
|
||||
type EntityItem = {
|
||||
entity_id: string;
|
||||
attributes: {
|
||||
friendly_name?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
state: 'on' | 'off' | 'unavailable' | 'unknown' | string;
|
||||
last_changed: string;
|
||||
last_updated: string;
|
||||
last_reported?: string;
|
||||
context?: any
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import Fuse from "fuse.js";
|
||||
import { HACore, HACoreOptions } from "./core";
|
||||
|
||||
export class EventHA extends HACore {
|
||||
getEvents() {
|
||||
const events = this.getEntities((entity: any) => entity.entity_id.startsWith('event.'));
|
||||
return events;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,2 @@
|
||||
export * from './light.ts'
|
||||
export * from './core.ts'
|
||||
|
||||
export * from './auto.ts'
|
||||
|
||||
export * from './event.ts'
|
||||
|
||||
export * from './script.ts'
|
||||
export * from './core.ts'
|
||||
79
src/light.ts
79
src/light.ts
@@ -1,57 +1,12 @@
|
||||
import Fuse from "fuse.js";
|
||||
import { HACore, HACoreOptions } from "./core";
|
||||
import { HACore, HACoreOptions, InfoItem } from "./core.ts";
|
||||
|
||||
export class LightHA extends HACore {
|
||||
static serviceName = 'light';
|
||||
constructor(options: HACoreOptions) {
|
||||
super(options);
|
||||
}
|
||||
async getLights(): Promise<any[]> {
|
||||
return this.getEntities((entity: any) => entity.entity_id.startsWith('light.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换灯的状态,若未指定 service,则根据当前状态切换
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
async toggleLight(opts: { entity_id?: string, name?: string, service?: string, data?: Record<string, any> }): Promise<any> {
|
||||
// e.g., service: 'turn_on', 'turn_off', 'toggle'
|
||||
let { entity_id, service = '', data } = opts;
|
||||
if (!entity_id && opts.name) {
|
||||
const entities = await this.getLights();
|
||||
const target = entities.find((entity: any) => entity.attributes?.friendly_name.includes(opts.name || ''));
|
||||
if (target) {
|
||||
entity_id = target.entity_id;
|
||||
} else {
|
||||
throw new Error(`Light with name including "${opts.name}" not found.`);
|
||||
}
|
||||
} else if (!entity_id) {
|
||||
throw new Error('实体 ID 或 名称 必须提供其一。');
|
||||
}
|
||||
if (!service) {
|
||||
const state = await this.getState(entity_id!);
|
||||
service = state.state === 'on' ? 'turn_off' : 'turn_on';
|
||||
}
|
||||
return this.post({
|
||||
url: `/api/services/light/${service}`,
|
||||
body: {
|
||||
entity_id,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
async getInfoList(): Promise<LightItem[]> {
|
||||
const lights = await this.getLights();
|
||||
const infoList = lights.map((light: any) => {
|
||||
return {
|
||||
entity_id: light.entity_id,
|
||||
name: light.attributes?.friendly_name || '',
|
||||
state: light.state,
|
||||
};
|
||||
});
|
||||
return infoList;
|
||||
}
|
||||
async searchLight(keyword: string): Promise<{ result: LightItem[], lights: LightItem[], id?: string, hasMore?: boolean }> {
|
||||
async searchLight(keyword: string): Promise<{ result: InfoItem[], lights: InfoItem[], id?: string, hasMore?: boolean }> {
|
||||
const devices = await this.getInfoList();
|
||||
const fuse = new Fuse(devices, {
|
||||
keys: ['name'], // 搜索字段
|
||||
@@ -69,17 +24,33 @@ export class LightHA extends HACore {
|
||||
return { result: resultItems, lights: devices, id, hasMore };
|
||||
}
|
||||
async closeAllLights(): Promise<void> {
|
||||
const lights = await this.getLights();
|
||||
const lights = await this.getServiceEntities();
|
||||
for (const light of lights) {
|
||||
if (light.state === 'on') {
|
||||
this.toggleLight({ entity_id: light.entity_id, service: 'turn_off' });
|
||||
this.runService({ entity_id: light.entity_id, service: 'turn_off' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type LightItem = {
|
||||
entity_id: string;
|
||||
name: string;
|
||||
state: string;
|
||||
export class EventHA extends HACore {
|
||||
static serviceName = 'event';
|
||||
constructor(options: HACoreOptions) {
|
||||
super(options);
|
||||
}
|
||||
}
|
||||
|
||||
export class AutoHA extends HACore {
|
||||
static serviceName = 'automation';
|
||||
constructor(options: HACoreOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ScriptHA extends HACore {
|
||||
static serviceName = 'script';
|
||||
constructor(options: HACoreOptions) {
|
||||
super(options);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { HACore, HACoreOptions } from "./core";
|
||||
|
||||
export class ScriptHA extends HACore {
|
||||
static serviceName = 'script';
|
||||
getScripts() {
|
||||
const scripts = this.getEntities((entity: any) => entity.entity_id.startsWith('script.'));
|
||||
return scripts;
|
||||
}
|
||||
runScript(entity_id: string, data?: Record<string, any>) {
|
||||
const serviceName = ScriptHA.serviceName;
|
||||
return this.post({
|
||||
url: `/api/services/${serviceName}/turn_on`,
|
||||
body: {
|
||||
entity_id,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { auto, showMore } from './common.ts'
|
||||
|
||||
const autos = await auto.getAutos();
|
||||
const autos = await auto.getServiceEntities();
|
||||
|
||||
console.log(showMore(autos));
|
||||
@@ -13,16 +13,10 @@ export const auto = new AutoHA({ token: process.env.HAAS_TOKEN || '', homeassist
|
||||
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 });
|
||||
|
||||
// const enti = await hacore.getLights();
|
||||
// console.log(showMore(enti), enti.length);
|
||||
const enti = await hacore.getEntities((item) => item.attributes?.friendly_name?.includes('电视'));
|
||||
console.log(showMore(enti), enti.length);
|
||||
|
||||
// const lightEntities = enti.filter((entity: any) => {
|
||||
// const hasLight = entity.entity_id.startsWith('light.');
|
||||
// const name = entity.attributes?.friendly_name || '';
|
||||
|
||||
// // return hasLight && name.includes('次卧');
|
||||
// return hasLight;
|
||||
// });
|
||||
// const lightEntities = await hacore.getInfoList();
|
||||
|
||||
// console.log(showMore(lightEntities));
|
||||
|
||||
@@ -116,7 +110,7 @@ export const script = new ScriptHA({ token: process.env.HAAS_TOKEN || '', homeas
|
||||
// }
|
||||
// ]
|
||||
|
||||
// const res = await hacore.toggleLight({
|
||||
// const res = await hacore.runService({
|
||||
// // name: '次卧灯',
|
||||
// // service: 'turn_off'
|
||||
// name: '阳台灯'
|
||||
|
||||
@@ -2,6 +2,7 @@ import { hacore, showMore } from "./common.ts";
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
const devices = await hacore.getInfoList();
|
||||
console.log(showMore(devices));
|
||||
const fuse = new Fuse(devices, {
|
||||
keys: ['name'], // 搜索字段
|
||||
threshold: 0.4, // 匹配宽松度:0~1,越小越严格
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { script, showMore } from './common.ts';
|
||||
|
||||
const scripts = await script.getScripts();
|
||||
const scripts = await script.getEntities();
|
||||
|
||||
const values = scripts.map(e => {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user