@@ -10,19 +10,20 @@ import * as ZSpec from "../zspec";
1010import type { Eui64 } from "../zspec/tstypes" ;
1111import * as Zcl from "../zspec/zcl" ;
1212import type { TPartialClusterAttributes } from "../zspec/zcl/definition/clusters-types" ;
13- import type { FrameControl } from "../zspec/zcl/definition/tstype" ;
13+ import type { CustomClusters , FrameControl } from "../zspec/zcl/definition/tstype" ;
1414import * as Zdo from "../zspec/zdo" ;
1515import type * as ZdoTypes from "../zspec/zdo/definition/tstypes" ;
1616import Database from "./database" ;
1717import type * as Events from "./events" ;
1818import GreenPower from "./greenPower" ;
1919import { ZclFrameConverter } from "./helpers" ;
2020import { checkInstallCode , parseInstallCode } from "./helpers/installCodes" ;
21+ import zclTransactionSequenceNumber from "./helpers/zclTransactionSequenceNumber" ;
2122import { Device , Entity } from "./model" ;
2223import { InterviewState } from "./model/device" ;
2324import Group from "./model/group" ;
2425import Touchlink from "./touchlink" ;
25- import type { DeviceType , GreenPowerDeviceJoinedPayload } from "./tstype" ;
26+ import type { DeviceType , GreenPowerDeviceJoinedPayload , RawPayload } from "./tstype" ;
2627
2728const NS = "zh:controller" ;
2829
@@ -229,6 +230,113 @@ export class Controller extends events.EventEmitter<ControllerEventMap> {
229230 return startResult ;
230231 }
231232
233+ /**
234+ * Send a request according to given payload.
235+ * @param rawPayload Payload used to determine and build the request
236+ * @param customClusters Manually passed custom clusters used in ZCL serialization (if any, if matching)
237+ * @returns A response may or may not be returned depending on given payload (up to caller to verify)
238+ */
239+ public async sendRaw ( rawPayload : RawPayload , customClusters : CustomClusters = { } ) : Promise < unknown > {
240+ const {
241+ ieeeAddress,
242+ networkAddress,
243+ groupId,
244+ dstEndpoint,
245+ srcEndpoint = ZSpec . HA_ENDPOINT ,
246+ interPan = false ,
247+ profileId = ZSpec . HA_PROFILE_ID ,
248+ clusterKey,
249+ zdoArgs,
250+ zcl,
251+ disableResponse = false ,
252+ timeout = 10000 ,
253+ } = rawPayload ;
254+
255+ if ( profileId === Zdo . ZDO_PROFILE_ID ) {
256+ assert ( ieeeAddress ) ;
257+ assert ( networkAddress !== undefined ) ;
258+ assert ( clusterKey !== undefined && typeof clusterKey === "number" ) ;
259+
260+ // will fail if args are incorrect for request
261+ const buf = Zdo . Buffalo . buildRequest ( this . adapter . hasZdoMessageOverhead , clusterKey , ...( zdoArgs ?? [ ] ) ) ;
262+
263+ return await this . adapter . sendZdo ( ieeeAddress , networkAddress , clusterKey , buf , disableResponse ) ;
264+ }
265+
266+ assert ( zcl ) ;
267+
268+ if ( interPan ) {
269+ assert ( zcl . commandKey ) ;
270+ assert ( zcl . payload ) ;
271+ assert ( zcl . frameType === undefined || zcl . frameType === Zcl . FrameType . GLOBAL || zcl . frameType === Zcl . FrameType . SPECIFIC ) ;
272+ assert (
273+ zcl . direction === undefined || zcl . direction === Zcl . Direction . CLIENT_TO_SERVER || zcl . direction === Zcl . Direction . SERVER_TO_CLIENT ,
274+ ) ;
275+
276+ const zclFrame = Zcl . Frame . create (
277+ zcl . frameType ?? Zcl . FrameType . SPECIFIC ,
278+ zcl . direction ?? Zcl . Direction . CLIENT_TO_SERVER ,
279+ true ,
280+ zcl . manufacturerCode ,
281+ 0 ,
282+ zcl . commandKey ,
283+ clusterKey ?? Zcl . Clusters . touchlink . ID ,
284+ zcl . payload ,
285+ customClusters ,
286+ ) ;
287+
288+ return ieeeAddress
289+ ? await this . adapter . sendZclFrameInterPANToIeeeAddr ( zclFrame , ieeeAddress )
290+ : await this . adapter . sendZclFrameInterPANBroadcast ( zclFrame , timeout , disableResponse ) ;
291+ }
292+
293+ assert ( clusterKey !== undefined ) ;
294+ assert ( zcl . frameType !== undefined ) ;
295+ assert ( zcl . direction !== undefined ) ;
296+
297+ const zclFrame = Zcl . Frame . create (
298+ zcl . frameType ,
299+ zcl . direction ,
300+ zcl . disableDefaultResponse ?? false ,
301+ zcl . manufacturerCode ,
302+ zcl . tsn ?? zclTransactionSequenceNumber . next ( ) ,
303+ zcl . commandKey ,
304+ clusterKey ,
305+ zcl . payload ,
306+ customClusters ,
307+ ) ;
308+
309+ if ( groupId !== undefined ) {
310+ assert ( groupId >= 0x0000 && groupId <= 0xffff ) ;
311+
312+ return await this . adapter . sendZclFrameToGroup ( groupId , zclFrame , srcEndpoint , profileId ) ;
313+ }
314+
315+ if ( networkAddress !== undefined && networkAddress >= ZSpec . BROADCAST_MIN ) {
316+ assert ( dstEndpoint !== undefined && dstEndpoint >= 0x01 && dstEndpoint <= 0xff ) ;
317+
318+ return await this . adapter . sendZclFrameToAll ( dstEndpoint , zclFrame , srcEndpoint , networkAddress , profileId ) ;
319+ }
320+
321+ if ( ieeeAddress && dstEndpoint !== undefined ) {
322+ assert ( networkAddress !== undefined && networkAddress >= 0x0001 && networkAddress <= ZSpec . BROADCAST_MIN - 1 ) ;
323+
324+ return await this . adapter . sendZclFrameToEndpoint (
325+ ieeeAddress ,
326+ networkAddress ,
327+ dstEndpoint ,
328+ zclFrame ,
329+ timeout ,
330+ disableResponse ,
331+ false ,
332+ srcEndpoint ,
333+ profileId ,
334+ ) ;
335+ }
336+
337+ throw new Error ( "Invalid raw payload" ) ;
338+ }
339+
232340 public async touchlinkIdentify ( ieeeAddr : string , channel : number ) : Promise < void > {
233341 await this . touchlink . identify ( ieeeAddr , channel ) ;
234342 }
0 commit comments