import { __esDecorate, __runInitializers } from "tslib";
import * as __validateArgs from "./IndicatorCC._validateArgs.js";
const { validateArgs_IndicatorCCAPI_get, validateArgs_IndicatorCCAPI_set, validateArgs_IndicatorCCAPI_sendReport, validateArgs_IndicatorCCAPI_getSupported, validateArgs_IndicatorCCAPI_reportSupported, validateArgs_IndicatorCCAPI_reportDescription, validateArgs_IndicatorCCAPI_setTimeout, validateArgs_IndicatorCCAPI_getTimeout, validateArgs_IndicatorCCAPI_getDescription } = __validateArgs;
import { CommandClasses, Indicator, MessagePriority, ValueMetadata, ZWaveError, ZWaveErrorCodes, encodeBitMask, getIndicatorProperty, parseBitMask, validatePayload, } from "@zwave-js/core";
import { Bytes, getEnumMemberName, num2hex } from "@zwave-js/shared";
import { clamp, roundTo } from "alcalzone-shared/math";
import { isArray } from "alcalzone-shared/typeguards";
import { CCAPI, POLL_VALUE, SET_VALUE, throwUnsupportedProperty, throwWrongValueType, } from "../lib/API.js";
import { CommandClass, } from "../lib/CommandClass.js";
import { API, CCCommand, ccValues, commandClass, expectedCCResponse, implementedVersion, useSupervision, } from "../lib/CommandClassDecorators.js";
import { IndicatorCommand } from "../lib/_Types.js";
function isManufacturerDefinedIndicator(indicatorId) {
    return indicatorId >= 0x80 && indicatorId <= 0x9f;
}
const timeoutStringRegex = /^(?:(?<hoursStr>\d+)h)?(?:(?<minutesStr>\d+)m)?(?:(?<secondsStr>\d+(?:\.\d+)?)s)?$/i;
const MAX_INDICATOR_OBJECTS = 31;
function parseIndicatorTimeoutString(text) {
    if (!text.length)
        return undefined;
    // Try to parse the numeric parts from a timeout
    const match = timeoutStringRegex.exec(text);
    if (!match)
        return undefined;
    const { hoursStr, minutesStr, secondsStr } = match.groups;
    if (!hoursStr && !minutesStr && !secondsStr)
        return undefined;
    const ret = {};
    if (hoursStr) {
        ret.hours = clamp(parseInt(hoursStr, 10), 0, 255);
    }
    if (minutesStr) {
        ret.minutes = clamp(parseInt(minutesStr, 10), 0, 255);
    }
    if (secondsStr) {
        ret.seconds = clamp(roundTo(parseFloat(secondsStr), 2), 0, 59.99);
    }
    return ret;
}
function groupByIndicatorId(values) {
    const ret = new Map();
    for (const value of values) {
        if (!ret.has(value.indicatorId)) {
            ret.set(value.indicatorId, []);
        }
        ret.get(value.indicatorId).push(value);
    }
    return ret;
}
function indicatorObjectsToTimeout(values) {
    const timeoutValues = values.filter((v) => [0x0a, 0x06, 0x07, 0x08].includes(v.propertyId));
    if (!timeoutValues.length)
        return undefined;
    const hours = (timeoutValues.find((v) => v.propertyId === 0x0a)?.value
        ?? 0);
    const minutes = (timeoutValues.find((v) => v.propertyId === 0x06)?.value
        ?? 0);
    const seconds = clamp(timeoutValues.find((v) => v.propertyId === 0x07)
        ?.value ?? 0, 0, 59)
        + clamp(timeoutValues.find((v) => v.propertyId === 0x08)
            ?.value ?? 0, 0, 99)
            / 100;
    return {
        hours,
        minutes,
        seconds,
    };
}
import { IndicatorCCValues } from "./_CCValues.generated.js";
export { IndicatorCCValues };
/**
 * Looks up the configured metadata for the given indicator and property
 */
