"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.fzLegrand = exports.tzLegrand = exports.readInitialBatteryState = exports.eLegrand = exports.legrandExtend = exports.legrandOptions = void 0;
const zigbee_herdsman_1 = require("zigbee-herdsman");
const m = __importStar(require("../lib/modernExtend"));
const utils = __importStar(require("../lib/utils"));
const exposes = __importStar(require("./exposes"));
const logger_1 = require("./logger");
const NS = "zhc:legrand";
const e = exposes.presets;
const ea = exposes.access;
const shutterCalibrationModes = {
    0: { description: "classic_nllv", onlyNLLV: true, supportsTilt: false },
    1: { description: "specific_nllv", onlyNLLV: true, supportsTilt: false },
    2: { description: "up_down_stop", onlyNLLV: false, supportsTilt: false },
    3: { description: "temporal", onlyNLLV: false, supportsTilt: false },
    4: { description: "venetian_bso", onlyNLLV: false, supportsTilt: true },
};
const ledModes = {
    1: "led_in_dark",
    2: "led_if_on",
};
const ledEffects = {
    0: "blink 3",
    1: "fixed",
    2: "blink green",
    3: "blink blue",
};
const ledColors = {
    0: "default",
    1: "red",
    2: "green",
    3: "blue",
    4: "lightblue",
    5: "yellow",
    6: "pink",
    7: "white",
};
const optsLegrand = {
    identityEffect: () => {
        return e
            .composite("Identity effect", "identity_effect", ea.SET)
            .withDescription("Defines the identification effect to simplify the device identification.")
            .withFeature(e.enum("effect", ea.SET, Object.values(ledEffects)).withLabel("Effect"))
            .withFeature(e.enum("color", ea.SET, Object.values(ledColors)).withLabel("Color"));
    },
};
const getApplicableCalibrationModes = (isNLLVSwitch) => {
    return Object.fromEntries(Object.entries(shutterCalibrationModes)
        .filter((e) => (isNLLVSwitch ? true : e[1].onlyNLLV === false))
        .map((e) => [e[0], e[1].description]));
};
exports.legrandOptions = { manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP, disableDefaultResponse: true };
const LEGRAND_GREENPOWER_LOOKUP = {
    16: "home_arrival",
    17: "home_departure", // ZLGP14
    18: "daytime_day",
    19: "daytime_night", // ZLGP16, yes these commandIDs are lower than ZLGP15s'
    20: "press_1",
    21: "press_2",
    22: "press_3",
    23: "press_4", // ZLGP15
    34: "press_once",
    32: "press_twice", // ZLGP17, ZLGP18
    51: "down_hold", // ZLGP17, ZLGP18
    52: "stop",
    53: "up",
    54: "down", // 600087l
    55: "up_hold", // ZLGP17, ZLGP18
};
exports.legrandExtend = {
    addLegrandDevicesCluster: () => m.deviceAddCustomCluster("manuSpecificLegrandDevices", {
        name: "manuSpecificLegrandDevices",
        ID: 0xfc01,
        manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
        attributes: {
            deviceMode: { name: "deviceMode", ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.DATA16, write: true },
            ledInDark: { name: "ledInDark", ID: 0x0001, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN, write: true },
            ledIfOn: { name: "ledIfOn", ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN, write: true },
        },
        commands: {},
        commandsResponse: {},
    }),
    addLegrandDevices2Cluster: () => m.deviceAddCustomCluster("manuSpecificLegrandDevices2", {
        name: "manuSpecificLegrandDevices2",
        ID: 0xfc40,
        manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
        attributes: {
            pilotWireMode: { name: "pilotWireMode", ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
        },
        commands: {
            command0: { name: "command0", ID: 0x00, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
        },
        commandsResponse: {},
    }),
    addLegrandDevices3Cluster: () => m.deviceAddCustomCluster("manuSpecificLegrandDevices3", {
        name: "manuSpecificLegrandDevices3",
        ID: 0xfc41,
        manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
        attributes: {},
        commands: {
            command0: { name: "command0", ID: 0x00, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
        },
        commandsResponse: {},
    }),
    addLegrandClosuresWindowCovering: () => m.deviceAddCustomCluster("closuresWindowCovering", {
        name: "closuresWindowCovering",
        ID: zigbee_herdsman_1.Zcl.Clusters.closuresWindowCovering.ID,
        attributes: {
            stepPositionLift: {
                name: "stepPositionLift",
                ID: 0xf001,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
                write: true,
                max: 0xff,
            },
            calibrationMode: {
                name: "calibrationMode",
                ID: 0xf002,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
                write: true,
                max: 0xff,
            },
            targetPositionTiltPercentage: {
                name: "targetPositionTiltPercentage",
                ID: 0xf003,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
                write: true,
                max: 0xff,
            },
            stepPositionTilt: {
                name: "stepPositionTilt",
                ID: 0xf004,
                type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
                write: true,
                max: 0xff,
            },
        },
        commands: {},
        commandsResponse: {},
    }),
    addLegrandHaElectricalMeasurement: () => m.deviceAddCustomCluster("haElectricalMeasurement", {
        name: "haElectricalMeasurement",
        ID: zigbee_herdsman_1.Zcl.Clusters.haElectricalMeasurement.ID,
        attributes: {
            //    type not declared in the code
            // powerAlarmActiveValue: {
            //     name: "powerAlarmActiveValue",
            //     ID: 0xf000,  // 61440
            //     type: Zcl.DataType.??,
            //     manufacturerCode: Zcl.ManufacturerCode.LEGRAND_GROUP,
            // },
            powerAlarmEnabled: {
                name: "powerAlarmEnabled",
                ID: 0xf001, // 61441
                type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
                write: true,
            },
            powerAlarmWhThreshold: {
                name: "powerAlarmWhThreshold",
                ID: 0xf002, // 61442
                type: zigbee_herdsman_1.Zcl.DataType.INT16,
                manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LEGRAND_GROUP,
                write: true,
            },
        },
        commands: {},
        commandsResponse: {},
    }),
};
exports.eLegrand = {
    identify: () => {
        return e
            .enum("identify", ea.SET, ["identify"])
            .withDescription("Blinks the built-in LED to make it easier to identify the device")
            .withCategory("config");
    },
    ledInDark: () => {
        return e
            .binary("led_in_dark", ea.ALL, "ON", "OFF")
            .withDescription("Enables the built-in LED allowing to see the switch in the dark")
            .withCategory("config");
    },
    ledIfOn: () => {
        return e.binary("led_if_on", ea.ALL, "ON", "OFF").withDescription("Enables the LED on activity").withCategory("config");
    },
    getCover: (device) => {
        const c = e.cover_position();
        const calMode = !utils.isDummyDevice(device)
            ? Number(device.getEndpoint(1)?.clusters?.closuresWindowCovering?.attributes?.calibrationMode)
            : 0;
        const showTilt = calMode ? utils.getFromLookup(calMode, shutterCalibrationModes)?.supportsTilt === true : false;
        if (showTilt) {
            c.addFeature(new exposes.Numeric("tilt", ea.ALL)
                .withValueMin(0)
                .withValueMax(100)
                .withValueStep(25)
                .withPreset("Closed", 0, "Vertical")
                .withPreset("25 %", 25, "25%")
                .withPreset("50 %", 50, "50%")
                .withPreset("75 %", 75, "75%")
                .withPreset("Open", 100, "Horizontal")
                .withUnit("%")
                .withDescription("Tilt percentage of that cover"));
        }
        return c;
    },
    getCalibrationModes: (isNLLVSwitch) => {
        const modes = getApplicableCalibrationModes(isNLLVSwitch);
        return e
            .enum("calibration_mode", ea.ALL, Object.values(modes))
            .withDescription("Defines the calibration mode of the switch. (Caution: Changing modes requires a recalibration of the shutter switch!)")
            .withCategory("config");
    },
};
const readInitialBatteryState = async (event) => {
    if (event.type === "deviceAnnounce") {
        const endpoint = event.data.device.getEndpoint(1);
        await endpoint.read("genPowerCfg", ["batteryVoltage"], exports.legrandOptions);
    }
};
exports.readInitialBatteryState = readInitialBatteryState;
exports.tzLegrand = {
    auto_mode: {
        key: ["auto_mode"],
        convertSet: async (entity, key, value, meta) => {
            const mode = utils.getFromLookup(value, { off: 0x00, auto: 0x02, on_override: 0x03 });
            const payload = { data: Buffer.from([mode]) };
            await entity.command("manuSpecificLegrandDevices3", "command0", payload);
            return { state: { auto_mode: value } };
        },
    },
    calibration_mode: (isNLLVSwitch) => {
        return {
            key: ["calibration_mode"],
            convertSet: async (entity, key, value, meta) => {
                const applicableModes = getApplicableCalibrationModes(isNLLVSwitch);
                utils.validateValue(value, Object.values(applicableModes));
                const idx = Number(utils.getKey(applicableModes, value));
                await entity.write("closuresWindowCovering", { calibrationMode: idx }, exports.legrandOptions);
            },
            convertGet: async (entity, key, meta) => {
                await entity.read("closuresWindowCovering", ["calibrationMode"], exports.legrandOptions);
            },
        };
    },
    led_mode: {
        key: ["led_in_dark", "led_if_on"],
        convertSet: async (entity, key, value, meta) => {
            utils.validateValue(key, Object.values(ledModes));
            const idx = utils.getKey(ledModes, key);
            const state = value === "ON" || (value === "OFF" ? false : !!value);
            const payload = { [idx]: { value: state, type: 16 } };
            await entity.write("manuSpecificLegrandDevices", payload, exports.legrandOptions);
            return { state: { [key]: value } };
        },
        convertGet: async (entity, key, meta) => {
            utils.validateValue(key, Object.values(ledModes));
            const idx = utils.getKey(ledModes, key);
            await entity.read("manuSpecificLegrandDevices", [Number(idx)], exports.legrandOptions);
        },
    },
    identify: {
        key: ["identify"],
        options: [optsLegrand.identityEffect()],
        convertSet: async (entity, key, value, meta) => {
            const identityEffect = meta.options.identity_effect;
            const selEffect = identityEffect?.effect ?? ledEffects[0];
            const selColor = identityEffect?.color ?? ledColors[0];
            const effectID = Number.parseInt(utils.getFromLookupByValue(selEffect, ledEffects, "0"), 10);
            const effectVariant = Number.parseInt(utils.getFromLookupByValue(selColor, ledColors, "0"), 10);
            // Trigger an effect
            await entity.command("genIdentify", "triggerEffect", { effectid: effectID, effectvariant: effectVariant }, {});
            // Trigger the identification
            await entity.command("genIdentify", "identify", { identifytime: 10 }, {});
        },
    },
    legrand_device_mode: {
        key: ["device_mode"],
        convertSet: async (entity, key, value, meta) => {
            utils.assertString(value, key);
            // enable the dimmer, requires a recent firmware on the device
            const lookup = {
                dimmer_on: 0x0101,
                dimmer_off: 0x0100,
                switch: 0x0003,
                auto: 0x0004,
                pilot_on: 0x0002,
                pilot_off: 0x0001,
            };
            const val = value.toLowerCase();
            utils.validateValue(val, Object.keys(lookup));
            const payload = { deviceMode: utils.getFromLookup(val, lookup) };
            await entity.write("manuSpecificLegrandDevices", payload, exports.legrandOptions);
            return { state: { device_mode: val } };
        },
        convertGet: async (entity, key, meta) => {
            await entity.read("manuSpecificLegrandDevices", [0x0000, 0x0001, 0x0002], exports.legrandOptions);
        },
    },
    pilot_wire_mode: {
        key: ["pilot_wire_mode"],
        convertSet: async (entity, key, value, meta) => {
            const modeLookup = {
                comfort: 0x00,
                "comfort_-1": 0x01,
                "comfort_-2": 0x02,
                eco: 0x03,
                frost_protection: 0x04,
                off: 0x05,
            };
            utils.validateValue(value, Object.keys(modeLookup));
            const payload = { data: Buffer.from([utils.getFromLookup(value, modeLookup)]) };
            await entity.command("manuSpecificLegrandDevices2", "command0", payload);
            return { state: { pilot_wire_mode: value } };
        },
        convertGet: async (entity, key, meta) => {
            await entity.read("manuSpecificLegrandDevices2", [0x0000], exports.legrandOptions);
        },
    },
    power_alarm: {
        key: ["power_alarm"],
        convertSet: async (entity, key, value, meta) => {
            const enableAlarm = !(value === "DISABLE" || value === false);
            const payloadBolean = { 61441: { value: enableAlarm ? 0x01 : 0x00, type: 0x10 } };
            const payloadValue = { 61442: { value: value, type: 0x29 } };
            await entity.write("haElectricalMeasurement", payloadValue);
            await entity.write("haElectricalMeasurement", payloadBolean);
            // To have consistent information in the system.
            await entity.read("haElectricalMeasurement", [0xf000, 0xf001, 0xf002]);
        },
        convertGet: async (entity, key, meta) => {
            await entity.read("haElectricalMeasurement", [
                0xf000,
                "powerAlarmEnabled",
                "powerAlarmWhThreshold",
            ]);
        },
    },
};
exports.fzLegrand = {
    calibration_mode: (isNLLVSwitch) => {
        return {
            cluster: "closuresWindowCovering",
            type: ["attributeReport", "readResponse"],
            convert: (model, msg, publish, options, meta) => {
                const attr = "calibrationMode";
                if (msg.data[attr] !== undefined) {
                    const applicableModes = getApplicableCalibrationModes(isNLLVSwitch);
                    const idx = msg.data[attr];
                    utils.validateValue(String(idx), Object.keys(applicableModes));
                    const calMode = applicableModes[idx];
                    return { calibration_mode: calMode };
                }
            },
        };
    },
    cluster_fc01: {
        cluster: "manuSpecificLegrandDevices",
        type: ["readResponse"],
        convert: (model, msg, publish, options, meta) => {
            const payload = {};
            if (msg.data.deviceMode !== undefined) {
                const modeLookup = {
                    1: "pilot_off",
                    2: "pilot_on",
                    3: "switch",
                    4: "auto",
                    256: "dimmer_off",
                    257: "dimmer_on",
                };
                const mode = msg.data.deviceMode;
                if (modeLookup[mode] !== undefined) {
                    payload.device_mode = modeLookup[mode];
                }
                else {
                    logger_1.logger.warning(`Device_mode ${mode} not recognized, please fix me!`, NS);
                    payload.device_mode = "unknown";
                }
            }
            if (msg.data.ledInDark !== undefined) {
                payload.led_in_dark = msg.data.ledInDark ? "ON" : "OFF";
            }
            if (msg.data.ledIfOn !== undefined) {
                payload.led_if_on = msg.data.ledIfOn ? "ON" : "OFF";
            }
            return payload;
        },
    },
    stop_poll_on_checkin: {
        cluster: "genPollCtrl",
        type: ["commandCheckin"],
        convert: (model, msg, publish, options, meta) => {
            // TODO current solution is a work around, it would be cleaner to answer to the request
            const endpoint = msg.device.getEndpoint(1);
            endpoint
                .command("genPollCtrl", "fastPollStop", {}, exports.legrandOptions)
                .catch((error) => logger_1.logger.debug(`Failed to stop poll on '${msg.device.ieeeAddr}' (${error})`, NS));
        },
    },
    command_cover: {
        cluster: "closuresWindowCovering",
        type: ["attributeReport", "readResponse"],
        convert: (model, msg, publish, options, meta) => {
            const payload = {};
            if (msg.data.tuyaMovingState !== undefined) {
                if (utils.hasAlreadyProcessedMessage(msg, model))
                    return;
                if (msg.data.tuyaMovingState === 0) {
                    // return {
                    // action: 'open',
                    // };
                    payload.action = utils.postfixWithEndpointName("OPEN", msg, model, meta);
                    utils.addActionGroup(payload, msg, model);
                }
                if (msg.data.tuyaMovingState === 100) {
                    // return {
                    // action: 'closed',
                    // };
                    payload.action = utils.postfixWithEndpointName("CLOSE", msg, model, meta);
                    utils.addActionGroup(payload, msg, model);
                }
                if (msg.data.tuyaMovingState >= 1 && msg.data.tuyaMovingState < 100) {
                    // return {
                    // action: 'stop',
                    // };
                    payload.action = utils.postfixWithEndpointName("STOP", msg, model, meta);
                    utils.addActionGroup(payload, msg, model);
                }
            }
            return payload;
        },
    },
    identify: {
        cluster: "genIdentify",
        type: ["attributeReport", "readResponse"],
        convert: (model, msg, publish, options, meta) => {
            return {};
        },
    },
    master_switch_center: {
        cluster: "manuSpecificLegrandDevices",
        type: "raw",
        convert: (model, msg, publish, options, meta) => {
            const centerSignature = [0x15, 0x21, 0x10, 0x00, 0x03, 0xff];
            if (msg.data?.length === 6 && centerSignature.every((b, i) => msg.data[i] === b)) {
                return { action: utils.postfixWithEndpointName("center", msg, model, meta) };
            }
        },
    },
    pilot_wire_mode: {
        cluster: "manuSpecificLegrandDevices2",
        type: ["readResponse"],
        convert: (model, msg, publish, options, meta) => {
            if (msg.data.pilotWireMode !== undefined) {
                const lookup = {
                    0: "comfort",
                    1: "comfort_-1",
                    2: "comfort_-2",
                    3: "eco",
                    4: "frost_protection",
                    5: "off",
                };
                const mode = msg.data.pilotWireMode;
                if (lookup[mode] !== undefined) {
                    return { pilot_wire_mode: lookup[mode] };
                }
                logger_1.logger.warning(`Pilot_wire_mode ${mode} not recognized, please fix me!`, NS);
                return { pilot_wire_mode: "unknown" };
            }
        },
    },
    binary_input_moving: {
        cluster: "genBinaryInput",
        type: ["attributeReport", "readResponse"],
        convert: (model, msg, publish, options, meta) => {
            return { action: msg.data.presentValue ? "moving" : "stopped" };
        },
    },
    binary_input_on_off: {
        cluster: "genBinaryInput",
        type: ["attributeReport", "readResponse"],
        convert: (model, msg, publish, options, meta) => {
            const multiEndpoint = model.meta?.multiEndpoint;
            const property = multiEndpoint ? utils.postfixWithEndpointName("state", msg, model, meta) : "state";
            return { [property]: msg.data.presentValue ? "ON" : "OFF" };
        },
    },
    scenes: {
        cluster: "genScenes",
        type: "commandRecall",
        convert: (model, msg, publish, options, meta) => {
            const lookup = {
                65527: "enter",
                65526: "leave",
                65524: "sleep",
                65525: "wakeup",
                65518: "ambiance_I",
                65519: "ambiance_II",
                65520: "ambiance_III",
            };
            return { action: lookup[msg.data.groupid] ? lookup[msg.data.groupid] : "default" };
        },
    },
    power_alarm: {
        cluster: "haElectricalMeasurement",
        type: ["attributeReport", "readResponse"],
        convert: (model, msg, publish, options, meta) => {
            const payload = {};
            // 0xf000 = 61440
            // This attribute returns usually 2 when power is over the defined threshold.
            if (msg.data["61440"] !== undefined) {
                payload.power_alarm_active_value = msg.data["61440"];
                payload.power_alarm_active = payload.power_alarm_active_value > 0;
            }
            // 0xf001 = 61441
            if (msg.data.powerAlarmEnabled !== undefined) {
                payload.power_alarm_enabled = msg.data.powerAlarmEnabled;
            }
            // 0xf002 = 61442, wh = watt hour
            if (msg.data.powerAlarmWhThreshold !== undefined) {
                payload.power_alarm_wh_threshold = msg.data.powerAlarmWhThreshold;
            }
            return payload;
        },
    },
    greenpower: {
        cluster: "greenPower",
        type: ["commandNotification", "commandCommissioningNotification"],
        convert: (model, msg, publish, options, meta) => {
            const commandID = msg.data.commandID;
            if (utils.hasAlreadyProcessedMessage(msg, model, msg.data.frameCounter, `${msg.device.ieeeAddr}_${commandID}`))
                return;
            if (commandID >= 0xe0)
                return; // Skip op commands
            if (LEGRAND_GREENPOWER_LOOKUP[commandID] === undefined) {
                logger_1.logger.error(`Legrand GreenPower: missing command '${commandID}'`, NS);
            }
            else {
                return { action: LEGRAND_GREENPOWER_LOOKUP[commandID] };
            }
        },
    },
};
//# sourceMappingURL=legrand.js.map