Skip to content

Commit

Permalink
refactor: further reduce dependency on IZWaveNode and IZWaveEndpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Oct 9, 2024
1 parent cf13ee0 commit a0a7584
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 63 deletions.
8 changes: 3 additions & 5 deletions packages/cc/src/cc/AssociationGroupInfoCC.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
CommandClasses,
type EndpointId,
type IZWaveEndpoint,
type MaybeNotKnown,
type MessageOrCCLogEntry,
MessagePriority,
type MessageRecord,
type SupportsCC,
encodeCCId,
getCCName,
parseCCId,
Expand Down Expand Up @@ -302,8 +302,7 @@ export class AssociationGroupInfoCC extends CommandClass {

public static findGroupsForIssuedCommand(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and endpoint capabilities would be enough
endpoint: IZWaveEndpoint,
endpoint: EndpointId & SupportsCC,
ccId: CommandClasses,
command: number,
): number[] {
Expand Down Expand Up @@ -333,8 +332,7 @@ export class AssociationGroupInfoCC extends CommandClass {

private static getAssociationGroupCountCached(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and endpoint capabilities would be enough
endpoint: IZWaveEndpoint,
endpoint: EndpointId & SupportsCC,
): number {
// The association group count is either determined by the
// Association CC or the Multi Channel Association CC
Expand Down
12 changes: 4 additions & 8 deletions packages/cc/src/cc/ConfigurationCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import {
CommandClasses,
ConfigValueFormat,
type ConfigurationMetadata,
type IVirtualEndpoint,
type IZWaveEndpoint,
type MaybeNotKnown,
type MessageOrCCLogEntry,
MessagePriority,
Expand Down Expand Up @@ -38,6 +36,7 @@ import { composeObject } from "alcalzone-shared/objects";
import { padStart } from "alcalzone-shared/strings";
import {
CCAPI,
type CCAPIEndpoint,
POLL_VALUE,
type PollValueImplementation,
SET_VALUE,
Expand Down Expand Up @@ -145,8 +144,7 @@ type NormalizedConfigurationCCAPISetOptions =

function createConfigurationCCInstance(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and index should be enough
endpoint: IZWaveEndpoint | IVirtualEndpoint,
endpoint: CCAPIEndpoint,
): ConfigurationCC {
return CommandClass.createInstanceUnchecked(
applHost,
Expand All @@ -157,8 +155,7 @@ function createConfigurationCCInstance(

function normalizeConfigurationCCAPISetOptions(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and index should be enough
endpoint: IZWaveEndpoint | IVirtualEndpoint,
endpoint: CCAPIEndpoint,
options: ConfigurationCCAPISetOptions,
): NormalizedConfigurationCCAPISetOptions {
if ("bitMask" in options && options.bitMask) {
Expand Down Expand Up @@ -215,8 +212,7 @@ function normalizeConfigurationCCAPISetOptions(

function bulkMergePartialParamValues(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and index should be enough
endpoint: IZWaveEndpoint | IVirtualEndpoint,
endpoint: CCAPIEndpoint,
options: NormalizedConfigurationCCAPISetOptions[],
): (NormalizedConfigurationCCAPISetOptions & { bitMask?: undefined })[] {
// Merge partial parameters before doing anything else. Therefore, take the non-partials, ...
Expand Down
6 changes: 2 additions & 4 deletions packages/cc/src/cc/ManufacturerProprietaryCC.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import {
CommandClasses,
type IVirtualEndpoint,
type IZWaveEndpoint,
ZWaveError,
ZWaveErrorCodes,
validatePayload,
} from "@zwave-js/core/safe";
import type { ZWaveApplicationHost, ZWaveHost } from "@zwave-js/host/safe";
import { staticExtends } from "@zwave-js/shared/safe";
import { validateArgs } from "@zwave-js/transformers";
import { CCAPI } from "../lib/API";
import { CCAPI, type CCAPIEndpoint } from "../lib/API";
import {
type CCCommandOptions,
CommandClass,
Expand Down Expand Up @@ -41,7 +39,7 @@ export type ManufacturerProprietaryCCConstructor<
export class ManufacturerProprietaryCCAPI extends CCAPI {
public constructor(
applHost: ZWaveApplicationHost,
endpoint: IZWaveEndpoint | IVirtualEndpoint,
endpoint: CCAPIEndpoint,
) {
super(applHost, endpoint);

Expand Down
11 changes: 7 additions & 4 deletions packages/cc/src/cc/TimeParametersCC.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {
CommandClasses,
type IZWaveEndpoint,
type MessageOrCCLogEntry,
MessagePriority,
type SupervisionResult,
ValueMetadata,
formatDate,
validatePayload,
} from "@zwave-js/core";
import { type MaybeNotKnown } from "@zwave-js/core/safe";
import {
type ControlsCC,
type EndpointId,
type MaybeNotKnown,
type SupportsCC,
} from "@zwave-js/core/safe";
import type {
ZWaveApplicationHost,
ZWaveHost,
Expand Down Expand Up @@ -60,8 +64,7 @@ export const TimeParametersCCValues = Object.freeze({
*/
function shouldUseLocalTime(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and endpoint capabilities would be enough
endpoint: IZWaveEndpoint,
endpoint: EndpointId & SupportsCC & ControlsCC,
): boolean {
// GH#311 Some nodes have no way to determine the time zone offset,
// so they need to interpret the set time as local time instead of UTC.
Expand Down
49 changes: 40 additions & 9 deletions packages/cc/src/lib/API.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { type CompatOverrideQueries } from "@zwave-js/config";
import {
CommandClasses,
type ControlsCC,
type Duration,
type EndpointId,
type GetEndpoint,
type IVirtualEndpoint,
type IZWaveEndpoint,
type IZWaveNode,
type MaybeNotKnown,
NODE_ID_BROADCAST,
NODE_ID_BROADCAST_LR,
NOT_KNOWN,
type PhysicalNodes,
type SendCommandOptions,
type SupervisionResult,
type SupportsCC,
type TXReport,
type ValueChangeOptions,
type ValueDB,
type ValueID,
type VirtualEndpointId,
ZWaveError,
ZWaveErrorCodes,
getCCName,
Expand Down Expand Up @@ -148,22 +154,47 @@ export interface SchedulePollOptions {
transition?: "fast" | "slow";
}

// Defines the necessary traits an endpoint passed to a CC API must have
export type CCAPIEndpoint =
& (
| (
// Physical endpoints must let us query their controlled CCs
EndpointId & ControlsCC
)
| (
// Virtual endpoints must let us query their physical nodes,
// the CCs those nodes implement, and access the endpoints of those
// physical nodes
VirtualEndpointId & {
node: PhysicalNodes<
& SupportsCC
& ControlsCC
& GetEndpoint<EndpointId & SupportsCC & ControlsCC>
>;
}
)
)
& SupportsCC;

export type PhysicalCCAPIEndpoint = CCAPIEndpoint & EndpointId;
export type VirtualCCAPIEndpoint = CCAPIEndpoint & VirtualEndpointId;

/**
* The base class for all CC APIs exposed via `Node.commandClasses.<CCName>`
* @publicAPI
*/
export class CCAPI {
public constructor(
protected readonly applHost: ZWaveApplicationHost,
protected readonly endpoint: IZWaveEndpoint | IVirtualEndpoint,
protected readonly endpoint: CCAPIEndpoint,
) {
this.ccId = getCommandClass(this);
}

public static create<T extends CommandClasses>(
ccId: T,
applHost: ZWaveApplicationHost,
endpoint: IZWaveEndpoint | IVirtualEndpoint,
endpoint: CCAPIEndpoint,
requireSupport?: boolean,
): CommandClasses extends T ? CCAPI : CCToAPI<T> {
const APIConstructor = getAPI(ccId);
Expand Down Expand Up @@ -312,7 +343,7 @@ export class CCAPI {
const timeoutMs = durationMs + additionalDelay;

if (this.isSinglecast()) {
const node = this.endpoint.getNodeUnsafe();
const node = this.getNodeUnsafe();
if (!node) return false;

return this.applHost.schedulePoll(
Expand Down Expand Up @@ -416,8 +447,8 @@ export class CCAPI {
}

protected assertPhysicalEndpoint(
endpoint: IZWaveEndpoint | IVirtualEndpoint,
): asserts endpoint is IZWaveEndpoint {
endpoint: EndpointId | VirtualEndpointId,
): asserts endpoint is EndpointId {
if (endpoint.virtual) {
throw new ZWaveError(
`This method is not supported for virtual nodes!`,
Expand Down Expand Up @@ -602,7 +633,7 @@ export class CCAPI {

function overrideQueriesWrapper(
applHost: ZWaveApplicationHost,
endpoint: IZWaveEndpoint,
endpoint: PhysicalCCAPIEndpoint,
ccId: CommandClasses,
method: string,
overrides: CompatOverrideQueries,
Expand Down Expand Up @@ -742,18 +773,18 @@ function overrideQueriesWrapper(
export class PhysicalCCAPI extends CCAPI {
public constructor(
applHost: ZWaveApplicationHost,
endpoint: IZWaveEndpoint | IVirtualEndpoint,
endpoint: CCAPIEndpoint,
) {
super(applHost, endpoint);
this.assertPhysicalEndpoint(endpoint);
}

declare protected readonly endpoint: IZWaveEndpoint;
declare protected readonly endpoint: PhysicalCCAPIEndpoint;
}

export type APIConstructor<T extends CCAPI = CCAPI> = new (
applHost: ZWaveApplicationHost,
endpoint: IZWaveEndpoint | IVirtualEndpoint,
endpoint: CCAPIEndpoint,
) => T;

// This type is auto-generated by maintenance/generateCCAPIInterface.ts
Expand Down
1 change: 1 addition & 0 deletions packages/cc/src/lib/CommandClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ export class CommandClass implements ICommandClass {
&& value.options.autoCreate(
applHost,
{
virtual: false,
nodeId: this.nodeId as number,
index: this.endpointIndex,
},
Expand Down
14 changes: 6 additions & 8 deletions packages/cc/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
type IZWaveNode,
type MaybeNotKnown,
NOT_KNOWN,
type NodeId,
SecurityClass,
type SupportsCC,
ZWaveError,
ZWaveErrorCodes,
actuatorCCs,
Expand Down Expand Up @@ -36,8 +38,7 @@ import {

export function getAssociations(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and endpoint capabilities would be enough
endpoint: IZWaveEndpoint,
endpoint: EndpointId & SupportsCC,
): ReadonlyMap<number, readonly AssociationAddress[]> {
const ret = new Map<number, readonly AssociationAddress[]>();

Expand Down Expand Up @@ -242,8 +243,7 @@ export function checkAssociation(

export function getAssociationGroups(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and endpoint capabilities would be enough
endpoint: IZWaveEndpoint,
endpoint: EndpointId & SupportsCC,
): ReadonlyMap<number, AssociationGroup> {
// Check whether we have multi channel support or not
let assocInstance: typeof AssociationCC;
Expand Down Expand Up @@ -640,8 +640,7 @@ export async function removeAssociations(

export function getLifelineGroupIds(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and endpoint capabilities would be enough
endpoint: IZWaveEndpoint,
endpoint: EndpointId & SupportsCC,
): number[] {
// For now only support this for the root endpoint - i.e. node
if (endpoint.index > 0) return [];
Expand Down Expand Up @@ -1243,8 +1242,7 @@ export async function assignLifelineIssueingCommand(

export function doesAnyLifelineSendActuatorOrSensorReports(
applHost: ZWaveApplicationHost,
// FIXME: GH#7261 ID and endpoint capabilities would be enough
node: IZWaveNode,
node: NodeId & SupportsCC,
): MaybeNotKnown<boolean> {
// No association support means no unsolicited reports
if (
Expand Down
51 changes: 39 additions & 12 deletions packages/core/src/abstractions/IZWaveEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,56 @@ import type { IVirtualNode, IZWaveNode } from "./IZWaveNode";

/** Identifies an endpoint */
export interface EndpointId {
readonly virtual: false;
readonly nodeId: number;
readonly index: number;
}

/** A basic abstraction of a Z-Wave endpoint providing access to the relevant functionality */
export interface IZWaveEndpoint extends EndpointId {
readonly virtual: false;
/** Allows querying if a CC is supported and in which version */
export interface SupportsCC {
supportsCC(cc: CommandClasses): boolean;
getCCVersion(cc: CommandClasses): number;
}

/** Allows querying if a CC is controlled */
export interface ControlsCC {
controlsCC(cc: CommandClasses): boolean;
}

/** Allows querying if a CC is supported or controlled only securely */
export interface IsCCSecure {
isCCSecure(cc: CommandClasses): boolean;
}

/** Allows modifying the list of supported/controlled CCs */
export interface ModifyCCs {
addCC(cc: CommandClasses, info: Partial<CommandClassInfo>): void;
removeCC(cc: CommandClasses): void;
}

/** A basic abstraction of a Z-Wave endpoint providing access to the relevant functionality */
export interface IZWaveEndpoint
extends EndpointId, SupportsCC, ControlsCC, IsCCSecure, ModifyCCs
{
getCCs(): Iterable<[ccId: CommandClasses, info: CommandClassInfo]>;
supportsCC(cc: CommandClasses): boolean;
controlsCC(cc: CommandClasses): boolean;
isCCSecure(cc: CommandClasses): boolean;
getNodeUnsafe(): IZWaveNode | undefined;
}

/** A basic abstraction of an endpoint of a virtual node (multicast or broadcast) providing access to the relevant functionality */
export interface IVirtualEndpoint {
/** Identifies a virtual endpoint */
export interface VirtualEndpointId {
readonly virtual: true;
readonly nodeId: number | MulticastDestination;
readonly node: IVirtualNode;
readonly index: number;
readonly virtual: true;
getCCVersion(cc: CommandClasses): number;
supportsCC(cc: CommandClasses): boolean;
}

/** A basic abstraction of an endpoint of a virtual node (multicast or broadcast) providing access to the relevant functionality */
export interface IVirtualEndpoint extends VirtualEndpointId, SupportsCC {
readonly node: IVirtualNode;
}

// TODO: Use cases in CCAPI implementations:
// - EndpointId or VirtualEndpointId
// - SupportsCC
// - VirtualEndpoint:
// - physical nodes -> NodeId
// - physical nodes -> Endpoint -> SupportsCC,ControlsCC
Loading

0 comments on commit a0a7584

Please sign in to comment.