UNPKG

inventoresed

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

124 lines (113 loc) 3.47 kB
import { ZWaveError, ZWaveErrorCodes } from "../error/ZWaveError"; /** Ensures that the values array is consecutive */ export function isConsecutiveArray(values: number[]): boolean { return values.every((v, i, arr) => (i === 0 ? true : v - 1 === arr[i - 1])); } /** Returns an object that includes all non-undefined properties from the original one */ export function stripUndefined<T>(obj: Record<string, T>): Record<string, T> { const ret = {} as Record<string, T>; for (const [key, value] of Object.entries(obj)) { if (value !== undefined) ret[key] = value; } return ret; } /** * Validates the payload about to be parsed. This can be used to avoid crashes caused by malformed packets * @param assertions An array of assertions to check if we have a valid payload */ type PayloadValidationFunction = (...assertions: unknown[]) => void; interface ValidatePayload extends PayloadValidationFunction { /** * @param reason The optional reason for a rejection. Strings will be logged, error codes will be used for internal communication */ withReason(reason: string | ZWaveErrorCodes): PayloadValidationFunction; /** * @param reason The reason for a rejection. Strings will be logged, error codes will be used for internal communication */ fail(reason: string | ZWaveErrorCodes): never; } function validatePayloadInternal( reason: string | ZWaveErrorCodes | undefined, ...assertions: unknown[] ): void { if (!assertions.every(Boolean)) { throw new ZWaveError( "The message payload is invalid!", ZWaveErrorCodes.PacketFormat_InvalidPayload, reason, ); } } // Export and augment the validatePayload method with a reason export const validatePayload = validatePayloadInternal.bind( undefined, undefined, ) as ValidatePayload; validatePayload.withReason = (reason: string | ZWaveErrorCodes) => validatePayloadInternal.bind(undefined, reason); validatePayload.fail = (reason: string | ZWaveErrorCodes) => validatePayload.withReason(reason)(false) as unknown as never; /** * Determines how many bits a value must be shifted to be right-aligned with a given bit mask * Example: * ```txt * Mask = 00110000 * ^---- => 4 bits * * Mask = 00110001 * ^ => 0 bits * ``` */ export function getMinimumShiftForBitMask(mask: number): number { let i = 0; while (mask % 2 === 0) { mask >>>= 1; if (mask === 0) break; i++; } return i; } /** * Determines how many wide a given bit mask is * Example: * ```txt * Mask = 00110000 * ^^---- => 2 bits * * Mask = 00110001 * ^....^ => 6 bits * ``` */ export function getBitMaskWidth(mask: number): number { mask = mask >>> getMinimumShiftForBitMask(mask); let i = 0; while (mask > 0) { mask >>>= 1; i++; } return i; } /** * Determines the legal range of values that can be encoded at with the given bit mask * Example: * ```txt * Mask = 00110000 * ^^---- => 0..3 unsigned OR -2..+1 signed * * Mask = 00110001 * ^....^ => 0..63 unsigned OR -32..+31 signed (with gaps) * ``` */ export function getLegalRangeForBitMask( mask: number, unsigned: boolean, ): [min: number, max: number] { if (mask === 0) return [0, 0]; const bitMaskWidth = getBitMaskWidth(mask); const min = unsigned || bitMaskWidth == 1 ? 0 : -(2 ** (bitMaskWidth - 1)); const max = unsigned || bitMaskWidth == 1 ? 2 ** bitMaskWidth - 1 : 2 ** (bitMaskWidth - 1) - 1; return [min, max]; }