更新版本号,重构核心类,移除未使用的自动化和事件功能,优化灯光管理,调整测试用例以适应新结构
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@kevisual/ha-api",
|
"name": "@kevisual/ha-api",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"scripts": {
|
"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 {
|
export class HACore {
|
||||||
token: string;
|
token: string;
|
||||||
homeassistantURL?: string;
|
homeassistantURL?: string;
|
||||||
|
static serviceName = '';
|
||||||
constructor(options: HACoreOptions) {
|
constructor(options: HACoreOptions) {
|
||||||
this.token = options.token;
|
this.token = options?.token;
|
||||||
this.homeassistantURL = options.homeassistantURL || 'http://localhost:8123';
|
this.homeassistantURL = options?.homeassistantURL || 'http://localhost:8123';
|
||||||
}
|
}
|
||||||
async get(opts: { url: string, params?: Record<string, any> }): Promise<any> {
|
async get(opts: { url: string, params?: Record<string, any> }): Promise<any> {
|
||||||
const _u = new URL(opts.url, this.homeassistantURL);
|
const _u = new URL(opts.url, this.homeassistantURL);
|
||||||
@@ -46,14 +47,14 @@ export class HACore {
|
|||||||
return response.json();
|
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' });
|
const entities = await this.get({ url: '/api/states' });
|
||||||
if (filter) {
|
if (filter) {
|
||||||
return entities.filter(filter);
|
return entities.filter(filter);
|
||||||
}
|
}
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
async getState(entity_id?: string): Promise<any> {
|
async getState(entity_id?: string): Promise<EntityItem> {
|
||||||
return this.get({ url: `/api/states/${entity_id}` });
|
return this.get({ url: `/api/states/${entity_id}` });
|
||||||
}
|
}
|
||||||
async getEntityTypes(): Promise<any[]> {
|
async getEntityTypes(): Promise<any[]> {
|
||||||
@@ -65,9 +66,54 @@ export class HACore {
|
|||||||
});
|
});
|
||||||
return Array.from(types);
|
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 = [
|
export const entitiesTypes = [
|
||||||
{ "type": "conversation", "desc": "处理自然语言对话指令,如语音助手" },
|
{ "type": "conversation", "desc": "处理自然语言对话指令,如语音助手" },
|
||||||
@@ -98,4 +144,17 @@ export const entitiesTypes = [
|
|||||||
{ "type": "light", "desc": "控制照明设备,支持开关、亮度、颜色、色温" },
|
{ "type": "light", "desc": "控制照明设备,支持开关、亮度、颜色、色温" },
|
||||||
{ "type": "notify", "desc": "发送通知,如推送、邮件、语音播报" },
|
{ "type": "notify", "desc": "发送通知,如推送、邮件、语音播报" },
|
||||||
{ "type": "number", "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 './light.ts'
|
||||||
export * from './core.ts'
|
export * from './core.ts'
|
||||||
|
|
||||||
export * from './auto.ts'
|
|
||||||
|
|
||||||
export * from './event.ts'
|
|
||||||
|
|
||||||
export * from './script.ts'
|
|
||||||
79
src/light.ts
79
src/light.ts
@@ -1,57 +1,12 @@
|
|||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { HACore, HACoreOptions } from "./core";
|
import { HACore, HACoreOptions, InfoItem } from "./core.ts";
|
||||||
|
|
||||||
export class LightHA extends HACore {
|
export class LightHA extends HACore {
|
||||||
|
static serviceName = 'light';
|
||||||
constructor(options: HACoreOptions) {
|
constructor(options: HACoreOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
}
|
}
|
||||||
async getLights(): Promise<any[]> {
|
async searchLight(keyword: string): Promise<{ result: InfoItem[], lights: InfoItem[], id?: string, hasMore?: boolean }> {
|
||||||
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 }> {
|
|
||||||
const devices = await this.getInfoList();
|
const devices = await this.getInfoList();
|
||||||
const fuse = new Fuse(devices, {
|
const fuse = new Fuse(devices, {
|
||||||
keys: ['name'], // 搜索字段
|
keys: ['name'], // 搜索字段
|
||||||
@@ -69,17 +24,33 @@ export class LightHA extends HACore {
|
|||||||
return { result: resultItems, lights: devices, id, hasMore };
|
return { result: resultItems, lights: devices, id, hasMore };
|
||||||
}
|
}
|
||||||
async closeAllLights(): Promise<void> {
|
async closeAllLights(): Promise<void> {
|
||||||
const lights = await this.getLights();
|
const lights = await this.getServiceEntities();
|
||||||
for (const light of lights) {
|
for (const light of lights) {
|
||||||
if (light.state === 'on') {
|
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 = {
|
export class EventHA extends HACore {
|
||||||
entity_id: string;
|
static serviceName = 'event';
|
||||||
name: string;
|
constructor(options: HACoreOptions) {
|
||||||
state: string;
|
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'
|
import { auto, showMore } from './common.ts'
|
||||||
|
|
||||||
const autos = await auto.getAutos();
|
const autos = await auto.getServiceEntities();
|
||||||
|
|
||||||
console.log(showMore(autos));
|
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 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 script = new ScriptHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL });
|
||||||
|
|
||||||
// const enti = await hacore.getLights();
|
const enti = await hacore.getEntities((item) => item.attributes?.friendly_name?.includes('电视'));
|
||||||
// console.log(showMore(enti), enti.length);
|
console.log(showMore(enti), enti.length);
|
||||||
|
|
||||||
// const lightEntities = enti.filter((entity: any) => {
|
// const lightEntities = await hacore.getInfoList();
|
||||||
// const hasLight = entity.entity_id.startsWith('light.');
|
|
||||||
// const name = entity.attributes?.friendly_name || '';
|
|
||||||
|
|
||||||
// // return hasLight && name.includes('次卧');
|
|
||||||
// return hasLight;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// console.log(showMore(lightEntities));
|
// 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: '次卧灯',
|
// // name: '次卧灯',
|
||||||
// // service: 'turn_off'
|
// // service: 'turn_off'
|
||||||
// name: '阳台灯'
|
// name: '阳台灯'
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { hacore, showMore } from "./common.ts";
|
|||||||
import Fuse from 'fuse.js';
|
import Fuse from 'fuse.js';
|
||||||
|
|
||||||
const devices = await hacore.getInfoList();
|
const devices = await hacore.getInfoList();
|
||||||
|
console.log(showMore(devices));
|
||||||
const fuse = new Fuse(devices, {
|
const fuse = new Fuse(devices, {
|
||||||
keys: ['name'], // 搜索字段
|
keys: ['name'], // 搜索字段
|
||||||
threshold: 0.4, // 匹配宽松度:0~1,越小越严格
|
threshold: 0.4, // 匹配宽松度:0~1,越小越严格
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { script, showMore } from './common.ts';
|
import { script, showMore } from './common.ts';
|
||||||
|
|
||||||
const scripts = await script.getScripts();
|
const scripts = await script.getEntities();
|
||||||
|
|
||||||
const values = scripts.map(e => {
|
const values = scripts.map(e => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user