"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.definitions = void 0;
const fz = __importStar(require("../converters/fromZigbee"));
const exposes = __importStar(require("../lib/exposes"));
const logger_1 = require("../lib/logger");
const m = __importStar(require("../lib/modernExtend"));
const tuya = __importStar(require("../lib/tuya"));
const e = exposes.presets;
// Constants
const MAX_ZONES = 8;
const DEFAULT_GROUP_ID_BASE = 100;
// Default zone to group mapping configuration
const defaultZoneGroupMapping = {
    1: 101,
    2: 102,
    3: 103,
    4: 104,
    5: 105,
    6: 106,
    7: 107,
    8: 108,
};
// Get zone group mapping from device options with fallback to defaults
function getZoneGroupMapping(options = {}) {
    const mapping = { ...defaultZoneGroupMapping };
    for (let i = 1; i <= MAX_ZONES; i++) {
        const optionKey = `zone_${i}_group_id`;
        const optionValue = options[optionKey];
        if (optionValue !== undefined && typeof optionValue === "number" && optionValue > 0) {
            mapping[i] = optionValue;
        }
    }
    return mapping;
}
// Get zone suffix from group ID by checking device options
function getZoneSuffixFromGroupId(groupId, options = {}) {
    if (!groupId)
        return null;
    // Get current zone group mapping from device options
    const zoneGroupMapping = getZoneGroupMapping(options);
    // Find the zone number for the given group ID
    const zoneNumber = Object.keys(zoneGroupMapping).find((zone) => zoneGroupMapping[Number.parseInt(zone, 10)] === groupId);
    // If no zone number is found for this group or if zone actions are disabled, add no suffix
    if (!zoneNumber || options.zone_actions !== true) {
        return "";
    }
    return `_zone_${zoneNumber}`;
}
// Create fromZigbee converter that adds zone suffix to action names
function createZoneAwareConverter(
// biome-ignore lint/suspicious/noExplicitAny: Fz.ConverterTypeStringOrArray is not exported
baseConverter) {
    return {
        cluster: baseConverter.cluster,
        type: baseConverter.type,
        convert: (model, msg, publish, options, meta) => {
            const zoneSuffix = getZoneSuffixFromGroupId(msg.groupID, options);
            if (zoneSuffix === null)
                return undefined;
            // Call the base converter
            const baseResult = baseConverter.convert(model, msg, publish, options, meta);
            if (!baseResult || typeof baseResult !== "object" || !("action" in baseResult))
                return baseResult;
            // Override the action with zone suffix
            return {
                ...baseResult,
                action: `${baseResult.action}${zoneSuffix}`,
            };
        },
    };
}
// Create converters that extract action properties to numeric sensors
const actionPropertyConverters = {
    hue_saturation: {
        cluster: "lightingColorCtrl",
        type: "commandMoveToHueAndSaturation",
        convert: (model, msg, publish, options, meta) => {
            if (!options?.expose_values)
                return undefined;
            return {
                hue: msg.data?.hue,
                saturation: msg.data?.saturation,
            };
        },
    },
    color_temp: {
        cluster: "lightingColorCtrl",
        type: "commandMoveToColorTemp",
        convert: (model, msg, publish, options, meta) => {
            if (!options?.expose_values)
                return undefined;
            return {
                color_temperature: msg.data?.colortemp,
            };
        },
    },
    level: {
        cluster: "genLevelCtrl",
        type: ["commandMoveToLevel", "commandMoveToLevelWithOnOff"],
        convert: (model, msg, publish, options, meta) => {
            if (!options?.expose_values)
                return undefined;
            return {
                level: msg.data?.level,
            };
        },
    },
};
// ModernExtend function for FUT089Z remote control
function miboxerFut089zControls() {
    // Create zone-aware converters using base converters (they will dynamically read device options)
    const onConverter = createZoneAwareConverter(fz.command_on);
    const offConverter = createZoneAwareConverter(fz.command_off);
    const brightnessConverter = createZoneAwareConverter(fz.command_move_to_level);
    const colorTempConverter = createZoneAwareConverter(fz.command_move_to_color_temp);
    const colorConverter = createZoneAwareConverter(fz.command_move_to_hue_and_saturation);
    // biome-ignore lint/suspicious/noExplicitAny: Fz.ConverterTypeStringOrArray is not exported
    const fromZigbee = [
        onConverter,
        offConverter,
        brightnessConverter,
        colorTempConverter,
        colorConverter,
        fz.battery,
        tuya.fz.switch_scene,
        // Add converters for numeric sensors (always included, but they check options internally)
        actionPropertyConverters.hue_saturation,
        actionPropertyConverters.color_temp,
        actionPropertyConverters.level,
    ];
    // Device exposes (battery info and action)
    const exposesList = [e.battery(), e.battery_voltage()];
    // Device options for zone group mapping configuration and numeric sensors
    const deviceOptions = [
        // Generate zone group ID options dynamically
        ...Array.from({ length: MAX_ZONES }, (_, i) => {
            const zoneNum = i + 1;
            return exposes
                .numeric(`zone_${zoneNum}_group_id`, exposes.access.SET)
                .withDescription(`Group ID for zone ${zoneNum} (default: ${DEFAULT_GROUP_ID_BASE + zoneNum})`)
                .withValueMin(1)
                .withValueMax(65535);
        }),
        // Feature toggles
        new exposes.Binary("expose_values", exposes.access.SET, true, false).withDescription("Expose additional numeric values for action properties (hue, saturation, level, etc.)"),
        new exposes.Binary("zone_actions", exposes.access.SET, true, false).withDescription("Publish zone-specific actions with zone IDs (e.g., on_zone_1, off_zone_2)"),
    ];
    // Generate action exposes for all possible zones
    function zoneActions(enableZoneActions) {
        const baseActions = ["on", "off", "brightness_move_to_level", "color_temperature_move", "move_to_hue_and_saturation"];
        if (!enableZoneActions) {
            return baseActions;
        }
        const actions = [];
        for (let zoneNumber = 1; zoneNumber <= MAX_ZONES; zoneNumber++) {
            baseActions.forEach((action) => {
                actions.push(`${action}_zone_${zoneNumber}`);
            });
        }
        return actions;
    }
    // Event handler to update device when zone options change
    const eventHandlers = [
        async (event) => {
            if (event.type !== "deviceOptionsChanged")
                return;
            const { device, from, to } = event.data;
            // Generate zone option keys dynamically
            const zoneOptionKeys = Array.from({ length: MAX_ZONES }, (_, i) => `zone_${i + 1}_group_id`);
            const hasZoneOptionChanged = zoneOptionKeys.some((key) => from[key] !== to[key]);
            if (hasZoneOptionChanged) {
                try {
                    const endpoint = device.getEndpoint(1);
                    if (!endpoint) {
                        throw new Error("Endpoint 1 not found on device");
                    }
                    // Get the updated zone group mapping from new options
                    const updatedMapping = getZoneGroupMapping(to);
                    // Build zone configuration array from the mapping
                    const zoneConfig = Object.entries(updatedMapping).map(([zoneNum, groupId]) => ({
                        zoneNum: Number.parseInt(zoneNum, 10),
                        groupId: groupId,
                    }));
                    // Send the updated zone configuration to the device
                    await endpoint.command("genGroups", "miboxerSetZones", { zones: zoneConfig });
                }
                catch (error) {
                    // Log error but don't throw to avoid breaking the update process
                    logger_1.logger.error(`Failed to update zone configuration: ${error}`, "zhc:miboxer");
                }
            }
            event.data.deviceExposesChanged();
        },
    ];
    return {
        fromZigbee,
        exposes: [
            ...exposesList,
            ((device, options = {}) => {
                const dynamicExposes = [];
                // Add action expose with zone-aware actions
                dynamicExposes.push(e.action(zoneActions(options.zone_actions === true)));
                // Add numeric sensors if enabled in options
                if (options.expose_values === true) {
                    dynamicExposes.push(exposes
                        .numeric("hue", exposes.access.STATE_GET)
                        .withDescription("Hue value from last action")
                        .withValueMin(0)
                        .withValueMax(254), exposes
                        .numeric("saturation", exposes.access.STATE_GET)
                        .withDescription("Saturation value from last action")
                        .withValueMin(0)
                        .withValueMax(254), exposes
                        .numeric("color_temperature", exposes.access.STATE_GET)
                        .withDescription("Color temperature value from last action")
                        .withUnit("mired")
                        .withValueMin(153)
                        .withValueMax(500), exposes
                        .numeric("level", exposes.access.STATE_GET)
                        .withDescription("Level value from last action")
                        .withValueMin(0)
                        .withValueMax(254));
                }
                return dynamicExposes;
            }),
        ],
        options: deviceOptions,
        onEvent: eventHandlers,
        isModernExtend: true,
        configure: [
            async (device, coordinatorEndpoint, definition) => {
                const endpoint = device.getEndpoint(1);
                if (!endpoint) {
                    throw new Error("Endpoint 1 not found on device");
                }
                await tuya.configureMagicPacket(device, coordinatorEndpoint);
                // Use default zone group mapping for initial configuration
                const currentMapping = getZoneGroupMapping();
                // Build zone configuration array from the mapping
                const zoneConfig = Object.entries(currentMapping).map(([zoneNum, groupId]) => ({
                    zoneNum: Number.parseInt(zoneNum, 10),
                    groupId: groupId,
                }));
                await endpoint.command("genGroups", "miboxerSetZones", { zones: zoneConfig });
                await endpoint.command("genBasic", "tuyaSetup", {}, { disableDefaultResponse: true });
            },
        ],
    };
}
exports.definitions = [
    {
        fingerprint: tuya.fingerprint("TS0504B", ["_TZ3210_ttkgurpb"]),
        model: "FUT038Z",
        description: "RGBW LED controller",
        vendor: "MiBoxer",
        extend: [tuya.modernExtend.tuyaLight({ colorTemp: { range: [153, 500] }, color: true })],
    },
    {
        fingerprint: tuya.fingerprint("TS1002", ["_TZ3000_xwh1e22x", "_TZ3000_zwszqdpy"]),
        model: "FUT089Z",
        vendor: "MiBoxer",
        description: "RGB+CCT Remote",
        whiteLabel: [tuya.whitelabel("Ledron", "YK-16", "RGB+CCT Remote", ["_TZ3000_zwszqdpy"])],
        extend: [
            tuya.clusters.addTuyaGenGroupsCluster(),
            tuya.clusters.addTuyaGenOnOffCluster(),
            miboxerFut089zControls(),
            m.quirkCheckinInterval(21600), // Device observed to report every 4h, set to 6h (21600s) for safety margin
        ],
    },
];
//# sourceMappingURL=miboxer.js.map