"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var ColorControlServer_exports = {};
__export(ColorControlServer_exports, {
  ColorControlBaseServer: () => ColorControlBaseServer,
  ColorControlServer: () => ColorControlServer
});
module.exports = __toCommonJS(ColorControlServer_exports);
var import_Transitions = require("#behavior/Transitions.js");
var import_general_diagnostics = require("#behaviors/general-diagnostics");
var import_on_off = require("#behaviors/on-off");
var import_scenes_management = require("#behaviors/scenes-management");
var import_color_control = require("#clusters/color-control");
var import_general_diagnostics2 = require("#clusters/general-diagnostics");
var import_aggregator = require("#endpoints/aggregator");
var import_general = require("#general");
var import_ServerNode = require("#node/ServerNode.js");
var import_protocol = require("#protocol");
var import_types = require("#types");
var import_ColorControlBehavior = require("./ColorControlBehavior.js");
var import_ColorConversionUtils = require("./ColorConversionUtils.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("ColorControlServer");
const ColorControlBase = import_ColorControlBehavior.ColorControlBehavior.with(
  import_color_control.ColorControl.Feature.HueSaturation,
  import_color_control.ColorControl.Feature.EnhancedHue,
  import_color_control.ColorControl.Feature.ColorLoop,
  import_color_control.ColorControl.Feature.Xy,
  import_color_control.ColorControl.Feature.ColorTemperature
);
const MIN_CIE_XY_VALUE = 0;
const MAX_CIE_XY_VALUE = 65279;
const MIN_TEMPERATURE_VALUE = 0;
const MAX_TEMPERATURE_VALUE = 65279;
const MIN_HUE_VALUE = 0;
const MAX_HUE_VALUE = 254;
const MAX_ENHANCED_HUE_VALUE = 65535;
const MIN_SATURATION_VALUE = 0;
const MAX_SATURATION_VALUE = 254;
const MIN_CURRENT_LEVEL = 1;
const MAX_CURRENT_LEVEL = 254;
class ColorControlBaseServer extends ColorControlBase {
  /*
   * The following block contains some convenience methods to allow to easily work with the CIE color space values
   * as usual and hide the matter specific data model.
   */
  /** Returns the current X value as decimal number according to CIE in the range 0..1. */
  get x() {
    return this.#returnAsXyValue(this.state.currentX);
  }
  /**
   * Sets the current X value as decimal number according to CIE in the range 0..1 and converts into Matter number
   * range.
   */
  set x(value) {
    this.#setFromXyValue("currentX", value);
  }
  /** Returns the current Y value as decimal number according to CIE in the range 0..1. */
  get y() {
    return this.#returnAsXyValue(this.state.currentY);
  }
  /**
   * Sets the current Y value as decimal number according to CIE in the range 0..1 and converts into Matter number
   * range.
   */
  set y(value) {
    this.#setFromXyValue("currentY", value);
  }
  /**
   * Returns the current hue value as decimal number in the range 0..360.
   */
  get hue() {
    return this.state.currentHue * 360 / 254;
  }
  /**
   * Sets the current hue value as decimal number in the range 0..360 and converts into Matter number range.
   */
  set hue(value) {
    this.state.currentHue = (0, import_general.cropValueRange)(Math.round(value * 254 / 360), MIN_HUE_VALUE, MAX_HUE_VALUE);
  }
  /** Returns the current enhanced hue value as decimal number in the range 0..0xFFFF. */
  get enhancedHue() {
    return this.state.enhancedCurrentHue;
  }
  /**
   * Sets the current enhanced hue value as decimal number in the range 0..0xFFFF and converts into Matter number range.
   */
  set enhancedHue(value) {
    this.state.enhancedCurrentHue = (0, import_general.cropValueRange)(value, MIN_HUE_VALUE, MAX_ENHANCED_HUE_VALUE);
  }
  /** Returns the current saturation value as decimal number in the range 0..1. */
  get saturation() {
    return this.state.currentSaturation / 254;
  }
  /**
   * Sets the current saturation value as decimal number in the range 0..1 and converts into Matter number range.
   */
  set saturation(value) {
    this.state.currentSaturation = (0, import_general.cropValueRange)(
      Math.round(value * 254),
      MIN_SATURATION_VALUE,
      MAX_SATURATION_VALUE
    );
  }
  /** Returns the current color temperature value in Mireds (0..0xFFFF). */
  get mireds() {
    return this.state.colorTemperatureMireds;
  }
  /**
   * Sets the current color temperature value in Mireds (0..0xFFFF) and converts into Matter number range.
   */
  set mireds(value) {
    this.state.colorTemperatureMireds = this.#cropColorTemperature(value);
  }
  /** Returns the current color temperature value in Kelvin (from internal Mireds). */
  get kelvin() {
    return (0, import_ColorConversionUtils.miredsToKelvin)(this.mireds);
  }
  /** Sets the current color temperature value in Kelvin and updates the internal mireds value. */
  set kelvin(value) {
    this.mireds = (0, import_ColorConversionUtils.kelvinToMireds)(value);
  }
  /**
   * Returns the minimum color temperature value in Mireds (0..0xFFFF). This automatically handled the case that no
   * physical minimum value is defined.
   */
  get minimumColorTemperatureMireds() {
    return this.state.colorTempPhysicalMinMireds === 0 ? MIN_TEMPERATURE_VALUE : this.state.colorTempPhysicalMinMireds;
  }
  /**
   * Returns the maximum color temperature value in Mireds (0..0xFFFF). This automatically handled the case that no
   * physical maximum value is defined.
   */
  get maximumColorTemperatureMireds() {
    return this.state.colorTempPhysicalMaxMireds === 0 ? MAX_TEMPERATURE_VALUE : this.state.colorTempPhysicalMaxMireds;
  }
  /** Returns the current whitePoint X value as decimal number according to CIE in the range 0..1. */
  get whitePointX() {
    return this.#returnAsOptionalXyValue(this.state.whitePointX);
  }
  /**
   * Sets the current whitePoint X value as decimal number according to CIE in the range 0..1 and converts into Matter
   * number range.
   */
  set whitePointX(value) {
    this.#setFromXyValue("whitePointX", value);
  }
  /** Returns the current whitePoint Y value as decimal number according to CIE in the range 0..1. */
  get whitePointY() {
    return this.#returnAsOptionalXyValue(this.state.whitePointY);
  }
  /**
   * Sets the current whitePoint Y value as decimal number according to CIE in the range 0..1 and converts into Matter
   * number range.
   */
  set whitePointY(value) {
    this.#setFromXyValue("whitePointY", value);
  }
  /** Returns the current colorPoint Rx value as decimal number according to CIE in the range 0..1. */
  get colorPointRx() {
    return this.#returnAsOptionalXyValue(this.state.colorPointRx);
  }
  /**
   * Sets the current colorPoint Rx value as decimal number according to CIE in the range 0..1 and converts into Matter
   * number range.
   */
  set colorPointRx(value) {
    this.#setFromXyValue("colorPointRx", value);
  }
  /** Returns the current colorPoint Ry value as decimal number according to CIE in the range 0..1. */
  get colorPointRy() {
    return this.#returnAsOptionalXyValue(this.state.colorPointRy);
  }
  /**
   * Sets the current colorPoint Ry value as decimal number according to CIE in the range 0..1 and converts into Matter
   * number range.
   */
  set colorPointRy(value) {
    this.#setFromXyValue("colorPointRy", value);
  }
  /** Returns the current colorPoint Gx value as decimal number according to CIE in the range 0..1. */
  get colorPointGx() {
    return this.#returnAsOptionalXyValue(this.state.colorPointGx);
  }
  /**
   * Sets the current colorPoint Gx value as decimal number according to CIE in the range 0..1 and converts into Matter
   * number range.
   */
  set colorPointGx(value) {
    this.#setFromXyValue("colorPointGx", value);
  }
  /** Returns the current colorPoint Gy value as decimal number according to CIE in the range 0..1. */
  get colorPointGy() {
    return this.#returnAsOptionalXyValue(this.state.colorPointGy);
  }
  /**
   * Sets the current colorPoint Gy value as decimal number according to CIE in the range 0..1 and converts into Matter
   * number range.
   */
  set colorPointGy(value) {
    this.#setFromXyValue("colorPointGy", value);
  }
  /** Returns the current colorPoint Bx value as decimal number according to CIE in the range 0..1. */
  get colorPointBx() {
    return this.#returnAsOptionalXyValue(this.state.colorPointBx);
  }
  /**
   * Sets the current colorPoint Bx value as decimal number according to CIE in the range 0..1 and converts into Matter
   * number range.
   */
  set colorPointBx(value) {
    this.#setFromXyValue("colorPointBx", value);
  }
  initialize() {
    if (this.events.remainingTime$Changed !== void 0) {
      this.events.remainingTime$Changed.quiet.config = {
        suppressionEnabled: false
      };
    }
    this.internal.transitions = this.#initializeTransitions();
    this.state.colorCapabilities = this.features;
    if (this.features.colorTemperature) {
      this.initializeColorTemperature();
    }
    this.agent.maybeGet(import_scenes_management.ScenesManagementServer)?.implementScenes(this, this.#applySceneValues);
  }
  /**
   * Initialize Color Temperature features.
   *
   * This only applies if the Color Control cluster has the "CT" feature enabled.
   */
  initializeColorTemperature() {
    if (this.#getBootReason() !== import_general_diagnostics2.GeneralDiagnostics.BootReason.SoftwareUpdateCompleted && !this.endpoint.ownerOfType(import_aggregator.AggregatorEndpoint)) {
      const startUpMiredsValue = this.state.startUpColorTemperatureMireds ?? null;
      if (startUpMiredsValue !== null) {
        const currentMiredsValue = this.#cropColorTemperature(this.state.colorTemperatureMireds);
        const targetMiredsValue = this.#cropColorTemperature(startUpMiredsValue);
        if (targetMiredsValue !== currentMiredsValue) {
          this.state.colorMode = import_color_control.ColorControl.ColorMode.ColorTemperatureMireds;
          this.state.enhancedColorMode = import_color_control.ColorControl.EnhancedColorMode.ColorTemperatureMireds;
          this.state.colorTemperatureMireds = targetMiredsValue;
        }
      }
    }
  }
  /**
   * Default implementation notes:
   * This method ignores the transition time provided by the command and just sets the hue value to the
   * requested value. After the options and value checks and the potential color mode switch it uses the
   * {@link moveToHueLogic} method to set the hue. If you want to implement own logic just override
   * {@link moveToHueLogic} with is also used for {@link enhancedMoveToHue}, {@link moveToHueAndSaturation} and
   * {@link enhancedMoveToHueAndSaturation}.
   */
  moveToHue({
    optionsMask,
    optionsOverride,
    hue,
    direction,
    transitionTime
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation),
      () => this.moveToHueLogic(hue, direction, transitionTime)
    );
  }
  /**
   * Default implementation of the moveToHue logic. When a transition time is specified the implementation uses a
   * step based to manage the move. The method handled hue and enhanced-Hue changes depending on the last parameter.
   *
   * @param targetHue The target hue value to move to (in range of the matter hue or enhanced hue ranges)
   * @param direction The direction to move to the target hue
   * @param transitionTime The time in seconds to move to the target hue. 0 means "as fast as possible"
   * @param isEnhancedHue If true the enhanced hue is handled, otherwise the normal hue
   * @protected
   */
  moveToHueLogic(targetHue, direction, transitionTime, isEnhancedHue = false) {
    const currentHue = isEnhancedHue ? this.state.enhancedCurrentHue : this.state.currentHue;
    const maxHue = isEnhancedHue ? MAX_ENHANCED_HUE_VALUE : MAX_HUE_VALUE;
    const effectiveRate = this.#getHueDistanceByDirection(currentHue, targetHue, direction, maxHue) / transitionTime * 10;
    return this.#startTransition({
      name: isEnhancedHue ? "enhancedCurrentHue" : "currentHue",
      owner: this,
      changePerS: effectiveRate,
      targetValue: targetHue,
      calculateCyclicDistance: (current, target) => this.#getHueDistanceByDirection(current, target, direction, maxHue)
    });
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveHueLogic} method to set the hue.
   * If you want to implement own logic just override {@link moveHueLogic} with is also used for {@link enhancedMoveHue}.
   * If a currently running movement should be stopped {@link stopHueAndSaturationMovement} is used which can also be
   * overridden.
   * The logic is implemented as follows: Depending on the provided rate the server will move as fast as possible, but
   * increase only one rate directly if the managed transition is not used. Else the step logic is applied and the
   * level is increased or decreased by the step size every second.
   */
  moveHue({ optionsMask, optionsOverride, moveMode, rate }) {
    this.#assertRate(moveMode, rate);
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation), () => {
      if (moveMode === import_color_control.ColorControl.MoveMode.Stop) {
        return this.stopHueAndSaturationMovement();
      }
      return this.moveHueLogic(moveMode, rate);
    });
  }
  /**
   * Default implementation of the moveHue logic. The method handled hue and enhanced-Hue changes depending on the
   * last parameter.
   * If the managed transition time handling is disabled the method directly increases the hue value by one rate step.
   * So without managed transition not too much happens.
   * Otherwise, the method initiates a transition with the given rate.
   *
   * @param moveMode Move Mode to move the hue up or down (Stop should not be provided in here because already handled)
   * @param rate The rate to move the hue up or down. 0 should never be provided her because handled here.
   * @param isEnhancedHue If true the enhanced hue is handled, otherwise the normal hue
   * @protected
   */
  moveHueLogic(moveMode, rate, isEnhancedHue = false) {
    const name = isEnhancedHue ? "enhancedCurrentHue" : "currentHue";
    return this.#startTransition({
      name,
      owner: this,
      changePerS: rate * (moveMode === import_color_control.ColorControl.MoveMode.Up ? 1 : -1)
    });
  }
  /**
   * Default implementation notes:
   * This method is called to stop any hue, enhanced hue and/or saturation movements potentially in progress. Override
   * this method with hardware specific logic to implement this functionality.
   * @protected
   */
  stopHueAndSaturationMovement() {
    this.internal.transitions?.stop("currentHue");
    this.internal.transitions?.stop("enhancedCurrentHue");
    this.internal.transitions?.stop("currentSaturation");
  }
  #assertStepSize(stepSize, errorContext) {
    if (stepSize === 0) {
      throw new import_types.StatusResponseError(`${errorContext} step size must not be 0`, import_types.StatusCode.InvalidCommand);
    }
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link stepHueLogic} method to set the hue.
   * If you want to implement own logic just override {@link stepHueLogic} with is also used for {@link enhancedStepHue}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set to min/max directly. Else the step logic is applied and the hue is increased or decreased
   * by the step size every transition time interval.
   */
  stepHue({
    optionsMask,
    optionsOverride,
    stepMode,
    stepSize,
    transitionTime
  }) {
    this.#assertStepSize(stepSize, "Hue");
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation),
      () => this.stepHueLogic(stepMode, stepSize, transitionTime)
    );
  }
  /**
   * Default implementation of the stepHue logic. The method handled hue and enhanced-Hue changes depending on the
   * last parameter.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set to current Hue +/- stepSize directly. Else the step logic is applied and the hue is increased
   * or decreased by the step size every transition time interval.
   * @param stepMode The step mode to move the hue up or down
   * @param stepSize The step size to move the hue up or down
   * @param transitionTime The time in seconds to move the hue. 0 means "as fast as possible"
   * @param isEnhancedHue If true the enhanced hue is handled, otherwise the normal hue
   * @protected
   */
  stepHueLogic(stepMode, stepSize, transitionTime, isEnhancedHue = false) {
    const currentHue = isEnhancedHue ? this.state.enhancedCurrentHue : this.state.currentHue;
    const maxHue = isEnhancedHue ? MAX_ENHANCED_HUE_VALUE : MAX_HUE_VALUE;
    const direction = stepMode === import_color_control.ColorControl.StepMode.Up ? 1 : -1;
    const effectiveRate = stepSize / transitionTime * 10 * direction;
    const targetValue = (0, import_general.addValueWithOverflow)(currentHue, stepSize * direction, MIN_HUE_VALUE, maxHue);
    return this.#startTransition({
      name: isEnhancedHue ? "enhancedCurrentHue" : "currentHue",
      owner: this,
      changePerS: effectiveRate,
      targetValue,
      calculateCyclicDistance: (current, target) => this.#getHueDistanceByDirection(
        current,
        target,
        stepMode === import_color_control.ColorControl.StepMode.Up ? import_color_control.ColorControl.Direction.Up : import_color_control.ColorControl.Direction.Down,
        maxHue
      )
    });
  }
  /**
   * Default implementation notes:
   * After the options checks and potentially switching the color mode it uses the {@link moveToSaturationLogic}
   * method to set the hue.
   * If you want to implement own logic just override {@link moveToSaturationLogic} with is also used for
   * {@link moveToHueAndSaturation}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target value directly. Else the step logic is applied and the saturation is increased or
   * decreased by the step size every transition time interval.
   */
  moveToSaturation({
    optionsMask,
    optionsOverride,
    saturation,
    transitionTime
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    this.#invalidateScenes();
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation),
      () => this.moveToSaturationLogic(saturation, transitionTime)
    );
  }
  /**
   * Default implementation of the moveToSaturation logic.
   * If the managed transition time handling is disabled the method directly sets the new saturation value.
   * Otherwise, the method initiates a transition with the given rate.
   *
   * @param targetSaturation The target saturation value to move to
   * @param transitionTime The time in seconds to move to the target saturation. 0 means "as fast as possible"
   * @protected
   */
  moveToSaturationLogic(targetSaturation, transitionTime) {
    const effectiveRate = (targetSaturation - this.state.currentSaturation) / transitionTime * 10;
    return this.#startTransition({
      name: "currentSaturation",
      owner: this,
      changePerS: effectiveRate,
      targetValue: targetSaturation
    });
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveSaturationLogic} method to set the hue.
   * If you want to implement own logic just override {@link moveSaturationLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set to min/max directly. Else the step logic is applied and the saturation is increased or
   * decreased by the step size every transition time interval.
   */
  moveSaturation({ optionsMask, optionsOverride, moveMode, rate }) {
    this.#assertRate(moveMode, rate);
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation), () => {
      if (moveMode === import_color_control.ColorControl.MoveMode.Stop) {
        return this.stopHueAndSaturationMovement();
      }
      return this.moveSaturationLogic(moveMode, rate);
    });
  }
  /**
   * Default implementation of the moveSaturation logic.
   * If the managed transition time handling is disabled the method directly sets the new saturation value.
   * Otherwise the method initiates a transition with the given rate.
   *
   * @param moveMode Move Mode to move the saturation up or down
   * @param rate The rate to move the saturation up or down
   * @protected
   */
  moveSaturationLogic(moveMode, rate) {
    return this.#startTransition({
      name: "currentSaturation",
      owner: this,
      changePerS: rate * (moveMode === import_color_control.ColorControl.MoveMode.Up ? 1 : -1)
    });
  }
  /**
   * Default implementation notes:
   * After the options checks and potentially switching the color mode it uses the {@link stepSaturationLogic}
   * method to set the hue.
   * If you want to implement own logic just override {@link stepSaturationLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target values directly. Else the step logic is applied and the hue and saturation are
   * increased or decreased by the step size every transition time interval.
   */
  stepSaturation({
    optionsMask,
    optionsOverride,
    stepMode,
    stepSize,
    transitionTime
  }) {
    this.#assertStepSize(stepSize, "Saturation");
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation),
      () => this.stepSaturationLogic(stepMode, stepSize, transitionTime)
    );
  }
  /**
   * Default implementation of the stepSaturation logic.
   * If the managed transition time handling is disabled the method directly sets the new saturation value.
   * Otherwise the method initiates a transition with the given rate.
   *
   * @param stepMode The step mode to move the saturation up or down
   * @param stepSize The step size to move the saturation up or down
   * @param transitionTime The time in seconds to move the saturation. 0 means "as fast as possible"
   * @protected
   */
  stepSaturationLogic(stepMode, stepSize, transitionTime) {
    const direction = stepMode === import_color_control.ColorControl.StepMode.Up ? 1 : -1;
    const effectiveRate = stepSize / transitionTime * 10 * direction;
    return this.#startTransition({
      name: "currentSaturation",
      owner: this,
      changePerS: effectiveRate,
      targetValue: this.state.currentSaturation + stepSize * direction
    });
  }
  /**
   * Default implementation notes:
   * After the options checks and potentially switching the color mode it uses the {@link moveToHueAndSaturationLogic}
   * method to set the hue.
   * If you want to implement own logic just override {@link moveToHueAndSaturationLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target values directly. Else the step logic is applied and the hue and saturation is
   * increased or decreased by the step size every transition time interval.
   */
  moveToHueAndSaturation({
    optionsOverride,
    optionsMask,
    saturation,
    hue,
    transitionTime
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    this.#invalidateScenes();
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation),
      () => this.moveToHueAndSaturationLogic(hue, saturation, transitionTime)
    );
  }
  /**
   * Default implementation of the moveToHueAndSaturation logic.
   * If the managed transition time handling is disabled the method directly sets the new hue and saturation values.
   * Otherwise, the method initiates a transition with the given rate.
   * This method internally uses {@link moveToHueLogic} and {@link moveToSaturationLogic} to handle the hue and
   * saturation changes, so if you have implemented them already you might not need to override this method.
   *
   * @param targetHue The target hue value to move to
   * @param targetSaturation The target saturation value to move to
   * @param transitionTime The time in seconds to move to the target hue and saturation. 0 means "as fast as possible"
   * @protected
   */
  moveToHueAndSaturationLogic(targetHue, targetSaturation, transitionTime) {
    return import_general.MaybePromise.then(
      this.moveToHueLogic(targetHue, import_color_control.ColorControl.Direction.Shortest, transitionTime, false),
      () => this.moveToSaturationLogic(targetSaturation, transitionTime)
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveToColorLogic} method to set the x and y value.
   * If you want to implement own logic just override {@link moveToColorLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target values directly. Else the step logic is applied and the x and y is
   * increased or decreased by the step size every transition time interval.
   */
  moveToColor({
    optionsMask,
    optionsOverride,
    colorX,
    colorY,
    transitionTime
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    this.#invalidateScenes();
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentXAndCurrentY),
      () => this.moveToColorLogic(colorX, colorY, transitionTime)
    );
  }
  /**
   * Default implementation of the moveToColor logic.
   * If the managed transition time handling is disabled the method directly sets the new x and y values.
   * Otherwise, the method initiates a transition with the given rate.
   * This method internally uses {@link moveToColorLogic} to handle the x and y changes, so if you have implemented it
   * already you might not need to override this method.
   *
   * @param targetX The target x value to move to
   * @param targetY The target y value to move to
   * @param transitionTime The time in seconds to move to the target x and y. 0 means "as fast as possible"
   * @protected
   */
  moveToColorLogic(targetX, targetY, transitionTime) {
    return import_general.MaybePromise.then(
      this.#startTransition({
        name: "currentX",
        owner: this,
        changePerS: (targetX - this.state.currentX) / transitionTime * 10,
        targetValue: targetX
      }),
      () => this.#startTransition({
        name: "currentY",
        owner: this,
        changePerS: (targetY - this.state.currentY) / transitionTime * 10,
        targetValue: targetY
      })
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveColorLogic} method to set the x and y value.
   * If you want to implement own logic just override {@link moveColorLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target values directly. Else the step logic is applied and the x and y is
   * increased or decreased by the step size every transition time interval.
   */
  moveColor({ optionsOverride, optionsMask, rateX, rateY }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    if (rateX === 0 && rateY === 0) {
      return this.stopAllColorMovement();
    }
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentXAndCurrentY),
      () => this.moveColorLogic(rateX, rateY)
    );
  }
  /**
   * Default implementation notes:
   * This method is called to stop any color movements potentially in progress, including any x/y, hue, saturation and
   * color temperature transitions. Override this method with hardware specific logic to implement this functionality.
   * @protected
   */
  stopAllColorMovement() {
    this.internal.transitions?.stop("currentX");
    this.internal.transitions?.stop("currentY");
    this.internal.transitions?.stop("colorTemperatureMireds");
    return this.stopHueAndSaturationMovement();
  }
  /**
   * Default implementation of the moveColor logic.
   * If the managed transition time handling is disabled the method directly sets the new x and y values.
   * Otherwise, the method initiates a transition with the given rate.
   *
   * @param rateX The rate to move the x value up or down (positive values mean up, negative down)
   * @param rateY The rate to move the y value up or down (positive values mean up, negative down)
   * @protected
   */
  moveColorLogic(rateX, rateY) {
    return import_general.MaybePromise.then(
      () => {
        if (rateX !== 0) {
          return this.#startTransition({
            name: "currentX",
            owner: this,
            changePerS: rateX
          });
        }
      },
      () => {
        if (rateY !== 0) {
          return this.#startTransition({
            name: "currentY",
            owner: this,
            changePerS: rateY
          });
        }
      }
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link stepColorLogic} method to set the x and y value.
   * If you want to implement own logic just override {@link stepColorLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target values directly. Else the step logic is applied and the x and y is
   * increased or decreased by the step size every transition time interval.
   */
  stepColor({
    optionsOverride,
    optionsMask,
    stepX,
    stepY,
    transitionTime
  }) {
    if (stepX === 0 && stepY === 0) {
      throw new import_types.StatusResponseError("Color step sizes must not be 0", import_types.StatusCode.InvalidCommand);
    }
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentXAndCurrentY),
      () => this.stepColorLogic(stepX, stepY, transitionTime)
    );
  }
  /**
   * Default implementation of the stepColor logic.
   * If the managed transition time handling is disabled the method directly sets the new x and y values.
   * Otherwise, the method initiates a transition with the given rate.
   *
   * @param stepX The step size to move the x value up or down
   * @param stepY The step size to move the y value up or down
   * @param transitionTime The time in seconds to move the x and y. 0 means "as fast as possible"
   * @protected
   */
  stepColorLogic(stepX, stepY, transitionTime) {
    return import_general.MaybePromise.then(
      () => {
        if (stepX !== 0) {
          return this.#startTransition({
            name: "currentX",
            owner: this,
            changePerS: stepX / transitionTime * 10,
            targetValue: this.state.currentX + stepX
          });
        }
      },
      () => {
        if (stepY !== 0) {
          return this.#startTransition({
            name: "currentY",
            owner: this,
            changePerS: stepY / transitionTime * 10,
            targetValue: this.state.currentY + stepY
          });
        }
      }
    );
  }
  /**
   * Default implementation notes:
   * After the option checks it uses the {@link moveToColorTemperatureLogic} method to set the color temperature.
   * If you want to implement own logic just override {@link moveToColorTemperatureLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target value directly. Else the step logic is applied and the color temperature is
   * increased or decreased by the step size every transition time interval.
   */
  moveToColorTemperature({
    optionsOverride,
    optionsMask,
    colorTemperatureMireds,
    transitionTime
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setColorMode(import_color_control.ColorControl.ColorMode.ColorTemperatureMireds),
      () => this.moveToColorTemperatureLogic(colorTemperatureMireds, transitionTime)
    );
  }
  /**
   * Default implementation of the moveToColorTemperature logic.
   * If the managed transition time handling is disabled the method directly sets the new color temperature value.
   * Otherwise the method initiates a transition with the given rate.
   *
   * @param targetMireds The target color temperature value to move to
   * @param transitionTime The time in seconds to move to the target color temperature. 0 means "as fast as possible"
   * @protected
   */
  moveToColorTemperatureLogic(targetMireds, transitionTime) {
    return this.#startTransition({
      name: "colorTemperatureMireds",
      owner: this,
      changePerS: (targetMireds - this.state.colorTemperatureMireds) / transitionTime * 10,
      targetValue: targetMireds
    });
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveToHueLogic} method to set the enhanced hue.
   * If you want to implement own logic just override {@link moveToHueLogic} which is used together with
   * {@link moveToHue}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target value directly. Else the step logic is applied and the color temperature is
   * increased or decreased by the step size every transition time interval.
   */
  enhancedMoveToHue({
    optionsMask,
    optionsOverride,
    enhancedHue,
    direction,
    transitionTime
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setEnhancedColorMode(import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation),
      () => this.moveToHueLogic(enhancedHue, direction, transitionTime, true)
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveHueLogic} method to set the enhanced hue.
   * If you want to implement own logic just override {@link moveHueLogic} which is used together with
   * {@link moveHue}.
   * The logic is implemented as follows: Depending on the provided rate the server will move as fast as possible, but
   * increase only one rate directly if the managed transition is not used. Else the step logic is applied and the
   * level is increased or decreased by the step size every second.
   */
  enhancedMoveHue({ optionsMask, optionsOverride, moveMode, rate }) {
    this.#assertRate(moveMode, rate);
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setEnhancedColorMode(import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation),
      () => {
        if (moveMode === import_color_control.ColorControl.MoveMode.Stop) {
          return this.stopHueAndSaturationMovement();
        }
        return this.moveHueLogic(moveMode, rate, true);
      }
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link stepHueLogic} method to set the enhanced hue.
   * If you want to implement own logic just override {@link stepHueLogic} which is used together with
   * {@link stepHue}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target value directly. Else the step logic is applied and the hue is increased or
   * decreased by the step size every transition time interval.
   */
  enhancedStepHue({
    optionsMask,
    optionsOverride,
    stepMode,
    stepSize,
    transitionTime
  }) {
    this.#assertStepSize(stepSize, "Enhanced Hue");
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(
      this.setEnhancedColorMode(import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation),
      () => this.stepHueLogic(stepMode, stepSize, transitionTime, true)
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveToEnhancedHueAndSaturationLogic} method to set the enhanced hue
   * and saturation.
   * If you want to implement own logic just override {@link moveToEnhancedHueAndSaturationLogic}.
   * The logic is implemented as follows: Depending on the provided rate the server will move as fast as possible, so
   * we set to min/max directly if the managed transition is not used. Else the step logic is applied and the
   * level is increased or decreased by the step size every second.
   */
  enhancedMoveToHueAndSaturation({
    optionsOverride,
    optionsMask,
    enhancedHue,
    saturation,
    transitionTime
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    this.#invalidateScenes();
    return import_general.MaybePromise.then(
      this.setEnhancedColorMode(import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation),
      () => this.moveToEnhancedHueAndSaturationLogic(enhancedHue, saturation, transitionTime)
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link moveToHueLogic} and {@link moveToSaturationLogic} methods to set the
   * hue and saturation.
   * Both methods are also used for other commands, so it maybe is not needed to override this method.
   * The logic is implemented as follows: Depending on the provided rate the server will move as fast as possible, so
   * we set to min/max directly if the managed transition is not used. Else the step logic is applied and the
   * level is increased or decreased by the step size every second.
   */
  moveToEnhancedHueAndSaturationLogic(targetEnhancedHue, targetSaturation, transitionTime) {
    return import_general.MaybePromise.then(
      this.moveToHueLogic(targetEnhancedHue, import_color_control.ColorControl.Direction.Shortest, transitionTime, true),
      () => this.moveToSaturationLogic(targetSaturation, transitionTime)
    );
  }
  /**
   * Default implementation notes:
   * After the options checks and potentially switching the color mode it uses the {@link stopColorLoopLogic}
   * method to stop color loops or {@link startColorLoopLogic} to start a color loop at a defined enhanced hue value.
   * If you want to implement own logic just override these methods.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target value directly. Else the step logic is applied and the color temperature is
   * increased or decreased by the step size every transition time interval.
   */
  colorLoopSet({
    optionsMask,
    optionsOverride,
    updateFlags,
    action,
    direction,
    time,
    startHue
  }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    let loopConfigChanged = false;
    if (updateFlags.updateDirection) {
      if (this.state.colorLoopDirection !== direction) {
        this.state.colorLoopDirection = direction;
        loopConfigChanged = true;
      }
    }
    if (updateFlags.updateTime) {
      if (this.state.colorLoopTime !== time) {
        this.state.colorLoopTime = time;
        loopConfigChanged = true;
      }
    }
    if (updateFlags.updateStartHue) {
      this.state.colorLoopStartEnhancedHue = startHue;
    }
    this.#invalidateScenes();
    if (updateFlags.updateAction) {
      if (action === import_color_control.ColorControl.ColorLoopAction.Deactivate) {
        if (this.state.colorLoopActive === import_color_control.ColorControl.ColorLoopActive.Active) {
          return this.#stopColorLoop();
        }
      } else {
        return import_general.MaybePromise.then(
          this.setEnhancedColorMode(import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation),
          () => {
            this.state.colorLoopStoredEnhancedHue = this.state.enhancedCurrentHue;
            this.state.colorLoopActive = import_color_control.ColorControl.ColorLoopActive.Active;
            if (action === import_color_control.ColorControl.ColorLoopAction.ActivateFromColorLoopStartEnhancedHue) {
              return this.startColorLoopLogic(this.state.colorLoopStartEnhancedHue);
            } else if (action === import_color_control.ColorControl.ColorLoopAction.ActivateFromEnhancedCurrentHue) {
              return this.startColorLoopLogic(this.state.enhancedCurrentHue);
            }
          }
        );
      }
    } else if (loopConfigChanged && this.state.colorLoopActive === import_color_control.ColorControl.ColorLoopActive.Active) {
      return this.startColorLoopLogic(this.state.enhancedCurrentHue);
    }
  }
  /**
   * Internal method to step the color loop and sync the internal ColorLoopActive state and restore the stored
   * enhanced hue. It uses {@link stopColorLoopLogic} to execute the stop.
   */
  #stopColorLoop() {
    return import_general.MaybePromise.then(this.stopColorLoopLogic(), () => {
      if (this.state.colorLoopActive === import_color_control.ColorControl.ColorLoopActive.Active) {
        this.state.colorLoopActive = import_color_control.ColorControl.ColorLoopActive.Inactive;
        this.state.enhancedCurrentHue = this.state.colorLoopStoredEnhancedHue;
      }
    });
  }
  /**
   * Default implementation notes:
   * Implement stopping the color loop here if any special hardware specific logic is needed.
   * @protected
   */
  stopColorLoopLogic() {
    this.internal.transitions?.stop("enhancedCurrentHue");
  }
  /**
   * Default implementation notes:
   * This method uses {@link moveHueLogic} to start a enhanced color loop and sets the required start hue beforehand.
   * If this is sufficient for your implementation you might not need to override this method.
   *
   * @param startHue The hue to start the color loop at
   * @protected
   */
  startColorLoopLogic(startHue) {
    this.state.enhancedCurrentHue = startHue;
    return this.moveHueLogic(
      this.state.colorLoopDirection === import_color_control.ColorControl.ColorLoopDirection.Decrement ? import_color_control.ColorControl.MoveMode.Down : import_color_control.ColorControl.MoveMode.Up,
      Math.floor(MAX_ENHANCED_HUE_VALUE / this.state.colorLoopTime),
      true
    );
  }
  /**
   * Default implementation notes:
   * After the options checks it uses the {@link stopMoveStepLogic} method to stop any ongoing color movement.
   * If you want to implement own logic just override {@link stopMoveStepLogic}.
   */
  stopMoveStep({ optionsOverride, optionsMask }) {
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return this.stopMoveStepLogic();
  }
  /**
   * Default implementation notes:
   * Implement stopping any ongoing color movement here if any special hardware specific logic is needed. In fact this
   * needs to stop any ongoing saturation, color temperature, x or y movement. Hue transitions should only be stopped
   * when they do not belong to a color loop.
   * @protected
   */
  stopMoveStepLogic() {
    if (this.state.colorLoopActive === import_color_control.ColorControl.ColorLoopActive.Inactive) {
      this.internal.transitions?.stop("enhancedCurrentHue");
    }
    this.internal.transitions?.stop("currentHue");
    this.internal.transitions?.stop("currentSaturation");
    this.internal.transitions?.stop("currentX");
    this.internal.transitions?.stop("currentY");
    this.internal.transitions?.stop("colorTemperatureMireds");
  }
  /**
   * Default implementation notes:
   * After the options checks and potentially changing the color mode it uses the {@link moveColorTemperatureLogic}
   * method to set the color temperature.
   * If the current movement should be stopped {@link stopHueAndSaturationMovement} is used.
   * If you want to implement own logic just override {@link moveColorTemperatureLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target value directly. Else the step logic is applied and the color temperature is
   * increased or decreased by the step size every transition time interval.
   */
  moveColorTemperature({
    optionsOverride,
    optionsMask,
    moveMode,
    colorTemperatureMinimumMireds,
    colorTemperatureMaximumMireds,
    rate
  }) {
    this.#assertRate(moveMode, rate);
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(this.setColorMode(import_color_control.ColorControl.ColorMode.ColorTemperatureMireds), () => {
      if (moveMode === import_color_control.ColorControl.MoveMode.Stop) {
        return this.stopHueAndSaturationMovement();
      }
      if (colorTemperatureMinimumMireds === 0) {
        colorTemperatureMinimumMireds = this.minimumColorTemperatureMireds;
      }
      if (colorTemperatureMaximumMireds === 0) {
        colorTemperatureMaximumMireds = this.maximumColorTemperatureMireds;
      }
      colorTemperatureMinimumMireds = this.#cropColorTemperature(colorTemperatureMinimumMireds);
      colorTemperatureMaximumMireds = this.#cropColorTemperature(colorTemperatureMaximumMireds);
      return this.moveColorTemperatureLogic(
        moveMode,
        rate,
        colorTemperatureMinimumMireds,
        colorTemperatureMaximumMireds
      );
    });
  }
  /**
   * Default implementation of the moveColorTemperature logic.
   * If the managed transition time handling is disabled the method directly sets the min or max color temperature value.
   * Otherwise the method initiates a transition with the given rate.
   *
   * @param moveMode
   * @param rate
   * @param colorTemperatureMinimumMireds
   * @param colorTemperatureMaximumMireds
   * @protected
   */
  moveColorTemperatureLogic(moveMode, rate, colorTemperatureMinimumMireds, colorTemperatureMaximumMireds) {
    const direction = moveMode === import_color_control.ColorControl.MoveMode.Up ? 1 : -1;
    const targetValue = moveMode === import_color_control.ColorControl.MoveMode.Up ? colorTemperatureMaximumMireds : colorTemperatureMinimumMireds;
    return this.#startTransition({
      name: "colorTemperatureMireds",
      owner: this,
      changePerS: rate * direction,
      targetValue
    });
  }
  /**
   * Default implementation notes:
   * After the options checks and potentially switching the color mode it uses the {@link stepColorTemperatureLogic}
   * method to set the color temperature.
   * If you want to implement own logic just override {@link stepColorTemperatureLogic}.
   * The logic is implemented as follows: When no transition time is provided, the server will move as fast as
   * possible, so we set the target value directly. Else the step logic is applied and the color temperature is
   * increased or decreased by the step size every transition time interval.
   */
  stepColorTemperature({
    optionsOverride,
    optionsMask,
    stepMode,
    stepSize,
    transitionTime,
    colorTemperatureMinimumMireds,
    colorTemperatureMaximumMireds
  }) {
    this.#assertStepSize(stepSize, "ColorTemperature");
    if (!this.#optionsAllowExecution(optionsMask, optionsOverride)) {
      return;
    }
    return import_general.MaybePromise.then(this.setColorMode(import_color_control.ColorControl.ColorMode.ColorTemperatureMireds), () => {
      if (colorTemperatureMinimumMireds === 0) {
        colorTemperatureMinimumMireds = this.minimumColorTemperatureMireds;
      }
      if (colorTemperatureMaximumMireds === 0) {
        colorTemperatureMaximumMireds = this.maximumColorTemperatureMireds;
      }
      colorTemperatureMinimumMireds = this.#cropColorTemperature(colorTemperatureMinimumMireds);
      colorTemperatureMaximumMireds = this.#cropColorTemperature(colorTemperatureMaximumMireds);
      return this.stepColorTemperatureLogic(
        stepMode,
        stepSize,
        transitionTime,
        colorTemperatureMinimumMireds,
        colorTemperatureMaximumMireds
      );
    });
  }
  /**
   * Default implementation of the stepColorTemperature logic.
   * If the managed transition time handling is disabled the method directly sets the new color temperature value.
   * Otherwise the method initiates a transition with the given rate.
   *
   * @param stepMode Step Mode to move the color temperature up or down
   * @param stepSize Step size to move the color temperature up or down
   * @param transitionTime Time in seconds to move the color temperature. 0 means "as fast as possible"
   * @param colorTemperatureMinimumMireds Minimum allowed color temperature value
   * @param colorTemperatureMaximumMireds Maximum allowed color temperature value
   * @protected
   */
  stepColorTemperatureLogic(stepMode, stepSize, transitionTime, colorTemperatureMinimumMireds, colorTemperatureMaximumMireds) {
    const direction = stepMode === import_color_control.ColorControl.StepMode.Up ? 1 : -1;
    const targetValue = (0, import_general.cropValueRange)(
      this.state.colorTemperatureMireds + stepSize * direction,
      colorTemperatureMinimumMireds,
      colorTemperatureMaximumMireds
    );
    return this.#startTransition({
      name: "colorTemperatureMireds",
      owner: this,
      changePerS: stepSize / transitionTime * 10 * direction,
      targetValue
    });
  }
  /**
   * Convenient method to change the color mode of the device. It uses {@link switchColorMode} to
   * convert the current color attributes to the new mode. Override this method for device specific behavior.
   * @private
   */
  setColorMode(mode) {
    if (this.state.colorMode === mode) {
      return;
    }
    return import_general.MaybePromise.then(this.switchColorMode(this.state.colorMode, mode), () => {
      this.state.colorMode = mode;
      this.state.enhancedColorMode = mode;
    });
  }
  /**
   * Convenient method to change the enhanced color mode of the device. it uses {@link switchColorMode} to
   * convert the current color attributes to the new mode. Override this method for device specific behavior.
   * @private
   */
  setEnhancedColorMode(mode) {
    if (this.state.enhancedColorMode === mode) {
      return;
    }
    let colorModeSet;
    if (mode === import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation) {
      colorModeSet = this.setColorMode(import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
    } else {
      colorModeSet = this.setColorMode(mode);
    }
    return import_general.MaybePromise.then(colorModeSet, () => {
      this.state.enhancedColorMode = mode;
    });
  }
  /**
   * Default implementation notes:
   * This method is used to switch the device color mode. It converts the current color attributes to the new mode on
   * a best effort "as close as possible" basis. If the device hardware can do this better this method should be
   * overridden to implement this switch logic and to update all state attributes for the new mode. The color mode
   * attribute update itself is handled by the calling method, so is not needed to be done here.
   *
   * @param oldMode Old color Mode
   * @param newMode New color Mode
   * @protected
   */
  switchColorMode(oldMode, newMode) {
    if (oldMode === newMode) {
      return;
    }
    import_general.MaybePromise.then(this.stopAllColorMovement(), () => {
      switch (oldMode) {
        case import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation:
          if (this.state.currentHue === void 0 || this.state.currentSaturation === void 0) {
            logger.warn("Could not convert from hue/saturation because one of them is undefined");
            break;
          }
          switch (newMode) {
            case import_color_control.ColorControl.ColorMode.CurrentXAndCurrentY:
              const [x, y] = (0, import_ColorConversionUtils.hsvToXy)(this.hue, this.saturation);
              this.x = x;
              this.y = y;
              break;
            case import_color_control.ColorControl.ColorMode.ColorTemperatureMireds:
              const mireds = (0, import_ColorConversionUtils.hsvToMireds)(this.hue, this.saturation);
              if (mireds === void 0) {
                logger.warn(
                  `Could not convert hue/saturation (${this.hue}/${this.saturation}) to color temperature`
                );
              } else {
                this.mireds = mireds;
              }
              break;
          }
          break;
        case import_color_control.ColorControl.ColorMode.CurrentXAndCurrentY:
          if (this.state.currentX === void 0 || this.state.currentY === void 0) {
            logger.warn("Could not convert from xy because one of them is undefined");
            break;
          }
          switch (newMode) {
            case import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation:
              const [hue, saturation] = (0, import_ColorConversionUtils.hsvToXy)(this.x, this.y);
              this.hue = hue;
              this.saturation = saturation;
              break;
            case import_color_control.ColorControl.ColorMode.ColorTemperatureMireds:
              const mireds = (0, import_ColorConversionUtils.xyToMireds)(this.x, this.y);
              if (mireds === void 0) {
                logger.warn(`Could not convert xy ${this.x / this.y} to color temperature`);
              } else {
                this.mireds = mireds;
              }
              break;
          }
          break;
        case import_color_control.ColorControl.ColorMode.ColorTemperatureMireds:
          if (this.state.colorTemperatureMireds === void 0) {
            logger.warn("Could not convert from color temperature because it is undefined");
            break;
          }
          switch (newMode) {
            case import_color_control.ColorControl.ColorMode.CurrentHueAndCurrentSaturation:
              const hsvResult = (0, import_ColorConversionUtils.miredsToHsv)(this.mireds);
              if (hsvResult === void 0) {
                logger.warn(`Could not convert color temperature ${this.mireds} to hue/saturation`);
              } else {
                const [hue, saturation] = hsvResult;
                this.hue = hue;
                this.saturation = saturation;
              }
              break;
            case import_color_control.ColorControl.ColorMode.CurrentXAndCurrentY:
              const xyResult = (0, import_ColorConversionUtils.miredsToXy)(this.mireds);
              if (xyResult === void 0) {
                logger.warn("Could not convert color temperature to xy");
              } else {
                const [x, y] = xyResult;
                this.x = x;
                this.y = y;
              }
              break;
          }
          break;
      }
    });
  }
  /**
   * This method is used internally to potentially handle the dependency between the ColorControl and LevelControl
   * cluster.
   * Do not override this method! Please use the {@link syncColorTemperatureWithLevelLogic} method instead which is
   * called by this method if a sync is needed.
   */
  syncColorTemperatureWithLevel(level) {
    if (this.state.colorMode === import_color_control.ColorControl.ColorMode.ColorTemperatureMireds || this.state.enhancedColorMode === import_color_control.ColorControl.EnhancedColorMode.ColorTemperatureMireds) {
      return this.syncColorTemperatureWithLevelLogic(level);
    }
  }
  /**
   * This method is used internally to potentially handle the dependency between the ColorControl and LevelControl
   * clusters as defined by the Matter specification. It uses {@link moveToColorTemperatureLogic} with a transition
   * time of 0 (as fast as possible) to set the determined color temperature. Unless you have special requirements
   * you do not need to override it.
   *
   * @param level The current level value from the LevelControl cluster
   * @protected
   */
  syncColorTemperatureWithLevelLogic(level) {
    if (this.state.colorMode !== import_color_control.ColorControl.ColorMode.ColorTemperatureMireds && this.state.enhancedColorMode !== import_color_control.ColorControl.EnhancedColorMode.ColorTemperatureMireds) {
      return;
    }
    const tempCoupleMin = this.state.coupleColorTempToLevelMinMireds ?? this.minimumColorTemperatureMireds;
    const tempPhysMax = this.maximumColorTemperatureMireds;
    let newColorTemp;
    if (level <= MIN_CURRENT_LEVEL) {
      newColorTemp = tempPhysMax;
    } else if (level >= MAX_CURRENT_LEVEL) {
      newColorTemp = tempCoupleMin;
    } else {
      const tempDelta = Math.floor(
        (tempPhysMax - tempCoupleMin) * level / (MAX_CURRENT_LEVEL - MIN_CURRENT_LEVEL + 1)
      );
      newColorTemp = tempPhysMax - tempDelta;
    }
    logger.debug(`Synced color temperature with level: ${level}, new color temperature: ${newColorTemp}`);
    return this.moveToColorTemperatureLogic(newColorTemp, 0);
  }
  #assertRate(mode, rate) {
    if ((mode === import_color_control.ColorControl.MoveMode.Up || mode === import_color_control.ColorControl.MoveMode.Down) && rate === 0) {
      throw new import_types.StatusResponseError("Rate must not be 0 when moving Up or Down", import_types.StatusCode.InvalidCommand);
    }
  }
  /** Calculate the hue distance depending on the direction and the current and target hue. */
  #getHueDistanceByDirection(currentHue, targetHue, direction, max) {
    const distance = (targetHue > currentHue ? targetHue : max + targetHue + 1) - currentHue;
    if (distance === 0) {
      return 0;
    }
    if (direction === import_color_control.ColorControl.Direction.Up) {
      return distance;
    } else if (direction === import_color_control.ColorControl.Direction.Down) {
      return -(max - distance);
    }
    if (direction === import_color_control.ColorControl.Direction.Shortest) {
      if (Math.abs(distance) > max / 2) {
        return -(max - distance);
      }
      return distance;
    }
    if (direction === import_color_control.ColorControl.Direction.Longest) {
      if (Math.abs(distance) > max / 2) {
        return distance;
      }
      return -(max - distance);
    }
    throw new import_general.ImplementationError(`Unknown direction: ${direction}`);
  }
  /**
   * Create transition management instance.
   *
   * We manage transitions using {@link Transitions} if
   * {@link LevelControlBaseServer.State#managedTransitionTimeHandling} is true.
   *
   * You may override this method to replace the {@link Transitions} implementation customized for your application.
   * The provided configuration object is the default one used for Color Control transitions, but can be adjusted
   * if needed.
   */
  createTransitions(config) {
    return new import_Transitions.Transitions(this.endpoint, config);
  }
  #initializeTransitions() {
    const { endpoint } = this;
    const readOnlyState = endpoint.state.colorControl;
    return this.createTransitions({
      type: ColorControlBaseServer,
      remainingTimeEvent: this.events.remainingTime$Changed,
      get manageTransitions() {
        return readOnlyState.managedTransitionTimeHandling;
      },
      get transitionEndTimeMs() {
        return readOnlyState.transitionEndTimeMs;
      },
      get stepInterval() {
        return readOnlyState.transitionStepInterval;
      },
      properties: {
        currentHue: {
          min: MIN_HUE_VALUE,
          max: MAX_HUE_VALUE,
          cyclic: true
        },
        enhancedCurrentHue: {
          min: MIN_HUE_VALUE,
          max: MAX_ENHANCED_HUE_VALUE,
          cyclic: true
        },
        currentSaturation: {
          min: MIN_SATURATION_VALUE,
          max: MAX_SATURATION_VALUE
        },
        colorTemperatureMireds: {
          get min() {
            const colorTempPhysicalMinMireds = readOnlyState.colorTempPhysicalMinMireds;
            return colorTempPhysicalMinMireds === 0 ? MIN_TEMPERATURE_VALUE : colorTempPhysicalMinMireds;
          },
          get max() {
            const colorTempPhysicalMaxMireds = readOnlyState.colorTempPhysicalMaxMireds;
            return colorTempPhysicalMaxMireds === 0 ? MAX_TEMPERATURE_VALUE : colorTempPhysicalMaxMireds;
          }
        },
        currentX: {
          min: MIN_CIE_XY_VALUE,
          max: MAX_CIE_XY_VALUE
        },
        currentY: {
          min: MIN_CIE_XY_VALUE,
          max: MAX_CIE_XY_VALUE
        }
      }
    });
  }
  #startTransition(transition) {
    const { name } = transition;
    if (name === "currentHue") {
      this.internal.transitions?.stop("enhancedCurrentHue");
    } else if (name === "enhancedCurrentHue") {
      this.internal.transitions?.stop("currentHue");
    }
    return this.internal.transitions?.start(transition);
  }
  #getBootReason() {
    const rootEndpoint = this.env.get(import_ServerNode.ServerNode);
    if (rootEndpoint.behaviors.has(import_general_diagnostics.GeneralDiagnosticsBehavior)) {
      return rootEndpoint.stateOf(import_general_diagnostics.GeneralDiagnosticsBehavior).bootReason;
    }
  }
  #calculateEffectiveOptions(optionsMask, optionsOverride) {
    const options = this.state.options ?? {};
    return {
      executeIfOff: optionsMask.executeIfOff ? optionsOverride.executeIfOff : options.executeIfOff
    };
  }
  #optionsAllowExecution(optionsMask, optionsOverride) {
    const options = this.#calculateEffectiveOptions(optionsMask, optionsOverride);
    return options.executeIfOff || !this.agent.has(import_on_off.OnOffServer) || this.agent.get(import_on_off.OnOffServer).state.onOff;
  }
  #returnAsXyValue(value) {
    return value / 65536;
  }
  #returnAsOptionalXyValue(value) {
    if (value === void 0) {
      return void 0;
    }
    return this.#returnAsXyValue(value);
  }
  #setFromXyValue(fieldName, value) {
    if (value === void 0) {
      if (this.state[fieldName] === void 0) {
        return;
      } else {
        throw new import_general.ImplementationError(`${fieldName} cannot be set to undefined while being enabled.`);
      }
    }
    this.state[fieldName] = (0, import_general.cropValueRange)(Math.round(value * 65536), MIN_CIE_XY_VALUE, MAX_CIE_XY_VALUE);
  }
  #cropColorTemperature(temperatureMireds) {
    return (0, import_general.cropValueRange)(
      temperatureMireds,
      this.minimumColorTemperatureMireds,
      this.maximumColorTemperatureMireds
    );
  }
  #supportsColorMode(targetMode) {
    switch (targetMode) {
      case import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation:
        return this.features.enhancedHue;
      case import_color_control.ColorControl.EnhancedColorMode.CurrentHueAndCurrentSaturation:
        return this.features.hueSaturation;
      case import_color_control.ColorControl.EnhancedColorMode.CurrentXAndCurrentY:
        return this.features.xy;
      case import_color_control.ColorControl.EnhancedColorMode.ColorTemperatureMireds:
        return this.features.colorTemperature;
    }
    return false;
  }
  /** Apply Scene values when requested from ScenesManagement cluster */
  #applySceneValues(values, transitionTime) {
    let targetEnhancedColorMode = import_color_control.ColorControl.EnhancedColorMode.CurrentHueAndCurrentSaturation;
    let targetColorLoopActive = import_color_control.ColorControl.ColorLoopActive.Inactive;
    let targetColorLoopDirection = import_color_control.ColorControl.ColorLoopDirection.Decrement;
    let targetColorLoopTime = 25;
    let targetCurrentX;
    let targetCurrentY;
    let targetEnhancedCurrentHue;
    let targetCurrentSaturation;
    let targetColorTemperatureMireds;
    if (this.features.xy) {
      if (typeof values.currentX === "number") {
        targetCurrentX = (0, import_general.cropValueRange)(values.currentX, 0, MAX_CIE_XY_VALUE);
      }
      if (typeof values.currentY === "number") {
        targetCurrentY = (0, import_general.cropValueRange)(values.currentY, 0, MAX_CIE_XY_VALUE);
      }
    }
    if (this.features.enhancedHue && typeof values.enhancedCurrentHue === "number") {
      targetEnhancedCurrentHue = values.enhancedCurrentHue;
    }
    if (this.features.hueSaturation && typeof values.currentSaturation === "number") {
      targetCurrentSaturation = (0, import_general.cropValueRange)(values.currentSaturation, 0, MAX_SATURATION_VALUE);
    }
    if (this.features.colorLoop) {
      if (typeof values.colorLoopActive === "number") {
        targetColorLoopActive = values.colorLoopActive;
      }
      if (typeof values.colorLoopDirection === "number") {
        targetColorLoopDirection = values.colorLoopDirection;
      }
      if (typeof values.colorLoopTime === "number") {
        targetColorLoopTime = values.colorLoopTime;
      }
    }
    if (this.features.colorTemperature && typeof values.colorTemperatureMireds === "number") {
      targetColorTemperatureMireds = (0, import_general.cropValueRange)(values.colorTemperatureMireds, 0, MAX_TEMPERATURE_VALUE);
    }
    if (this.features.enhancedHue && typeof values.enhancedColorMode === "number" && values.enhancedColorMode <= import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation) {
      targetEnhancedColorMode = values.enhancedColorMode;
    }
    if (!this.#supportsColorMode(targetEnhancedColorMode)) {
      logger.info(
        `Can not apply scene with unsupported color mode: ${import_color_control.ColorControl.EnhancedColorMode[targetEnhancedColorMode]} (${targetEnhancedColorMode})`
      );
    }
    if (this.features.colorLoop && targetColorLoopActive === import_color_control.ColorControl.ColorLoopActive.Active) {
      return this.colorLoopSet({
        updateFlags: { updateDirection: true, updateTime: true, updateAction: true },
        action: import_color_control.ColorControl.ColorLoopAction.ActivateFromEnhancedCurrentHue,
        direction: targetColorLoopDirection,
        time: targetColorLoopTime,
        optionsMask: { executeIfOff: true },
        optionsOverride: { executeIfOff: true },
        startHue: this.state.colorLoopStartEnhancedHue
        // Will be ignored
      });
    }
    switch (targetEnhancedColorMode) {
      case import_color_control.ColorControl.EnhancedColorMode.CurrentHueAndCurrentSaturation:
        if (targetCurrentSaturation !== void 0) {
          return this.moveToSaturationLogic(targetCurrentSaturation, transitionTime / 100);
        }
        break;
      case import_color_control.ColorControl.EnhancedColorMode.CurrentXAndCurrentY:
        if (targetCurrentX !== void 0 && targetCurrentY !== void 0) {
          return this.moveToColorLogic(targetCurrentX, targetCurrentY, transitionTime);
        }
        break;
      case import_color_control.ColorControl.EnhancedColorMode.ColorTemperatureMireds:
        if (targetColorTemperatureMireds !== void 0) {
          return this.moveToColorTemperatureLogic(targetColorTemperatureMireds, transitionTime);
        }
        break;
      case import_color_control.ColorControl.EnhancedColorMode.EnhancedCurrentHueAndCurrentSaturation:
        if (targetEnhancedCurrentHue !== void 0 && targetCurrentSaturation !== void 0) {
          return this.moveToEnhancedHueAndSaturationLogic(
            targetEnhancedCurrentHue,
            targetCurrentSaturation,
            transitionTime
          );
        }
        break;
      default:
        logger.info(
          `No supported color mode found to apply scene: ${import_color_control.ColorControl.EnhancedColorMode[targetEnhancedColorMode]} (${targetEnhancedColorMode})`
        );
        break;
    }
  }
  /** Invalidate all stored scenes manually for this endpoint in the Scenesmanagement cluster because SDK behavior. */
  #invalidateScenes() {
    this.agent.maybeGet(import_scenes_management.ScenesManagementServer)?.makeAllFabricSceneInfoEntriesInvalid();
  }
  async [Symbol.asyncDispose]() {
    if (this.internal.transitions) {
      await this.internal.transitions.close();
      this.internal.transitions = void 0;
    }
    await super[Symbol.asyncDispose]?.();
  }
}
((ColorControlBaseServer2) => {
  class Internal {
    /**
     * Transition management.
     */
    transitions;
  }
  ColorControlBaseServer2.Internal = Internal;
  class State extends ColorControlBase.State {
    /**
     * The default implementation always set the target level immediately and so ignores all transition times
     * requested or configured.
     * Set this to true to manage transition times by changing the level value step wise every second. This is in
     * most cases not the best way because hardware supporting levels usually have ways to specify the change rate
     * or target value and transition time.
     */
    managedTransitionTimeHandling = false;
    /**
     * If transition management is disabled you may specify this as the "end time" for transitions.  The remaining
     * time attribute will then report correctly.
     */
    transitionEndTimeMs;
    /**
     * When managing transitions, this is the interval at which steps occur in ms.
     */
    transitionStepInterval = (0, import_general.Millis)(100);
    // These were the default values specified prior to Matter 1.4.2.  We still need default values so using the
    // previously specified values which are presumably educated logical defaults
    currentX = 24939;
    currentY = 24701;
    colorTemperatureMireds = 250;
    enhancedColorMode = import_color_control.ColorControl.EnhancedColorMode.CurrentXAndCurrentY;
    colorLoopTime = 25;
    colorLoopStartEnhancedHue = 8960;
    colorLoopActive = import_color_control.ColorControl.ColorLoopActive.Inactive;
    colorLoopDirection = import_color_control.ColorControl.ColorLoopDirection.Increment;
    [import_protocol.Val.properties](endpoint) {
      if (endpoint.behaviors.optionsFor(ColorControlBaseServer2)?.remainingTime === void 0 && endpoint.behaviors.defaultsFor(ColorControlBaseServer2)?.remainingTime === void 0) {
        return {};
      }
      return {
        set remainingTime(value) {
          const transition = endpoint.behaviors.internalsOf(ColorControlBaseServer2).transitions;
          if (transition) {
            transition.remainingTime = value;
          }
        },
        get remainingTime() {
          return endpoint.behaviors.internalsOf(ColorControlBaseServer2).transitions?.remainingTime ?? 0;
        }
      };
    }
  }
  ColorControlBaseServer2.State = State;
  class Events extends ColorControlBase.Events {
    transitionEndTime$Changed = (0, import_general.AsyncObservable)();
  }
  ColorControlBaseServer2.Events = Events;
})(ColorControlBaseServer || (ColorControlBaseServer = {}));
class ColorControlServer extends ColorControlBaseServer.for((0, import_types.ClusterType)(import_color_control.ColorControl.Base)) {
}
//# sourceMappingURL=ColorControlServer.js.map
