diff --git a/package.json b/package.json index 3f2cc1b..c34dff0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/ha-api", - "version": "0.0.3", + "version": "0.0.4", "description": "", "main": "src/index.ts", "scripts": { @@ -15,14 +15,16 @@ }, "author": "abearxiong (https://www.xiongxiao.me)", "license": "MIT", - "packageManager": "pnpm@10.24.0", + "packageManager": "pnpm@10.26.2", "type": "module", "devDependencies": { - "@types/bun": "^1.3.3", - "dotenv": "^17.2.3", - "@types/node": "^24.10.1" + "@types/bun": "^1.3.5", + "@types/node": "^25.0.3", + "dotenv": "^17.2.3" }, "dependencies": { - "fuse.js": "^7.1.0" + "@kevisual/cache": "^0.0.4", + "fuse.js": "^7.1.0", + "lru-cache": "^11.2.4" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d098af..3b2cc7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,30 +8,39 @@ importers: .: dependencies: - dotenv: - specifier: ^17.2.3 - version: 17.2.3 + '@kevisual/cache': + specifier: ^0.0.4 + version: 0.0.4 fuse.js: specifier: ^7.1.0 version: 7.1.0 + lru-cache: + specifier: ^11.2.4 + version: 11.2.4 devDependencies: '@types/bun': - specifier: ^1.3.3 - version: 1.3.3 + specifier: ^1.3.5 + version: 1.3.5 '@types/node': - specifier: ^24.10.1 - version: 24.10.1 + specifier: ^25.0.3 + version: 25.0.3 + dotenv: + specifier: ^17.2.3 + version: 17.2.3 packages: - '@types/bun@1.3.3': - resolution: {integrity: sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g==} + '@kevisual/cache@0.0.4': + resolution: {integrity: sha512-NlyriJ9fC27TgQhWYbEH9hG84R2k0lIofOxo/+nVHN6a6LJSLnVbpDIysRcnH8MI52n/XHfWwLSjeDDL3D1/cQ==} - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/bun@1.3.5': + resolution: {integrity: sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w==} - bun-types@1.3.3: - resolution: {integrity: sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ==} + '@types/node@25.0.3': + resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} + + bun-types@1.3.5: + resolution: {integrity: sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw==} dotenv@17.2.3: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} @@ -41,25 +50,49 @@ packages: resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} engines: {node: '>=10'} + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + + nanoid@5.1.6: + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + engines: {node: ^18 || >=20} + hasBin: true + undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} snapshots: - '@types/bun@1.3.3': + '@kevisual/cache@0.0.4': dependencies: - bun-types: 1.3.3 + idb-keyval: 6.2.2 + lru-cache: 11.2.4 + nanoid: 5.1.6 - '@types/node@24.10.1': + '@types/bun@1.3.5': + dependencies: + bun-types: 1.3.5 + + '@types/node@25.0.3': dependencies: undici-types: 7.16.0 - bun-types@1.3.3: + bun-types@1.3.5: dependencies: - '@types/node': 24.10.1 + '@types/node': 25.0.3 dotenv@17.2.3: {} fuse.js@7.1.0: {} + idb-keyval@6.2.2: {} + + lru-cache@11.2.4: {} + + nanoid@5.1.6: {} + undici-types@7.16.0: {} diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 0000000..554af9b --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,47 @@ + +import { LRUCache } from 'lru-cache'; +interface StorageItem { + getItem(name: string): any | null; + setItem(name: string, value: any): void; + removeItem(name: string): void; +} +export class LRUStorage implements StorageItem { + cache: LRUCache; + constructor(options?: LRUCache.Options) { + this.cache = new LRUCache(options || { max: 500 }); + } + getItem(name: string): any | null { + const value = this.cache.get(name); + return value || null; + } + setItem(name: string, value: any): void { + this.cache.set(name, value); + } + removeItem(name: string): void { + this.cache.delete(name); + } +} +export class SessionStorage { + storage: StorageItem; + constructor(opts?: { storage?: StorageItem }) { + const isBrowser = typeof window !== 'undefined' && typeof window.sessionStorage !== 'undefined'; + if (opts?.storage) { + this.storage = opts.storage; + } else { + this.storage = isBrowser ? window.sessionStorage : new LRUStorage(); + } + } + async getItem(name: string): Promise { + const data = this.storage.getItem(name); + return data + } + async setItem(name: string, data: any) { + if (typeof data === 'string') { + this.storage.setItem(name, data); + return; + } + this.storage.setItem(name, JSON.stringify(data)); + } +} + +export const sessionStorage = new SessionStorage(); \ No newline at end of file diff --git a/src/core.ts b/src/core.ts index 012cee5..d5eb832 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,3 +1,5 @@ +import { LRUStorage } from './cache.ts'; + export type HACoreOptions = { token: string; homeassistantURL?: string; @@ -9,6 +11,7 @@ export class HACore { token: string; homeassistantURL?: string; static serviceName = ''; + cache = new LRUStorage({ max: 200, ttl: 1000 * 60 * 5 }); // 5åˆ†é’Ÿįž“å­˜ constructor(options: HACoreOptions) { this.token = options?.token; this.homeassistantURL = options?.homeassistantURL || 'http://localhost:8123'; @@ -48,7 +51,12 @@ export class HACore { }); } async getEntities(filter?: (entity: EntityItem) => boolean | undefined): Promise { - const entities = await this.get({ url: '/api/states' }); + let cacheEntities = this.cache.getItem('all_entities'); + let entities: EntityItem[] = cacheEntities; + if (!entities) { + entities = await this.get({ url: '/api/states' }); + this.cache.setItem('all_entities', entities); + } if (filter) { return entities.filter(filter); } diff --git a/src/light.ts b/src/light.ts index 04ba225..b88b8af 100644 --- a/src/light.ts +++ b/src/light.ts @@ -1,6 +1,5 @@ import Fuse from "fuse.js"; import { HACore, HACoreOptions, InfoItem } from "./core.ts"; - export class LightHA extends HACore { static serviceName = 'light'; constructor(options: HACoreOptions) {