diff --git a/package.json b/package.json index dfd17800494294f58722255e04d7fbf74ad2fcfa..c8d2c14c43a412d21c9667e5a0e14320d141b0d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "usp-js", - "version": "0.2.19", + "version": "0.2.20", "description": "Helper library for easy usp communication using mqtt over tcp or ws.", "main": "node/index.js", "browser": "web/index.js", diff --git a/src/commands/get.ts b/src/commands/get.ts index b26a7f9e5ffeff5a4192d6923144f943859b1305..cfc6df4ea958e6c685fa93eeb6c50a7a1a963090 100644 --- a/src/commands/get.ts +++ b/src/commands/get.ts @@ -4,8 +4,10 @@ import * as util from "./util"; const isEmpty = (obj: any): boolean => obj && typeof obj === "object" && Object.keys(obj).length === 0; -const decode: DecodeFn = (msg) => { +const decode: DecodeFn = (msg, decodeOptions) => { const resolvedPathResultsArr = util.searchAll(msg, "resolvedPathResults"); + if (decodeOptions?.raw) return [resolvedPathResultsArr]; + const requestedPath = util.search(msg, "requestedPath"); if (resolvedPathResultsArr) { // path has search query (ex. Device.IP.Interface.[Name=="wan"].) diff --git a/src/commands/index.ts b/src/commands/index.ts index 42a313e5392986daa31fe62f7a7306638c79f9c5..8f712620eaac81fdffa8e2a81263f2f328b61f99 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -8,6 +8,7 @@ import { OnFn, DecodeResponse, Proto, + DecodeOptions, } from "../types"; import get from "./get"; @@ -36,7 +37,7 @@ const commands: Record<CommandType, CommandObject> = { SET: set, }; -const recipes: RecipeObject[] = [resolve, operateRecipe, subscribe]; +const recipes: RecipeObject[] = [resolve as any, operateRecipe, subscribe]; export const makeRecipes = (call: CallFn, on: OnFn): any => recipes.reduce( @@ -64,6 +65,7 @@ export const decode = (parsedMsg: any): DecodeResponse => { const err = searchParent(parsedMsg, "errMsg") || null; const command = extractCommand(parsedMsg); const foundId = search(parsedMsg, "msgId"); + // if id is formatted by me (command@) then use, otherwise check for sub id const id = foundId.includes("@") ? foundId @@ -71,14 +73,25 @@ export const decode = (parsedMsg: any): DecodeResponse => { // if command is unkown if (!command) return unkownErr(parsedMsg); - if (err) return [id, null, err, command]; const cmd: CommandObject | null = commands[command] || null; if (!cmd) return unkownErr(parsedMsg); - const [ddata, did, derr] = cmd.decode(parsedMsg); - return [did || id, ddata, derr || err, command]; + const [decodedData, decodedId, decodedErr] = cmd.decode(parsedMsg); + return [decodedId || id, decodedData, decodedErr || err, command]; +}; + +export const decodeWithOptions = ( + parsedMsg: any, + cmdType: CommandType | undefined, + options: DecodeOptions +) => { + const cmd: CommandObject | null = commands[cmdType as CommandType] || null; + if (!cmd) return unkownErr(parsedMsg); + + const [decodedData] = cmd.decode(parsedMsg, options); + return decodedData; }; export const makeEncode = diff --git a/src/commands/util.ts b/src/commands/util.ts index db44763608413932db1dd0f0e8efc72cd4043a68..9faef7d90bbcb4ed3a309adb782ba9fb6f8de07f 100644 --- a/src/commands/util.ts +++ b/src/commands/util.ts @@ -178,3 +178,12 @@ export const uniq = (initial?: string): string => ( Date.now().toString(36) + Math.random().toString(36).substr(2, 5) ).toUpperCase(); + +export const parseID = (msg: any) => { + const foundId = search(msg, "msgId"); + // if id is formatted by me (command@) then use, otherwise check for sub id + const id = foundId.includes("@") + ? foundId + : search(msg, "subscriptionId") || null; + return id; +}; diff --git a/src/configurations/build.ts b/src/configurations/build.ts index b8754f2f7787772fb3333af501201c848daa6baa..de5b3f69d794cc75519003c2198a18a9bb01b642 100644 --- a/src/configurations/build.ts +++ b/src/configurations/build.ts @@ -1,4 +1,10 @@ -import { decode, makeEncode, makeRecipes, readMsg } from "../commands"; +import { + decode, + decodeWithOptions, + makeEncode, + makeRecipes, + readMsg, +} from "../commands"; import { CallFn, OnFn, @@ -124,7 +130,7 @@ const buildConnect: BuildConnectionFn = client.on("message", (_topic, data: any) => { const parsedMsg = readMsg(proto, data || _topic); const [id, message, err, cmdType] = decode(parsedMsg); - if (typeof id !== "string") { + if (typeof id !== "string") { handleError( `Could not locate id for message:\n${JSON.stringify( parsedMsg, @@ -136,7 +142,14 @@ const buildConnect: BuildConnectionFn = const call = router.get(id, cmdType || "NOTIFY"); if (call && call.resolve && call.reject) { if (err) call.reject(err); - else call.resolve(message); + else if (call.options) { + const messageAfterOptions = decodeWithOptions( + parsedMsg, + cmdType, + call.options + ); + call.resolve(messageAfterOptions); + } else call.resolve(message); } const cbs = callbackRouter.get(id); @@ -187,6 +200,7 @@ const buildConnect: BuildConnectionFn = router.add(id, callOpts?.responseMsgType || command, { resolve, reject, + options: args.options, }); client.publish(publishEndpoint, msg); } @@ -195,7 +209,7 @@ const buildConnect: BuildConnectionFn = await client.subscribe(subscribeEndpoint); const baseUSP: Partial<USP> = { - get: (paths) => call("GET", { paths }), + get: (paths, options) => call("GET", { paths, options }), set: (path, value) => call("SET", { path, value }), add: (path, value) => call("ADD", { path, value }), del: (paths, allowPartial) => call("DELETE", { paths, allowPartial }), diff --git a/src/types.ts b/src/types.ts index 2f1edf9d85890bb1893f834627c06432bd1cce49..f1bdb3d55e240743876662716445b9cba5aaba0b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,11 +9,20 @@ export type CommandType = | "GET_INSTANCES" | "GET_SUPPORTED_PROTO"; +export type GetCommandOptions = { + raw?: boolean; +}; + export type GetReturn = string | Record<string, any> | Record<string, any>[]; -export type GetCommand = (paths: string | string[]) => Promise<GetReturn>; +export type GetCommand = ( + paths: string | string[], + options?: GetCommandOptions +) => Promise<GetReturn>; export type SetCommand = ( path: string | string[], - value: (JSValue | JSValue[] | InputRecord) | (JSValue | JSValue[] | InputRecord)[] + value: + | (JSValue | JSValue[] | InputRecord) + | (JSValue | JSValue[] | InputRecord)[] ) => Promise<void>; export type AddCommand = ( path: string | string[], @@ -218,6 +227,8 @@ export interface USP { * await usp.get("Device.WiFi.Radio.1.") * // or * await usp.get(["Device.WiFi.Radio.1.", "Device.WiFi.Radio.2."]) + * + * await usp.get("Device.WiFi.Radio.1.", { raw: true }) // skips parsing, produces raw results * ``` */ get: GetCommand; @@ -378,12 +389,12 @@ export interface USP { options: (opts: Options) => USP; } - /** - * Connect to device - * @param opts - Connection options - * @param events - Optional event handlers - * @returns A set of functions for interacting with the device - */ +/** + * Connect to device + * @param opts - Connection options + * @param events - Optional event handlers + * @returns A set of functions for interacting with the device + */ export type Connect = ( options: ConnectionOptions, events?: ConnectionEvents @@ -464,7 +475,15 @@ export interface OtherConnectionOptions { export type ConnectionOptions = URLConnectionOptions | HostConnectionOptions; export type Response = string | Record<string, any>; -export type DecodeFn = (msg: Record<string, any>) => DecodeResponse | [any]; + +export type DecodeOptions = { + raw?: boolean; +}; + +export type DecodeFn = ( + msg: Record<string, any>, + decodeOptions?: DecodeOptions +) => DecodeResponse | [any]; export type DecodeResponse = | [any] | [any, ResponseID | null, null | Response]