Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bridged device basic information #361

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/backend/src/home-assistant/api/get-registry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { HomeAssistantEntityRegistry } from "@home-assistant-matter-hub/common";
import {
HomeAssistantEntityRegistry,
HomeAssistantDeviceRegistry,
} from "@home-assistant-matter-hub/common";
import { Connection } from "home-assistant-js-websocket";

export async function getRegistry(
Expand All @@ -8,3 +11,11 @@ export async function getRegistry(
type: "config/entity_registry/list",
});
}

export async function getDeviceRegistry(
connection: Connection,
): Promise<HomeAssistantDeviceRegistry[]> {
return connection.sendMessagePromise<HomeAssistantDeviceRegistry[]>({
type: "config/device_registry/list",
});
}
15 changes: 14 additions & 1 deletion packages/backend/src/home-assistant/home-assistant-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
HomeAssistantEntityInformation,
HomeAssistantEntityState,
} from "@home-assistant-matter-hub/common";
import { getRegistry } from "./api/get-registry.js";
import { getRegistry, getDeviceRegistry } from "./api/get-registry.js";
import { HomeAssistantClient } from "./home-assistant-client.js";
import { getStates } from "home-assistant-js-websocket";
import _, { Dictionary } from "lodash";
Expand All @@ -24,16 +24,29 @@ export class HomeAssistantRegistry {
await getRegistry(client.connection),
(r) => r.entity_id,
);
const deviceRegistry = _.keyBy(
await getDeviceRegistry(client.connection),
(r) => r.id,
);
const entityStates = _.keyBy(
await getStates(client.connection),
(s) => s.entity_id,
);
const entityIds = _.uniq(
_.keys(entityRegistry).concat(_.keys(entityStates)),
);

const deviceByEntityId = _.fromPairs(
entityIds
.map((entityId) => [entityId, entityRegistry[entityId]?.device_id])
.filter(([, deviceId]) => !!deviceId)
.map(([entityId, deviceId]) => [entityId, deviceRegistry[deviceId!]]),
);

return entityIds.map((entity_id) => ({
entity_id,
registry: entityRegistry[entity_id],
deviceRegistry: deviceByEntityId[entity_id],
state: entityStates[entity_id],
}));
}
Expand Down
34 changes: 31 additions & 3 deletions packages/backend/src/matter/behaviors/basic-information-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import { applyPatchState } from "../../utils/apply-patch-state.js";
import { BridgeDataProvider } from "../bridge/bridge-data-provider.js";
import { VendorId } from "@matter/main";

const ellipsize = (
t0bst4r marked this conversation as resolved.
Show resolved Hide resolved
str: string | undefined,
len: number,
): string | undefined => {
if (!str || str.length <= len) {
return str;
}
return str.slice(0, len - 3) + "...";
};

export class BasicInformationServer extends Base {
override async initialize(): Promise<void> {
await super.initialize();
Expand All @@ -16,18 +26,36 @@ export class BasicInformationServer extends Base {

private update(entity: HomeAssistantEntityInformation) {
const { basicInformation } = this.env.get(BridgeDataProvider);
const device = entity.deviceRegistry;
applyPatchState(this.state, {
// The correct vendor name is in `device?.manufacturer`, but most
// controllers are currently unable to show this for bridged devices, and
// as Alexa Media Player relies on a check for a t0bst4r vendor to avoid
// loops, we keep it like this for now.
// See: https://github.com/alandtse/alexa_media_player/pull/2730
vendorId: VendorId(basicInformation.vendorId),
vendorName: maxLengthOrHash(basicInformation.vendorName, 32),
productName: maxLengthOrHash(basicInformation.productName, 32),
productLabel: maxLengthOrHash(basicInformation.productLabel, 64),
productName:
(device?.model_id
? ellipsize(device?.model_id, 32)
: ellipsize(device?.model, 32)) ??
maxLengthOrHash(basicInformation.productName, 32),
productLabel:
ellipsize(device?.model, 64) ??
maxLengthOrHash(basicInformation.productLabel, 64),
hardwareVersion: basicInformation.hardwareVersion,
softwareVersion: basicInformation.softwareVersion,
nodeLabel: maxLengthOrHash(
hardwareVersionString: ellipsize(device?.hw_version, 64) ?? undefined,
softwareVersionString: ellipsize(device?.sw_version, 64) ?? undefined,
nodeLabel: ellipsize(
entity.state.attributes.friendly_name ?? entity.entity_id,
32,
),
reachable: entity.state.state !== "unavailable",

// The device serial number is available in `device?.serial_number`, but
// we're keeping it as the entity ID for now to avoid breaking existing
// deployments.
serialNumber: maxLengthOrHash(entity.entity_id, 32),
});
}
Expand Down
22 changes: 22 additions & 0 deletions packages/common/src/home-assistant-device-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface HomeAssistantDeviceRegistry {
area_id?: unknown;
config_entries?: unknown;
configuration_url?: unknown;
connections?: unknown;
default_manufacturer?: string;
default_model?: string;
default_name?: string;
entry_type?: unknown;
hw_version?: string;
id?: string;
identifiers?: unknown;
name?: string;
name_by_user?: string;
manufacturer?: string;
model?: string;
model_id?: string;
serial_number?: string;
suggested_area?: unknown;
sw_version?: string;
via_device?: unknown;
}
2 changes: 2 additions & 0 deletions packages/common/src/home-assistant-entity-information.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { HomeAssistantEntityState } from "./home-assistant-entity-state.js";
import { HomeAssistantEntityRegistry } from "./home-assistant-entity-registry.js";
import { HomeAssistantDeviceRegistry } from "./home-assistant-device-registry.js";

export interface HomeAssistantEntityInformation {
entity_id: string;
registry?: HomeAssistantEntityRegistry;
deviceRegistry?: HomeAssistantDeviceRegistry;
t0bst4r marked this conversation as resolved.
Show resolved Hide resolved
state: HomeAssistantEntityState;
}
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./bridge-data.js";
export * from "./home-assistant-filter.js";
export * from "./home-assistant-device-registry.js";
export * from "./home-assistant-entity-registry.js";
export * from "./home-assistant-entity-state.js";
export * from "./home-assistant-entity-information.js";
Expand Down
Loading