update
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
|
||||||
|
.env
|
||||||
33
package-lock.json
generated
Normal file
33
package-lock.json
generated
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "ha-api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "ha-api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.10.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||||
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "ha-api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.24.0",
|
||||||
|
"type": "module",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "^1.3.3",
|
||||||
|
"@types/node": "^24.10.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^17.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
56
pnpm-lock.yaml
generated
Normal file
56
pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
dotenv:
|
||||||
|
specifier: ^17.2.3
|
||||||
|
version: 17.2.3
|
||||||
|
devDependencies:
|
||||||
|
'@types/bun':
|
||||||
|
specifier: ^1.3.3
|
||||||
|
version: 1.3.3
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^24.10.1
|
||||||
|
version: 24.10.1
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@types/bun@1.3.3':
|
||||||
|
resolution: {integrity: sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g==}
|
||||||
|
|
||||||
|
'@types/node@24.10.1':
|
||||||
|
resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==}
|
||||||
|
|
||||||
|
bun-types@1.3.3:
|
||||||
|
resolution: {integrity: sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ==}
|
||||||
|
|
||||||
|
dotenv@17.2.3:
|
||||||
|
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
undici-types@7.16.0:
|
||||||
|
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@types/bun@1.3.3':
|
||||||
|
dependencies:
|
||||||
|
bun-types: 1.3.3
|
||||||
|
|
||||||
|
'@types/node@24.10.1':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 7.16.0
|
||||||
|
|
||||||
|
bun-types@1.3.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 24.10.1
|
||||||
|
|
||||||
|
dotenv@17.2.3: {}
|
||||||
|
|
||||||
|
undici-types@7.16.0: {}
|
||||||
61
src/core.ts
Normal file
61
src/core.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
const homeassistantURL = 'http://home.mz.zxj.im:8123';
|
||||||
|
|
||||||
|
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmMzk3ZDUwMWU3ODE0MTA4Yjk4ZjYwNDFjMzI3NzVkZSIsImlhdCI6MTc2NDg2NDUyMywiZXhwIjoyMDgwMjI0NTIzfQ.S2zO3DNzeVYgd1c_N9IkRc13zmtj2HGVq-n6IUmttRQ'
|
||||||
|
|
||||||
|
type HACoreOptions = {
|
||||||
|
token: string;
|
||||||
|
homeassistantURL?: string;
|
||||||
|
}
|
||||||
|
export class HACore {
|
||||||
|
token: string;
|
||||||
|
homeassistantURL?: string;
|
||||||
|
constructor(options: HACoreOptions) {
|
||||||
|
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);
|
||||||
|
if (opts.params) {
|
||||||
|
Object.entries(opts.params).forEach(([key, value]) => {
|
||||||
|
_u.searchParams.append(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return fetch(_u.toString(), {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${this.token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}).then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch ${opts.url}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async post(opts: { url: string, body?: any }): Promise<any> {
|
||||||
|
return fetch(new URL(opts.url, this.homeassistantURL).toString(), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${this.token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(opts.body || {}),
|
||||||
|
}).then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to post to ${opts.url}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getEntities = async (): Promise<any[]> => {
|
||||||
|
const hacore = new HACore({ token, homeassistantURL });
|
||||||
|
return hacore.get({ url: '/api/states' });
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLights = async (): Promise<any[]> => {
|
||||||
|
const hacore = new HACore({ token, homeassistantURL });
|
||||||
|
const entities = await hacore.get({ url: '/api/states' });
|
||||||
|
return entities.filter((entity: any) => entity.entity_id.startsWith('light.'));
|
||||||
|
}
|
||||||
113
test/common.ts
Normal file
113
test/common.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import { HACore } from "../src/core";
|
||||||
|
|
||||||
|
import util from 'node:util';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
|
||||||
|
const hacore = new HACore({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL });
|
||||||
|
const enti = await hacore.get({ url: '/api/states' });
|
||||||
|
// console.log(util.inspect(enti, { depth: null }));
|
||||||
|
|
||||||
|
// const lightEntities = enti.filter((entity: any) => {
|
||||||
|
// const hasLight = entity.entity_id.startsWith('light.');
|
||||||
|
// const name = entity.attributes?.friendly_name || '';
|
||||||
|
|
||||||
|
// return hasLight && name.includes('次卧');
|
||||||
|
// });
|
||||||
|
// console.log(util.inspect(lightEntities, { depth: null }));
|
||||||
|
|
||||||
|
const lights = [
|
||||||
|
{
|
||||||
|
entity_id: 'light.lemesh_wy0c14_f18d_light',
|
||||||
|
state: 'off',
|
||||||
|
attributes: {
|
||||||
|
min_color_temp_kelvin: 2700,
|
||||||
|
max_color_temp_kelvin: 6500,
|
||||||
|
min_mireds: 153,
|
||||||
|
max_mireds: 370,
|
||||||
|
effect_list: [
|
||||||
|
'WY', 'Day',
|
||||||
|
'Night', 'Warmth',
|
||||||
|
'Tv', 'Reading',
|
||||||
|
'Computer', 'Hospitality',
|
||||||
|
'Entertainment', 'Wakeup',
|
||||||
|
'Dusk', 'Sleep'
|
||||||
|
],
|
||||||
|
supported_color_modes: ['color_temp'],
|
||||||
|
effect: null,
|
||||||
|
color_mode: null,
|
||||||
|
brightness: null,
|
||||||
|
color_temp_kelvin: null,
|
||||||
|
color_temp: null,
|
||||||
|
hs_color: null,
|
||||||
|
rgb_color: null,
|
||||||
|
xy_color: null,
|
||||||
|
'light.mode': 0,
|
||||||
|
'light.on': false,
|
||||||
|
'light.color_temperature': 6500,
|
||||||
|
'light.brightness': 1,
|
||||||
|
friendly_name: '次卧灯 灯光',
|
||||||
|
supported_features: 4
|
||||||
|
},
|
||||||
|
last_changed: '2025-12-02T16:35:00.229216+00:00',
|
||||||
|
last_reported: '2025-12-02T16:36:00.314622+00:00',
|
||||||
|
last_updated: '2025-12-02T16:35:00.229216+00:00',
|
||||||
|
context: {
|
||||||
|
id: '01KBFYNN05ZYWZHTGC1W7N66Z5',
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entity_id: 'light.lemesh_cn_1099991426_wy0c14_s_2_light',
|
||||||
|
state: 'off',
|
||||||
|
attributes: {
|
||||||
|
min_color_temp_kelvin: 2700,
|
||||||
|
max_color_temp_kelvin: 6500,
|
||||||
|
min_mireds: 153,
|
||||||
|
max_mireds: 370,
|
||||||
|
effect_list: [
|
||||||
|
'色温模式',
|
||||||
|
'日光',
|
||||||
|
'月光(夜间)模式',
|
||||||
|
'温馨',
|
||||||
|
'电视模式(影院模式)',
|
||||||
|
'阅读模式',
|
||||||
|
'电脑模式',
|
||||||
|
'会客模式',
|
||||||
|
'娱乐模式',
|
||||||
|
'清晨唤醒',
|
||||||
|
'黄昏明亮',
|
||||||
|
'夜晚助眠'
|
||||||
|
],
|
||||||
|
supported_color_modes: ['color_temp'],
|
||||||
|
effect: null,
|
||||||
|
color_mode: null,
|
||||||
|
brightness: null,
|
||||||
|
color_temp_kelvin: null,
|
||||||
|
color_temp: null,
|
||||||
|
hs_color: null,
|
||||||
|
rgb_color: null,
|
||||||
|
xy_color: null,
|
||||||
|
friendly_name: '次卧灯 灯光',
|
||||||
|
supported_features: 4
|
||||||
|
},
|
||||||
|
last_changed: '2025-12-04T13:28:05.284635+00:00',
|
||||||
|
last_reported: '2025-12-04T13:28:09.331178+00:00',
|
||||||
|
last_updated: '2025-12-04T13:28:05.284635+00:00',
|
||||||
|
context: {
|
||||||
|
id: '01KBMRRTX4ZE10MGHTVVNC1K7Y',
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const res = await hacore.post({
|
||||||
|
url: '/api/services/light/turn_off',
|
||||||
|
body: {
|
||||||
|
entity_id: 'light.lemesh_cn_1099991426_wy0c14_s_2_light' // 或第二个次卧灯的ID
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(util.inspect(res, { depth: null }));
|
||||||
Reference in New Issue
Block a user