function getIndicatorMetadata(indicatorId, propertyId, overrideIndicatorLabel) {
    const label = getIndicatorLabel(indicatorId, propertyId, overrideIndicatorLabel);
    const prop = getIndicatorProperty(propertyId);
    const baseMetadata = IndicatorCCValues.valueV2(indicatorId, propertyId).meta;
    if (!prop) {
        return {
            ...baseMetadata,
            ...ValueMetadata.UInt8,
            label,
        };
    }
    else {
        if (prop.type === "boolean") {
            return {
                ...baseMetadata,
                ...ValueMetadata.Boolean,
                label,
                description: prop.description,
                readable: !prop.readonly,
            };
        }
        else {
            // UInt8
            return {
                ...baseMetadata,
                ...ValueMetadata.UInt8,
                label,
                description: prop.description,
                min: prop.min,
                max: prop.max,
                readable: !prop.readonly,
            };
        }
    }
}
function getIndicatorName(indicatorId) {
    if (indicatorId) {
        return getEnumMemberName(Indicator, indicatorId);
    }
    else {
        return "Default";
    }
}
/** Returns a user-friendly label for an indicator */
function getIndicatorLabel(indicatorId, propertyId, overrideIndicatorLabel, overridePropertyLabel) {
    const defaultName = overrideIndicatorLabel || getIndicatorName(indicatorId);
    // Without a defined property, we cannot suggest a better label
    const prop = getIndicatorProperty(propertyId);
    if (!prop)
        return defaultName;
    // For indicators that correspond to an LCD or buttons,
    // the indicator enum names are enough
    if (indicatorId >= Indicator["LCD backlight"]
        && indicatorId <= Indicator["Button 12 indication"]
        && (propertyId === 0x01 /* Multilevel */
            || propertyId === 0x02 /* Binary */
            || overridePropertyLabel) // custom property label
    ) {
        let ret = Indicator[indicatorId];
        // Append the overridden property label if it exists
        if (overridePropertyLabel) {
            ret += ` - ${overridePropertyLabel}`;
        }
        return ret;
    }
    // Default to the current behavior of <indicator name> - <property label>
    return `${defaultName} - ${overridePropertyLabel || prop.label}`;
}
export function shouldExposeIndicatorValue(indicatorId, propertyId) {
    // Many possible indicator values are not suitable to be exposed as CC values,
    // usually because they need to be set in groups.
    // Indicator properties that should be exposed are marked as such
    const prop = getIndicatorProperty(propertyId);
    if (prop && !prop.exposeAsValue) {
        return false;
    }
    // Unless we know better, expose anyways
    return true;
}
let IndicatorCCAPI = (() => {
    let _classDecorators = [API(CommandClasses.Indicator)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = CCAPI;
    let _instanceExtraInitializers = [];
    let _get_decorators;
    let _set_decorators;
    let _sendReport_decorators;
    let _getSupported_decorators;
    let _reportSupported_decorators;
    let _reportDescription_decorators;
    let _setTimeout_decorators;
    let _getTimeout_decorators;
    let _getDescription_decorators;
    var IndicatorCCAPI = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _get_decorators = [validateArgs_IndicatorCCAPI_get()];
            _set_decorators = [validateArgs_IndicatorCCAPI_set()];
            _sendReport_decorators = [validateArgs_IndicatorCCAPI_sendReport()];
            _getSupported_decorators = [validateArgs_IndicatorCCAPI_getSupported()];
            _reportSupported_decorators = [validateArgs_IndicatorCCAPI_reportSupported()];
            _reportDescription_decorators = [validateArgs_IndicatorCCAPI_reportDescription()];
            _setTimeout_decorators = [validateArgs_IndicatorCCAPI_setTimeout()];
            _getTimeout_decorators = [validateArgs_IndicatorCCAPI_getTimeout()];
            _getDescription_decorators = [validateArgs_IndicatorCCAPI_getDescription()];
            __esDecorate(this, null, _get_decorators, { kind: "method", name: "get", static: false, private: false, access: { has: obj => "get" in obj, get: obj => obj.get }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _set_decorators, { kind: "method", name: "set", static: false, private: false, access: { has: obj => "set" in obj, get: obj => obj.set }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _sendReport_decorators, { kind: "method", name: "sendReport", static: false, private: false, access: { has: obj => "sendReport" in obj, get: obj => obj.sendReport }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _getSupported_decorators, { kind: "method", name: "getSupported", static: false, private: false, access: { has: obj => "getSupported" in obj, get: obj => obj.getSupported }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _reportSupported_decorators, { kind: "method", name: "reportSupported", static: false, private: false, access: { has: obj => "reportSupported" in obj, get: obj => obj.reportSupported }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _reportDescription_decorators, { kind: "method", name: "reportDescription", static: false, private: false, access: { has: obj => "reportDescription" in obj, get: obj => obj.reportDescription }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _setTimeout_decorators, { kind: "method", name: "setTimeout", static: false, private: false, access: { has: obj => "setTimeout" in obj, get: obj => obj.setTimeout }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _getTimeout_decorators, { kind: "method", name: "getTimeout", static: false, private: false, access: { has: obj => "getTimeout" in obj, get: obj => obj.getTimeout }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _getDescription_decorators, { kind: "method", name: "getDescription", static: false, private: false, access: { has: obj => "getDescription" in obj, get: obj => obj.getDescription }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCAPI = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        supportsCommand(cmd) {
            switch (cmd) {
                case IndicatorCommand.Get:
                case IndicatorCommand.Report:
                    return this.isSinglecast();
                case IndicatorCommand.Set:
                    return true; // This is mandatory
                case IndicatorCommand.SupportedGet:
                case IndicatorCommand.SupportedReport:
                    return this.version >= 2 && this.isSinglecast();
                case IndicatorCommand.DescriptionGet:
                case IndicatorCommand.DescriptionReport:
                    return this.version >= 4 && this.isSinglecast();
            }
            return super.supportsCommand(cmd);
        }
        get [SET_VALUE]() {
            return async function ({ property, propertyKey }, value) {
                const valueId = {
                    commandClass: this.ccId,
                    endpoint: this.endpoint.index,
                    property,
                    propertyKey,
                };
                if (property === "value") {
                    // V1 value
                    if (typeof value !== "number") {
                        throwWrongValueType(this.ccId, property, "number", typeof value);
                    }
                    return this.set(value);
                }
                else if (IndicatorCCValues.valueV2.is(valueId)) {
                    const indicatorId = property;
                    const propertyId = propertyKey;
                    const expectedType = getIndicatorMetadata(indicatorId, propertyId).type;
                    // V2+ value
                    if (typeof value !== expectedType) {
                        throwWrongValueType(this.ccId, property, expectedType, typeof value);
                    }
                    return this.set([
                        {
                            indicatorId,
                            propertyId,
                            value: value,
                        },
                    ]);
                }
                else if (IndicatorCCValues.timeout.is(valueId)) {
                    const indicatorId = property;
                    if (typeof value !== "string") {
                        throwWrongValueType(this.ccId, property, "string", typeof value);
                    }
                    return this.setTimeout(indicatorId, value);
                }
                else if (IndicatorCCValues.identify.is(valueId)) {
                    if (typeof value !== "boolean") {
                        throwWrongValueType(this.ccId, property, "boolean", typeof value);
                    }
                    return this.identify();
                }
                else {
                    throwUnsupportedProperty(this.ccId, property);
                }
            };
        }
        get [POLL_VALUE]() {
            return async function ({ property }) {
                if (property === "value")
                    return this.get();
                if (typeof property === "number") {
                    return this.get(property);
                }
                throwUnsupportedProperty(this.ccId, property);
            };
        }
        async get(indicatorId) {
            this.assertSupportsCommand(IndicatorCommand, IndicatorCommand.Get);
            const cc = new IndicatorCCGet({
                nodeId: this.endpoint.nodeId,
                endpointIndex: this.endpoint.index,
                indicatorId,
            });
            const response = await this.host.sendCommand(cc, this.commandOptions);
            if (!response)
                return;
            if (response.values)
                return response.values;
            return response.indicator0Value;
        }
        async set(value) {
            this.assertSupportsCommand(IndicatorCommand, IndicatorCommand.Set);
            if (this.version === 1 && typeof value !== "number") {
                throw new ZWaveError(`Node ${this.endpoint
                    .nodeId} only supports IndicatorCC V1 which requires a single value to be set`, ZWaveErrorCodes.Argument_Invalid);
            }
            else if (this.version >= 2
                && isArray(value)
                && value.length > MAX_INDICATOR_OBJECTS) {
                throw new ZWaveError(`Only ${MAX_INDICATOR_OBJECTS} indicator values can be set at a time!`, ZWaveErrorCodes.Argument_Invalid);
            }
            const cc = new IndicatorCCSet({
                nodeId: this.endpoint.nodeId,
                endpointIndex: this.endpoint.index,
                ...(typeof value === "number" ? { value } : { values: value }),
            });
            return this.host.sendCommand(cc, this.commandOptions);
        }
        async sendReport(options) {
            this.assertSupportsCommand(IndicatorCommand, IndicatorCommand.Report);
            const cc = new IndicatorCCReport({
                nodeId: this.endpoint.nodeId,
                endpointIndex: this.endpoint.index,
                ...options,
            });
            await this.host.sendCommand(cc, this.commandOptions);
        }
        async getSupported(indicatorId) {
            this.assertSupportsCommand(IndicatorCommand, IndicatorCommand.SupportedGet);
            const cc = new IndicatorCCSupportedGet({
                nodeId: this.endpoint.nodeId,
                endpointIndex: this.endpoint.index,
                indicatorId,
            });
            const response = await this.host.sendCommand(cc, this.commandOptions);
            if (response) {
                return {
                    // Include the actual indicator ID if 0x00 was requested
                    ...(indicatorId === 0x00
                        ? { indicatorId: response.indicatorId }
                        : undefined),
                    supportedProperties: response.supportedProperties,
                    nextIndicatorId: response.nextIndicatorId,
                };
            }
        }
        async reportSupported(indicatorId, supportedProperties, nextIndicatorId) {
            this.assertSupportsCommand(IndicatorCommand, IndicatorCommand.SupportedReport);
            const cc = new IndicatorCCSupportedReport({
                nodeId: this.endpoint.nodeId,
                endpointIndex: this.endpoint.index,
                indicatorId,
                supportedProperties,
                nextIndicatorId,
            });
            await this.host.sendCommand(cc, this.commandOptions);
        }
        async reportDescription(indicatorId, description) {
            this.assertSupportsCommand(IndicatorCommand, IndicatorCommand.DescriptionReport);
            const cc = new IndicatorCCDescriptionReport({
                nodeId: this.endpoint.nodeId,
                endpointIndex: this.endpoint.index,
                indicatorId,
                description,
            });
            await this.host.sendCommand(cc, this.commandOptions);
        }
        /**
         * Instructs the node to identify itself. Available starting with V3 of this CC.
         */
        async identify() {
            if (this.version < 3) {
                throw new ZWaveError(`The identify command is only supported in Indicator CC version 3 and above`, ZWaveErrorCodes.CC_NotSupported);
            }
            return this.set([
                {
                    indicatorId: 0x50,
                    propertyId: 0x03,
                    value: 0x08,
                },
                {
                    indicatorId: 0x50,
                    propertyId: 0x04,
                    value: 0x03,
                },
                {
                    indicatorId: 0x50,
                    propertyId: 0x05,
                    value: 0x06,
                },
            ]);
        }
        /**
         * Set a timeout for a given indicator ID after which the indicator will be turned off.
         * @param timeout The timeout in one of the supported forms:
         * 	- a timeout string in the form `12h18m17.59s`. All parts (hours, minutes, seconds, hundredths) are optional, but must be specified in this order. An empty string will be treated like `undefined`.
         * 	- an object specifying the timeout parts. An empty object will be treated like `undefined`.
         * 	- `undefined` to disable the timeout.
         */
        async setTimeout(indicatorId, timeout) {
            this.assertPhysicalEndpoint(this.endpoint);
            if (this.version < 3) {
                throw new ZWaveError(`The setTimeout command is only supported in Indicator CC version 3 and above`, ZWaveErrorCodes.CC_NotSupported);
            }
            if (typeof timeout === "string") {
                if (timeout === "") {
                    timeout = undefined;
                }
                else {
                    const parsed = parseIndicatorTimeoutString(timeout);
                    if (!parsed) {
                        throw new ZWaveError(`The timeout string "${timeout}" is not valid`, ZWaveErrorCodes.Argument_Invalid);
                    }
                    timeout = parsed;
                }
            }
            const supportedPropertyIDs = IndicatorCC.getSupportedPropertyIDsCached(this.host, this.endpoint, indicatorId);
            const objects = [];
            if (timeout) {
                const hours = timeout.hours ?? 0;
                const minutes = timeout.minutes ?? 0;
                const seconds = Math.floor(timeout.seconds ?? 0);
                const hundredths = Math.round(((timeout.seconds ?? 0) % 1) * 100);
                if (hours) {
                    if (!supportedPropertyIDs?.includes(0x0a)) {
                        throw new ZWaveError(`The indicator ${indicatorId} does not support setting the timeout in hours`, ZWaveErrorCodes.Argument_Invalid);
                    }
                    objects.push({
                        indicatorId,
                        propertyId: 0x0a,
                        value: hours,
                    });
                }
                if (minutes) {
                    if (!supportedPropertyIDs?.includes(0x06)) {
                        throw new ZWaveError(`The indicator ${indicatorId} does not support setting the timeout in minutes`, ZWaveErrorCodes.Argument_Invalid);
                    }
                    objects.push({
                        indicatorId,
                        propertyId: 0x06,
                        value: minutes,
                    });
                }
                if (seconds) {
                    if (!supportedPropertyIDs?.includes(0x07)) {
                        throw new ZWaveError(`The indicator ${indicatorId} does not support setting the timeout in seconds`, ZWaveErrorCodes.Argument_Invalid);
                    }
                    objects.push({
                        indicatorId,
                        propertyId: 0x07,
                        value: seconds,
                    });
                }
                if (hundredths) {
                    if (!supportedPropertyIDs?.includes(0x08)) {
                        throw new ZWaveError(`The indicator ${indicatorId} does not support setting the timeout in 1/100 seconds`, ZWaveErrorCodes.Argument_Invalid);
                    }
                    objects.push({
                        indicatorId,
                        propertyId: 0x08,
                        value: hundredths,
                    });
                }
            }
            if (!objects.length) {
                objects.push(...(supportedPropertyIDs ?? []).map((p) => ({
                    indicatorId,
                    propertyId: p,
                    value: 0,
                })));
            }
            return this.set(objects);
        }
        /**
         * Returns the timeout after which the given indicator will be turned off.
         */
        async getTimeout(indicatorId) {
            const values = await this.get(indicatorId);
            if (!isArray(values))
                return;
            return indicatorObjectsToTimeout(values);
        }
        async getDescription(indicatorId) {
            this.assertSupportsCommand(IndicatorCommand, IndicatorCommand.DescriptionGet);
            const cc = new IndicatorCCDescriptionGet({
                nodeId: this.endpoint.nodeId,
                endpointIndex: this.endpoint.index,
                indicatorId,
            });
            const response = await this.host.sendCommand(cc, this.commandOptions);
            return response?.description;
        }
        constructor() {
            super(...arguments);
            __runInitializers(this, _instanceExtraInitializers);
        }
    };
    return IndicatorCCAPI = _classThis;
})();
export { IndicatorCCAPI };
let IndicatorCC = (() => {
    let _classDecorators = [commandClass(CommandClasses.Indicator), implementedVersion(4), ccValues(IndicatorCCValues)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = CommandClass;
    var IndicatorCC = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCC = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        async interview(ctx) {
            const node = this.getNode(ctx);
            const endpoint = this.getEndpoint(ctx);
            const api = CCAPI.create(CommandClasses.Indicator, ctx, endpoint).withOptions({
                priority: MessagePriority.NodeQuery,
            });
            ctx.logNode(node.id, {
                endpoint: this.endpointIndex,
                message: `Interviewing ${this.ccName}...`,
                direction: "none",
            });
            if (api.version > 1) {
                ctx.logNode(node.id, {
                    endpoint: this.endpointIndex,
                    message: "scanning supported indicator IDs...",
                    direction: "outbound",
                });
                // Query ID 0 to get the first supported ID
                let curId = 0x00;
                const supportedIndicatorIds = [];
                do {
                    const supportedResponse = await api.getSupported(curId);
                    if (!supportedResponse) {
                        ctx.logNode(node.id, {
                            endpoint: this.endpointIndex,
                            message: "Time out while scanning supported indicator IDs, skipping interview...",
                            level: "warn",
                        });
                        return;
                    }
                    if (supportedResponse.supportedProperties.length > 0) {
                        supportedIndicatorIds.push(supportedResponse.indicatorId ?? curId);
                    }
                    curId = supportedResponse.nextIndicatorId;
                } while (curId !== 0x00);
                // The IDs are not stored by the report CCs so store them here once we have all of them
                this.setValue(ctx, IndicatorCCValues.supportedIndicatorIds, supportedIndicatorIds);
                const logMessage = `supported indicator IDs: ${supportedIndicatorIds.join(", ")}`;
                ctx.logNode(node.id, {
                    endpoint: this.endpointIndex,
                    message: logMessage,
                    direction: "inbound",
                });
                if (api.version >= 4) {
                    const manufacturerDefinedIndicatorIds = supportedIndicatorIds
                        .filter((id) => isManufacturerDefinedIndicator(id));
                    if (manufacturerDefinedIndicatorIds.length > 0) {
                        ctx.logNode(node.id, {
                            endpoint: this.endpointIndex,
                            message: "retrieving description for manufacturer-defined indicator IDs...",
                            direction: "outbound",
                        });
                        for (const id of manufacturerDefinedIndicatorIds) {
                            await api.getDescription(id);
                        }
                    }
                }
            }
            // Query current values
            await this.refreshValues(ctx);
            // Remember that the interview is complete
            this.setInterviewComplete(ctx, true);
        }
        async refreshValues(ctx, options) {
            const node = this.getNode(ctx);
            const endpoint = this.getEndpoint(ctx);
            const api = CCAPI.create(CommandClasses.Indicator, ctx, endpoint).withOptions({
                priority: options?.priority ?? MessagePriority.NodeQuery,
            });
            if (api.version === 1) {
                ctx.logNode(node.id, {
                    endpoint: this.endpointIndex,
                    message: "requesting current indicator value...",
                    direction: "outbound",
                });
                await api.get();
            }
            else {
                const supportedIndicatorIds = this.getValue(ctx, IndicatorCCValues.supportedIndicatorIds) ?? [];
                for (const indicatorId of supportedIndicatorIds) {
                    ctx.logNode(node.id, {
                        endpoint: this.endpointIndex,
                        message: `requesting current indicator value (id = ${num2hex(indicatorId)})...`,
                        direction: "outbound",
                    });
                    await api.get(indicatorId);
                }
            }
        }
        translatePropertyKey(ctx, property, propertyKey) {
            if (property === "value") {
                // CC version 1 only has a single value that doesn't need to be translated
                return undefined;
            }
            else if (typeof property === "number"
                && typeof propertyKey === "number") {
                // The indicator property is our property key
                const prop = getIndicatorProperty(propertyKey);
                if (prop)
                    return prop.label;
            }
            else if (typeof property === "number"
                && propertyKey === "timeout") {
                return "Timeout";
            }
            return super.translatePropertyKey(ctx, property, propertyKey);
        }
        translateProperty(ctx, property, propertyKey) {
            if (typeof property === "number") {
                if (typeof propertyKey === "number" || propertyKey === "timeout") {
                    // The indicator corresponds to our property
                    if (property in Indicator)
                        return Indicator[property];
                }
            }
            return super.translateProperty(ctx, property, propertyKey);
        }
        supportsV2Indicators(ctx) {
            // First test if there are any indicator ids defined
            const supportedIndicatorIds = this.getValue(ctx, IndicatorCCValues.supportedIndicatorIds);
            if (!supportedIndicatorIds?.length)
                return false;
            // Then test if there are any property ids defined
            return supportedIndicatorIds.some((indicatorId) => !!this.getValue(ctx, IndicatorCCValues.supportedPropertyIDs(indicatorId))?.length);
        }
        getManufacturerDefinedIndicatorLabel(ctx, indicatorId) {
            if (isManufacturerDefinedIndicator(indicatorId)) {
                return this.getValue(ctx, IndicatorCCValues.indicatorDescription(indicatorId));
            }
        }
        static getSupportedPropertyIDsCached(ctx, endpoint, indicatorId) {
            return ctx
                .getValueDB(endpoint.nodeId)
                .getValue(IndicatorCCValues.supportedPropertyIDs(indicatorId).endpoint(endpoint.index));
        }
    };
    return IndicatorCC = _classThis;
})();
export { IndicatorCC };
let IndicatorCCSet = (() => {
    let _classDecorators = [CCCommand(IndicatorCommand.Set), useSupervision()];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = IndicatorCC;
    var IndicatorCCSet = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCSet = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        constructor(options) {
            super(options);
            if ("value" in options) {
                this.indicator0Value = options.value;
            }
            else {
                this.values = options.values;
            }
        }
        static from(raw, ctx) {
            validatePayload(raw.payload.length >= 1);
            const objCount = raw.payload.length >= 2
                ? raw.payload[1] & 0b11111
                : 0;
            if (objCount === 0) {
                const indicator0Value = raw.payload[0];
                return new this({
                    nodeId: ctx.sourceNodeId,
                    value: indicator0Value,
                });
            }
            validatePayload(raw.payload.length >= 2 + 3 * objCount);
            const values = [];
            for (let i = 0; i < objCount; i++) {
                const offset = 2 + 3 * i;
                const value = {
                    indicatorId: raw.payload[offset],
                    propertyId: raw.payload[offset + 1],
                    value: raw.payload[offset + 2],
                };
                values.push(value);
            }
            return new this({
                nodeId: ctx.sourceNodeId,
                values,
            });
        }
        indicator0Value;
        values;
        serialize(ctx) {
            if (this.values != undefined) {
                // V2+
                this.payload = Bytes.alloc(2 + 3 * this.values.length, 0);
                // Byte 0 is the legacy value
                const objCount = this.values.length & MAX_INDICATOR_OBJECTS;
                this.payload[1] = objCount;
                for (let i = 0; i < objCount; i++) {
                    const offset = 2 + 3 * i;
                    this.payload[offset] = this.values[i].indicatorId;
                    this.payload[offset + 1] = this.values[i].propertyId;
                    const value = this.values[i].value;
                    this.payload[offset + 2] = value === true
                        ? 0xff
                        : value === false
                            ? 0x00
                            : value;
                }
            }
            else {
                // V1
                this.payload = Bytes.from([this.indicator0Value ?? 0]);
            }
            return super.serialize(ctx);
        }
        toLogEntry(ctx) {
            const message = {};
            if (this.indicator0Value != undefined) {
                message["indicator 0 value"] = this.indicator0Value;
            }
            if (this.values != undefined) {
                message.values = this.values
                    .map((v) => `
· indicatorId: ${v.indicatorId}
  propertyId:  ${v.propertyId}
  value:       ${v.value}`)
                    .join("");
            }
            return {
                ...super.toLogEntry(ctx),
                message,
            };
        }
    };
    return IndicatorCCSet = _classThis;
})();
export { IndicatorCCSet };
let IndicatorCCReport = (() => {
    let _classDecorators = [CCCommand(IndicatorCommand.Report)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = IndicatorCC;
    var IndicatorCCReport = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCReport = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        constructor(options) {
            super(options);
            if ("value" in options) {
                this.indicator0Value = options.value;
            }
            else if ("values" in options) {
                if (options.values.length > MAX_INDICATOR_OBJECTS) {
                    throw new ZWaveError(`Only ${MAX_INDICATOR_OBJECTS} indicator values can be set at a time!`, ZWaveErrorCodes.Argument_Invalid);
                }
                this.values = options.values;
            }
        }
        static from(raw, ctx) {
            validatePayload(raw.payload.length >= 1);
            const objCount = raw.payload.length >= 2
                ? raw.payload[1] & 0b11111
                : 0;
            if (objCount === 0) {
                const indicator0Value = raw.payload[0];
                return new this({
                    nodeId: ctx.sourceNodeId,
                    value: indicator0Value,
                });
            }
            validatePayload(raw.payload.length >= 2 + 3 * objCount);
            const values = [];
            for (let i = 0; i < objCount; i++) {
                const offset = 2 + 3 * i;
                const value = {
                    indicatorId: raw.payload[offset],
                    propertyId: raw.payload[offset + 1],
                    value: raw.payload[offset + 2],
                };
                values.push(value);
            }
            return new this({
                nodeId: ctx.sourceNodeId,
                values,
            });
            // TODO: Think if we want this:
            // // If not all Property IDs are included in the command for the actual Indicator ID,
            // // a controlling node MUST assume non-specified Property IDs values to be 0x00.
            // const indicatorId = this.values[0].indicatorId;
            // const supportedIndicatorProperties =
            // 	valueDB.getValue<number[]>(
            // 		getSupportedPropertyIDsValueID(
            // 			this.endpointIndex,
            // 			indicatorId,
            // 		),
            // 	) ?? [];
            // // Find out which ones are missing
            // const missingIndicatorProperties = supportedIndicatorProperties.filter(
            // 	prop =>
            // 		!this.values!.find(({ propertyId }) => prop === propertyId),
            // );
            // // And assume they are 0 (false)
            // for (const missing of missingIndicatorProperties) {
            // 	this.setIndicatorValue({
            // 		indicatorId,
            // 		propertyId: missing,
            // 		value: 0,
            // 	});
            // }
        }
        persistValues(ctx) {
            if (!super.persistValues(ctx))
                return false;
            if (this.indicator0Value != undefined) {
                if (!this.supportsV2Indicators(ctx)) {
                    // Publish the value
                    const valueV1 = IndicatorCCValues.valueV1;
                    this.setMetadata(ctx, valueV1);
                    this.setValue(ctx, valueV1, this.indicator0Value);
                }
                else {
                    if (this.isSinglecast()) {
                        // Don't!
                        ctx.logNode(this.nodeId, {
                            message: `ignoring V1 indicator report because the node supports V2 indicators`,
                            direction: "none",
                            endpoint: this.endpointIndex,
                        });
                    }
                }
            }
            else if (this.values) {
                // Store the simple values first
                for (const value of this.values) {
                    this.setIndicatorValue(ctx, value);
                }
                // Then group values into the convenience properties
                const groupedValues = groupByIndicatorId(this.values);
                for (const [indicatorId, values] of groupedValues) {
                    // Some devices report all properties it supports in every report,
                    // even if it does not support all properties for the given indicator ID.
                    // To avoid confusion, we need to filter out the unsupported properties
                    const supportedPropertyIDs = this.getValue(ctx, IndicatorCCValues.supportedPropertyIDs(indicatorId)) ?? [];
                    const filteredValues = values.filter((v) => supportedPropertyIDs.includes(v.propertyId));
                    const overrideIndicatorLabel = this
                        .getManufacturerDefinedIndicatorLabel(ctx, indicatorId);
                    // ... timeout
                    const timeout = indicatorObjectsToTimeout(filteredValues);
                    if (timeout) {
                        let timeoutString = "";
                        if (timeout?.hours)
                            timeoutString += `${timeout.hours}h`;
                        if (timeout?.minutes) {
                            timeoutString += `${timeout.minutes}m`;
                        }
                        if (timeout?.seconds) {
                            timeoutString += `${timeout.seconds}s`;
                        }
                        const timeoutValue = IndicatorCCValues.timeout(indicatorId);
                        const metadata = {
                            ...timeoutValue.meta,
                            label: getIndicatorLabel(indicatorId, 0x06, // This is actually ignored, but pass a valid ID for clarity
                            overrideIndicatorLabel, "Timeout"),
                        };
                        this.setMetadata(ctx, timeoutValue, metadata);
                        this.setValue(ctx, timeoutValue, timeoutString);
                    }
                }
            }
            return true;
        }
        indicator0Value;
        values;
        setIndicatorValue(ctx, value) {
            // Manufacturer-defined indicators may need a custom label
            const overrideIndicatorLabel = this
                .getManufacturerDefinedIndicatorLabel(ctx, value.indicatorId);
            const metadata = getIndicatorMetadata(value.indicatorId, value.propertyId, overrideIndicatorLabel);
            // Some values need to be converted
            if (metadata.type === "boolean") {
                value.value = !!value.value;
            }
            // Only expose the value if it should be user-facing
            if (value.indicatorId === Indicator["Node Identify"]) {
                return;
            }
            const prop = getIndicatorProperty(value.propertyId);
            if (!prop?.exposeAsValue)
                return;
            // And only if it is actually supported and not just reported by accident
            const supportedPropertyIDs = this.getValue(ctx, IndicatorCCValues.supportedPropertyIDs(value.indicatorId)) ?? [];
            if (!supportedPropertyIDs.includes(value.propertyId))
                return;
            // Publish the value
            const valueV2 = IndicatorCCValues.valueV2(value.indicatorId, value.propertyId);
            this.setMetadata(ctx, valueV2, metadata);
            this.setValue(ctx, valueV2, value.value);
        }
        serialize(ctx) {
            if (this.values != undefined) {
                // V2+
                this.payload = Bytes.alloc(2 + 3 * this.values.length, 0);
                // Byte 0 is the legacy value
                const objCount = this.values.length & MAX_INDICATOR_OBJECTS;
                this.payload[1] = objCount;
                for (let i = 0; i < objCount; i++) {
                    const offset = 2 + 3 * i;
                    this.payload[offset] = this.values[i].indicatorId;
                    this.payload[offset + 1] = this.values[i].propertyId;
                    const value = this.values[i].value;
                    this.payload[offset + 2] = value === true
                        ? 0xff
                        : value === false
                            ? 0x00
                            : value;
                }
            }
            else {
                // V1
                this.payload = Bytes.from([this.indicator0Value ?? 0]);
            }
            return super.serialize(ctx);
        }
        toLogEntry(ctx) {
            const message = {};
            if (this.indicator0Value != undefined) {
                message["indicator 0 value"] = this.indicator0Value;
            }
            if (this.values != undefined) {
                message.values = this.values
                    .map((v) => `
· indicatorId: ${v.indicatorId}
  propertyId:  ${v.propertyId}
  value:       ${v.value}`)
                    .join("");
            }
            return {
                ...super.toLogEntry(ctx),
                message,
            };
        }
    };
    return IndicatorCCReport = _classThis;
})();
export { IndicatorCCReport };
let IndicatorCCGet = (() => {
    let _classDecorators = [CCCommand(IndicatorCommand.Get), expectedCCResponse(IndicatorCCReport)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = IndicatorCC;
    var IndicatorCCGet = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCGet = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        constructor(options) {
            super(options);
            this.indicatorId = options.indicatorId;
        }
        static from(raw, ctx) {
            let indicatorId;
            if (raw.payload.length > 0) {
                indicatorId = raw.payload[0];
            }
            return new this({
                nodeId: ctx.sourceNodeId,
                indicatorId,
            });
        }
        indicatorId;
        serialize(ctx) {
            if (this.indicatorId != undefined) {
                this.payload = Bytes.from([this.indicatorId]);
            }
            return super.serialize(ctx);
        }
        toLogEntry(ctx) {
            return {
                ...super.toLogEntry(ctx),
                message: {
                    indicator: getIndicatorName(this.indicatorId),
                },
            };
        }
    };
    return IndicatorCCGet = _classThis;
})();
export { IndicatorCCGet };
let IndicatorCCSupportedReport = (() => {
    let _classDecorators = [CCCommand(IndicatorCommand.SupportedReport)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = IndicatorCC;
    var IndicatorCCSupportedReport = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCSupportedReport = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        constructor(options) {
            super(options);
            this.indicatorId = options.indicatorId;
            this.nextIndicatorId = options.nextIndicatorId;
            this.supportedProperties = options.supportedProperties;
        }
        static from(raw, ctx) {
            validatePayload(raw.payload.length >= 3);
            const indicatorId = raw.payload[0];
            const nextIndicatorId = raw.payload[1];
            const bitMaskLength = raw.payload[2] & 0b11111;
            let supportedProperties;
            if (bitMaskLength === 0) {
                supportedProperties = [];
            }
            else {
                validatePayload(raw.payload.length >= 3 + bitMaskLength);
                // The bit mask starts at 0, but bit 0 is not used
                supportedProperties = parseBitMask(raw.payload.subarray(3, 3 + bitMaskLength), 0).filter((v) => v !== 0);
            }
            return new this({
                nodeId: ctx.sourceNodeId,
                indicatorId,
                nextIndicatorId,
                supportedProperties,
            });
        }
        persistValues(ctx) {
            if (!super.persistValues(ctx))
                return false;
            if (this.indicatorId !== 0x00) {
                // Remember which property IDs are supported
                this.setValue(ctx, IndicatorCCValues.supportedPropertyIDs(this.indicatorId), this.supportedProperties);
            }
            return true;
        }
        indicatorId;
        nextIndicatorId;
        supportedProperties;
        serialize(ctx) {
            const bitmask = this.supportedProperties.length > 0
                ? encodeBitMask(this.supportedProperties, undefined, 0)
                : new Bytes();
            this.payload = Bytes.concat([
                [
                    this.indicatorId,
                    this.nextIndicatorId,
                    bitmask.length,
                ],
                bitmask,
            ]);
            return super.serialize(ctx);
        }
        toLogEntry(ctx) {
            return {
                ...super.toLogEntry(ctx),
                message: {
                    indicator: getIndicatorName(this.indicatorId),
                    "supported properties": this.supportedProperties
                        .map((id) => getIndicatorProperty(id)?.label
                        ?? `Unknown (${num2hex(id)})`)
                        .join(", "),
                    "next indicator": getIndicatorName(this.nextIndicatorId),
                },
            };
        }
    };
    return IndicatorCCSupportedReport = _classThis;
})();
export { IndicatorCCSupportedReport };
function testResponseForIndicatorSupportedGet(sent, received) {
    return sent.indicatorId === 0 || received.indicatorId === sent.indicatorId;
}
let IndicatorCCSupportedGet = (() => {
    let _classDecorators = [CCCommand(IndicatorCommand.SupportedGet), expectedCCResponse(IndicatorCCSupportedReport, testResponseForIndicatorSupportedGet)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = IndicatorCC;
    var IndicatorCCSupportedGet = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCSupportedGet = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        constructor(options) {
            super(options);
            this.indicatorId = options.indicatorId;
        }
        static from(raw, ctx) {
            validatePayload(raw.payload.length >= 1);
            const indicatorId = raw.payload[0];
            return new this({
                nodeId: ctx.sourceNodeId,
                indicatorId,
            });
        }
        indicatorId;
        serialize(ctx) {
            this.payload = Bytes.from([this.indicatorId]);
            return super.serialize(ctx);
        }
        toLogEntry(ctx) {
            return {
                ...super.toLogEntry(ctx),
                message: {
                    indicator: getIndicatorName(this.indicatorId),
                },
            };
        }
    };
    return IndicatorCCSupportedGet = _classThis;
})();
export { IndicatorCCSupportedGet };
let IndicatorCCDescriptionReport = (() => {
    let _classDecorators = [CCCommand(IndicatorCommand.DescriptionReport)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = IndicatorCC;
    var IndicatorCCDescriptionReport = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCDescriptionReport = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        constructor(options) {
            super(options);
            this.indicatorId = options.indicatorId;
            this.description = options.description;
        }
        static from(raw, ctx) {
            validatePayload(raw.payload.length >= 2);
            const indicatorId = raw.payload[0];
            const descrptionLength = raw.payload[1];
            validatePayload(raw.payload.length >= 2 + descrptionLength);
            const description = raw.payload
                .subarray(2, 2 + descrptionLength)
                .toString("utf8");
            return new this({
                nodeId: ctx.sourceNodeId,
                indicatorId,
                description,
            });
        }
        indicatorId;
        description;
        persistValues(ctx) {
            if (!super.persistValues(ctx))
                return false;
            if (this.description) {
                this.setValue(ctx, IndicatorCCValues.indicatorDescription(this.indicatorId), this.description);
            }
            return true;
        }
        serialize(ctx) {
            const description = Bytes.from(this.description, "utf8");
            this.payload = Bytes.concat([
                [this.indicatorId, description.length],
                description,
            ]);
            return super.serialize(ctx);
        }
        toLogEntry(ctx) {
            return {
                ...super.toLogEntry(ctx),
                message: {
                    "indicator ID": this.indicatorId,
                    description: this.description || "(none)",
                },
            };
        }
    };
    return IndicatorCCDescriptionReport = _classThis;
})();
export { IndicatorCCDescriptionReport };
function testResponseForIndicatorDescriptionGet(sent, received) {
    return received.indicatorId === sent.indicatorId;
}
let IndicatorCCDescriptionGet = (() => {
    let _classDecorators = [CCCommand(IndicatorCommand.DescriptionGet), expectedCCResponse(IndicatorCCDescriptionReport, testResponseForIndicatorDescriptionGet)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = IndicatorCC;
    var IndicatorCCDescriptionGet = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            IndicatorCCDescriptionGet = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        constructor(options) {
            super(options);
            this.indicatorId = options.indicatorId;
        }
        static from(raw, ctx) {
            validatePayload(raw.payload.length >= 1);
            const indicatorId = raw.payload[0];
            return new this({
                nodeId: ctx.sourceNodeId,
                indicatorId,
            });
        }
        indicatorId;
        serialize(ctx) {
            this.payload = Bytes.from([this.indicatorId]);
            return super.serialize(ctx);
        }
        toLogEntry(ctx) {
            return {
                ...super.toLogEntry(ctx),
                message: {
                    "indicator ID": this.indicatorId,
                },
            };
        }
    };
    return IndicatorCCDescriptionGet = _classThis;
})();
export { IndicatorCCDescriptionGet };
//# sourceMappingURL=IndicatorCC.js.map