Skip to content

Commit

Permalink
Implement missing features and models in javascript client (#803)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt authored Jul 16, 2024
1 parent 7573567 commit 8776a4d
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 24 deletions.
99 changes: 77 additions & 22 deletions dashboard/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import { Connection } from "./connection";
import { InvalidServerVersion } from "./exceptions";
import {
APICommands,
CommissionableNodeData,
CommissioningParameters,
ErrorResultMessage,
EventMessage,
MatterFabricData,
MatterSoftwareVersion,
NodePingResult,
SuccessResultMessage,
} from "./models/model";

Expand Down Expand Up @@ -40,60 +45,104 @@ export class MatterClient {
};
}

async commissionWithCode(code: string, networkOnly: boolean) {
console.log("TODO");
}

async commissionOnNetwork(setup_pin_code: number, ip_addr: string) {
console.log("TODO");
async commissionWithCode(code: string, networkOnly: boolean): Promise<MatterNode> {
// Commission a device using a QR Code or Manual Pairing Code.
// code: The QR Code or Manual Pairing Code for device commissioning.
// network_only: If True, restricts device discovery to network only.
// Returns: The NodeInfo of the commissioned device.
return await this.sendCommand("commission_with_code", 0, { code: code, network_only: networkOnly }) as MatterNode;
}

async setWifiCredentials(ssid: string, credentials: string) {
console.log("TODO");
// Set WiFi credentials for commissioning to a (new) device.
await this.sendCommand("set_wifi_credentials", 0, { ssid, credentials })
}

async setThreadOperationalDataset(dataset: string) {
console.log("TODO");
// Set Thread Operational dataset in the stack.
await this.sendCommand("set_thread_dataset", 0, { dataset })
}

async openCommissioningWindow(
nodeId: number,
timeout = 300,
iteration = 1000,
option = 1,
distriminator: number | undefined = undefined
) {
console.log("TODO");
timeout?: number,
iteration?: number,
option?: number,
distriminator?: number
): Promise<CommissioningParameters> {
// Open a commissioning window to commission a device present on this controller to another.
// Returns code to use as discriminator.
return await this.sendCommand("open_commissioning_window", 0, { node_id: nodeId, timeout, iteration, option, distriminator }) as CommissioningParameters;
}

async discoverCommissionableNodes(): Promise<CommissionableNodeData[]> {
// Discover Commissionable Nodes (discovered on BLE or mDNS).
return await this.sendCommand("discover_commissionable_nodes", 0, {}) as CommissionableNodeData[];
}

async discoverCommissionableNodes() {
console.log("TODO");
async getMatterFabrics(nodeId: number): Promise<MatterFabricData[]> {
// Get Matter fabrics from a device.
// Returns a list of MatterFabricData objects.
return await this.sendCommand("get_matter_fabrics", 3, {}) as MatterFabricData[];
}

async getMatterFabrics(nodeId: number) {
console.log("TODO");
async removeMatterFabric(nodeId: number, fabricIndex: number) {
// Remove a Matter fabric from a device.
await this.sendCommand("remove_matter_fabric", 3, { node_id: nodeId, fabric_index: fabricIndex });
}

async removeMatterFabric(nodeId: number, fabricId: number) {
console.log("TODO");
async pingNode(nodeId: number): Promise<NodePingResult> {
// Ping node on the currently known IP-address(es).
return await this.sendCommand("ping_node", 0, { node_id: nodeId }) as NodePingResult;
}

async pingNode(nodeId: number) {
await this.sendCommand("ping_node", 0, { node_id: nodeId });
async getNodeIPAddresses(nodeId: number, preferCache?: boolean, scoped?: boolean): Promise<string[]> {
// Return the currently known (scoped) IP-address(es).
return await this.sendCommand("get_node_ip_addresses", 8, { node_id: nodeId, prefer_cache: preferCache, scoped: scoped }) as string[];
}

async removeNode(nodeId: number) {
// Remove a Matter node/device from the fabric.
await this.sendCommand("remove_node", 0, { node_id: nodeId });
}

async interviewNode(nodeId: number) {
// Interview a node.
await this.sendCommand("interview_node", 0, { node_id: nodeId });
}

async importTestNode(dump: string) {
// Import test node(s) from a HA or Matter server diagnostics dump.
await this.sendCommand("import_test_node", 0, { dump });
}

async readAttribute(nodeId: number, attributePath: string | string[]): Promise<Record<string, any>> {
// Read one or more attribute(s) on a node by specifying an attributepath.
return await this.sendCommand("read_attribute", 0, { node_id: nodeId, attribute_path: attributePath });
}

async writeAttribute(nodeId: number, attributePath: string, value: any) {
// Write an attribute(value) on a target node.
await this.sendCommand("write_attribute", 0, { node_id: nodeId, attribute_path: attributePath, value: value });
}

async checkNodeUpdate(nodeId: number): Promise<MatterSoftwareVersion | null> {
// Check if there is an update for a particular node.
// Reads the current software version and checks the DCL if there is an update
// available. If there is an update available, the command returns the version
// information of the latest update available.
return await this.sendCommand("check_node_update", 10, { node_id: nodeId });
}

async updateNode(nodeId: number, softwareVersion: number | string) {
// Update a node to a new software version.
// This command checks if the requested software version is indeed still available
// and if so, it will start the update process. The update process will be handled
// by the built-in OTA provider. The OTA provider will download the update and
// notify the node about the new update.
await this.sendCommand("update_node", 10, { node_id: nodeId, software_version: softwareVersion });
}

async sendCommand<T extends keyof APICommands>(
command: T,
require_schema: number | undefined = undefined,
Expand Down Expand Up @@ -218,6 +267,12 @@ export class MatterClient {
this.fireEvent("nodes_changed");
return;
}

if (event.event === "server_info_updated") {
this.connection.serverInfo = event.data;
this.fireEvent("server_info_updated");
return;
}
}

private fireEvent(event: string, data?: any) {
Expand Down
87 changes: 85 additions & 2 deletions dashboard/src/client/models/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,33 @@ export interface APICommands {
response: {};
};
import_test_node: {
requestArgs: { dump: string; };
requestArgs: {};
response: null;
};
get_node_ip_addresses: {
requestArgs: {};
response: {};
};
check_node_update: {
requestArgs: {};
response: MatterSoftwareVersion | null;
};
update_node: {
requestArgs: {};
response: MatterSoftwareVersion | null;
};
discover_commissionable_nodes: {
requestArgs: {};
response: {};
};
get_matter_fabrics: {
requestArgs: {};
response: {};
};
remove_matter_fabric: {
requestArgs: {};
response: {};
};
}

export interface CommandMessage {
Expand All @@ -97,6 +121,7 @@ export interface ServerInfoMessage {
sdk_version: string;
wifi_credentials_set: boolean;
thread_credentials_set: boolean;
bluetooth_enabled: boolean;
}

interface ServerEventNodeAdded {
Expand Down Expand Up @@ -131,8 +156,12 @@ interface ServerEventEndpointRemoved {
event: "endpoint_removed";
data: {};
}
interface ServerEvenInfoUpdated {
event: "server_info_updated";
data: ServerInfoMessage;
}

export type EventMessage = ServerEventNodeAdded | ServerEventNodeUpdated | ServerEventNodeRemoved | ServerEventNodeEvent | ServerEventAttributeUpdated | ServerEventServerShutdown | ServerEventEndpointAdded | ServerEventEndpointRemoved
export type EventMessage = ServerEventNodeAdded | ServerEventNodeUpdated | ServerEventNodeRemoved | ServerEventNodeEvent | ServerEventAttributeUpdated | ServerEventServerShutdown | ServerEventEndpointAdded | ServerEventEndpointRemoved | ServerEvenInfoUpdated


export interface ResultMessageBase {
Expand All @@ -155,4 +184,58 @@ export interface WebSocketConfig {
path: string;
}

export enum UpdateSource {
MAIN_NET_DCL = "main-net-dcl",
TEST_NET_DCL = "test-net-dcl",
LOCAL = "local"
}

export interface MatterSoftwareVersion {
vid: number
pid: number
software_version: number
software_version_string: string
firmware_information?: string
min_applicable_software_version: number
max_applicable_software_version: number
release_notes_url?: string
update_source: UpdateSource
}

export interface CommissioningParameters {
setup_pin_code: number
setup_manual_code: string
setup_qr_code: string
}

export interface CommissionableNodeData {
instance_name?: string
host_name?: string
port?: number
long_discriminator?: number
vendor_id?: number
product_id?: number
commissioning_mode?: number
device_type?: number
device_name?: string
pairing_instruction?: string
pairing_hint?: number
mrp_retry_interval_idle?: number
mrp_retry_interval_active?: number
supports_tcp?: boolean
addresses?: string[]
rotating_id?: string
}

export interface MatterFabricData {
fabric_id?: number
vendor_id?: number
fabric_index?: number
fabric_label?: string
vendor_name?: string
}



export type NotificationType = "success" | "info" | "warning" | "error";
export type NodePingResult = Record<string, boolean>;
1 change: 1 addition & 0 deletions matter_server/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class EventType(Enum):
NODE_EVENT = "node_event"
ATTRIBUTE_UPDATED = "attribute_updated"
SERVER_SHUTDOWN = "server_shutdown"
SERVER_INFO_UPDATED = "server_info_updated"
ENDPOINT_ADDED = "endpoint_added"
ENDPOINT_REMOVED = "endpoint_removed"

Expand Down
2 changes: 2 additions & 0 deletions matter_server/server/device_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,15 @@ async def set_wifi_credentials(self, ssid: str, credentials: str) -> None:

await self._chip_device_controller.set_wifi_credentials(ssid, credentials)
self._wifi_credentials_set = True
self.server.signal_event(EventType.SERVER_INFO_UPDATED, self.server.get_info())

@api_command(APICommand.SET_THREAD_DATASET)
async def set_thread_operational_dataset(self, dataset: str) -> None:
"""Set Thread Operational dataset in the stack."""

await self._chip_device_controller.set_thread_operational_dataset(dataset)
self._thread_credentials_set = True
self.server.signal_event(EventType.SERVER_INFO_UPDATED, self.server.get_info())

@api_command(APICommand.OPEN_COMMISSIONING_WINDOW)
async def open_commissioning_window(
Expand Down

0 comments on commit 8776a4d

Please sign in to comment.