Skip to content

Commit

Permalink
refactor: use custom homeAssistantBehavior for better state management
Browse files Browse the repository at this point in the history
  • Loading branch information
t0bst4r committed Nov 5, 2024
1 parent f88add6 commit 6520413
Show file tree
Hide file tree
Showing 63 changed files with 794 additions and 1,120 deletions.
3 changes: 1 addition & 2 deletions packages/backend/.env.sample → .env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
NODE_ENV=development
HAMH_HOME_ASSISTANT_URL="http://192.168.178.111:8123/"
HAMH_HOME_ASSISTANT_ACCESS_TOKEN=""
HAMH_STORAGE_LOCATION=$PWD/.local-storage
HAMH_LOG_LEVEL=info
HAMH_LOG_LEVEL=debug
1 change: 1 addition & 0 deletions apps/docker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"type": "module",
"scripts": {
"cleanup": "rimraf package.tgz",
"build": "./build.sh",
"retrieve-package": "node retrieve-package.js",
"nx-release-publish": "./build.sh --latest --push --all-platforms"
Expand Down
3 changes: 0 additions & 3 deletions apps/home-assistant-matter-hub/.env.sample

This file was deleted.

7 changes: 4 additions & 3 deletions apps/home-assistant-matter-hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,17 @@
"license": "Apache-2.0",
"repository": "github:t0bst4r/home-assistant-matter-hub",
"scripts": {
"cleanup": "rimraf dist pack",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"build": "node build.js",
"test": "vitest run",
"start": "dotenvx run -- ./dist/backend/cli.js start --log-level=info",
"start": "dotenvx run -f ../../.env -- ./dist/backend/cli.js start",
"pack": "mkdir -p pack && npm pack --pack-destination pack --json | jq -r .[0].filename > pack/package-name.txt"
},
"dependencies": {
"@project-chip/matter.js": "~0.10.6",
"@project-chip/matter-node.js": "~0.10.6",
"@project-chip/matter.js": "~0.11.2",
"@project-chip/matter-node.js": "~0.11.2",
"ajv": "^8.17.1",
"chalk": "^5.3.0",
"color": "^4.2.3",
Expand Down
101 changes: 86 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"type": "module",
"scripts": {
"cleanup": "nx run-many -t cleanup --parallel 100 && nx reset && git clean -fx",
"lint": "nx run-many -t lint && prettier . --check",
"lint:fix": "nx run-many -t lint:fix && prettier . --write",
"test": "nx run-many -t test",
Expand Down
1 change: 1 addition & 0 deletions packages/backend/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NODE_ENV=development
7 changes: 4 additions & 3 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
"type": "module",
"destination": "./dist",
"scripts": {
"cleanup": "rimraf dist",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"build": "tsc && node bundle.js",
"serve": "tsx watch --env-file=.env --clear-screen=false src/cli.ts start"
"serve": "tsx watch --env-file=../../.env --env-file=.env.development --clear-screen=false src/cli.ts start"
},
"dependencies": {
"@home-assistant-matter-hub/common": "*",
"@project-chip/matter.js": "~0.10.6",
"@project-chip/matter-node.js": "~0.10.6",
"@project-chip/matter.js": "~0.11.2",
"@project-chip/matter-node.js": "~0.11.2",
"ajv": "^8.17.1",
"chalk": "^5.3.0",
"express": "^4.21.1",
Expand Down
28 changes: 6 additions & 22 deletions packages/backend/src/home-assistant/api/get-registry.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
import {
HomeAssistantEntityRegistry,
HomeAssistantEntityRegistryWithInitialState,
HomeAssistantEntityState,
} from "@home-assistant-matter-hub/common";
import { Connection, getStates } from "home-assistant-js-websocket";
import _, { Dictionary } from "lodash";
import { HomeAssistantEntityRegistry } from "@home-assistant-matter-hub/common";
import { Connection } from "home-assistant-js-websocket";

export async function getRegistry(
connection: Connection,
): Promise<Dictionary<HomeAssistantEntityRegistryWithInitialState>> {
const registry = _.keyBy(
await connection.sendMessagePromise<HomeAssistantEntityRegistry[]>({
type: "config/entity_registry/list",
}),
(r) => r.entity_id,
);
const states: Dictionary<HomeAssistantEntityState> = _.keyBy(
await getStates(connection),
(e) => e.entity_id,
);
return _.mapValues(registry, (r) => ({
...r,
initialState: states[r.entity_id],
}));
): Promise<HomeAssistantEntityRegistry[]> {
return connection.sendMessagePromise<HomeAssistantEntityRegistry[]>({
type: "config/entity_registry/list",
});
}
63 changes: 27 additions & 36 deletions packages/backend/src/home-assistant/home-assistant-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {
Connection,
createConnection,
createLongLivedTokenAuth,
getStates,
UnsubscribeFunc,
} from "home-assistant-js-websocket";
import {
HomeAssistantEntityRegistryWithInitialState,
HomeAssistantEntityRegistry,
HomeAssistantEntityState,
HomeAssistantFilter,
} from "@home-assistant-matter-hub/common";
Expand All @@ -15,11 +16,12 @@ import { ServiceBase } from "../utils/service.js";
import _, { Dictionary } from "lodash";
import { Subject } from "rxjs";
import crypto from "node:crypto";
import { subscribeEntities } from "./subscribe-entities.js";
import { subscribeEntities } from "./api/subscribe-entities.js";
import { matchEntityFilter } from "./match-entity-filter.js";
import { getRegistry } from "./api/get-registry.js";
import { HomeAssistantActions } from "./home-assistant-actions.js";
import { HassServiceTarget } from "home-assistant-js-websocket/dist/types.js";
import { isValidEntity } from "./is-valid-entity.js";

export interface HomeAssistantClientProps {
readonly url: string;
Expand All @@ -38,7 +40,9 @@ export class HomeAssistantClient
Dictionary<HomeAssistantEntityState>
>();
private connection?: Connection;
private _registry?: Dictionary<HomeAssistantEntityRegistryWithInitialState>;

private _initialStates?: Dictionary<HomeAssistantEntityState>;
private _registry?: Dictionary<HomeAssistantEntityRegistry>;

private subscriptions: Record<string, HomeAssistantFilter> = {};
private unsubscribeState?: UnsubscribeFunc;
Expand All @@ -55,37 +59,14 @@ export class HomeAssistantClient
this.connection = await createConnection({
auth: createLongLivedTokenAuth(this.url, this.accessToken),
});
const registry = await getRegistry(this.connection);
this._registry = _.fromPairs(
_.toPairs(registry)
.filter(([, item]) => {
const isHidden = item.hidden_by != undefined;
const isDisabled = item.disabled_by != undefined;
if (isHidden) {
this.log.debug(
"%s is hidden by %s",
item.entity_id,
item.hidden_by,
);
}
if (isDisabled) {
this.log.debug(
"%s is disabled by %s",
item.entity_id,
item.disabled_by,
);
}
return !isHidden && !isDisabled;
})
.filter(([, item]) => {
const hasState = !!item.initialState;
if (!hasState) {
this.log.warn("%s does not have an initial-state", item.entity_id);
}
return hasState;
}),
this._initialStates = _.keyBy(
await getStates(this.connection),
(e) => e.entity_id,
);
this._registry = _.keyBy(
await getRegistry(this.connection),
(r) => r.entity_id,
);

this.subscriptions = {};
}

Expand All @@ -98,18 +79,28 @@ export class HomeAssistantClient
this.connection = undefined;

this._registry = undefined;
this._initialStates = undefined;
}

registry(
filter: HomeAssistantFilter,
): HomeAssistantEntityRegistryWithInitialState[] {
): Dictionary<HomeAssistantEntityRegistry> {
if (!this._registry) {
throw new Error("Home Assistant Client is not yet initialized");
}
return _.filter(this._registry, (r) => matchEntityFilter(r, filter));
return _.pickBy(
this._registry,
(r) => isValidEntity(r) && matchEntityFilter(r, filter),
);
}

initialStates(entityIds: string[]): Dictionary<HomeAssistantEntityState> {
return _.pickBy(this._initialStates, (e) =>
entityIds.includes(e.entity_id),
);
}

states(
subscribeStates(
filter: HomeAssistantFilter,
cb: (entities: Dictionary<HomeAssistantEntityState>) => Promise<void>,
): () => void {
Expand Down
10 changes: 10 additions & 0 deletions packages/backend/src/home-assistant/is-valid-entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { HomeAssistantEntityRegistry } from "@home-assistant-matter-hub/common";

export function isValidEntity(registry: HomeAssistantEntityRegistry): boolean {
if (registry.disabled_by != null) {
return false;
} else if (registry.hidden_by != null) {
return false;
}
return true;
}
Loading

0 comments on commit 6520413

Please sign in to comment.