212 lines
5.3 KiB
TypeScript
212 lines
5.3 KiB
TypeScript
import {
|
|
CSSResultGroup,
|
|
LitElement,
|
|
PropertyValues,
|
|
css,
|
|
html,
|
|
nothing,
|
|
} from "lit";
|
|
import { customElement, property, state } from "lit/decorators";
|
|
import { styleMap } from "lit/directives/style-map";
|
|
import memoizeOne from "memoize-one";
|
|
import { rgb2hex } from "../../../../common/color/convert-color";
|
|
import {
|
|
DEFAULT_MAX_KELVIN,
|
|
DEFAULT_MIN_KELVIN,
|
|
temperature2rgb,
|
|
} from "../../../../common/color/convert-light-color";
|
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
|
import { stateColorCss } from "../../../../common/entity/state_color";
|
|
import { throttle } from "../../../../common/util/throttle";
|
|
import "../../../../components/ha-control-slider";
|
|
import { UNAVAILABLE } from "../../../../data/entity";
|
|
import {
|
|
LightColor,
|
|
LightColorMode,
|
|
LightEntity,
|
|
} from "../../../../data/light";
|
|
import { HomeAssistant } from "../../../../types";
|
|
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
|
|
|
|
declare global {
|
|
interface HASSDomEvents {
|
|
"color-changed": LightColor;
|
|
"color-hovered": LightColor | undefined;
|
|
}
|
|
}
|
|
|
|
export const generateColorTemperatureGradient = (min: number, max: number) => {
|
|
const count = 10;
|
|
|
|
const gradient: [number, string][] = [];
|
|
|
|
const step = (max - min) / count;
|
|
const percentageStep = 1 / count;
|
|
|
|
for (let i = 0; i < count + 1; i++) {
|
|
const value = min + step * i;
|
|
|
|
const hex = rgb2hex(temperature2rgb(value));
|
|
gradient.push([percentageStep * i, hex]);
|
|
}
|
|
|
|
return gradient
|
|
.map(([stop, color]) => `${color} ${(stop as number) * 100}%`)
|
|
.join(", ");
|
|
};
|
|
|
|
@customElement("light-color-temp-picker")
|
|
class LightColorTempPicker extends LitElement {
|
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
|
|
@property({ attribute: false }) public stateObj!: LightEntity;
|
|
|
|
@state() private _ctPickerValue?: number;
|
|
|
|
protected render() {
|
|
if (!this.stateObj) {
|
|
return nothing;
|
|
}
|
|
|
|
const minKelvin =
|
|
this.stateObj.attributes.min_color_temp_kelvin ?? DEFAULT_MIN_KELVIN;
|
|
const maxKelvin =
|
|
this.stateObj.attributes.max_color_temp_kelvin ?? DEFAULT_MAX_KELVIN;
|
|
|
|
const gradient = this._generateTemperatureGradient(minKelvin!, maxKelvin);
|
|
const color = stateColorCss(this.stateObj);
|
|
|
|
return html`
|
|
<ha-control-slider
|
|
inverted
|
|
vertical
|
|
.value=${this._ctPickerValue}
|
|
.min=${minKelvin}
|
|
.max=${maxKelvin}
|
|
mode="cursor"
|
|
@value-changed=${this._ctColorChanged}
|
|
@slider-moved=${this._ctColorCursorMoved}
|
|
.ariaLabel=${this.hass.localize(
|
|
"ui.dialogs.more_info_control.light.color_temp"
|
|
)}
|
|
style=${styleMap({
|
|
"--control-slider-color": color,
|
|
"--gradient": gradient,
|
|
})}
|
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
|
.unit=${DOMAIN_ATTRIBUTES_UNITS.light.color_temp_kelvin}
|
|
.locale=${this.hass.locale}
|
|
>
|
|
</ha-control-slider>
|
|
`;
|
|
}
|
|
|
|
private _generateTemperatureGradient = memoizeOne(
|
|
(min: number, max: number) => generateColorTemperatureGradient(min, max)
|
|
);
|
|
|
|
public _updateSliderValues() {
|
|
const stateObj = this.stateObj;
|
|
|
|
if (stateObj.state === "on") {
|
|
this._ctPickerValue =
|
|
stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP
|
|
? stateObj.attributes.color_temp_kelvin
|
|
: undefined;
|
|
} else {
|
|
this._ctPickerValue = undefined;
|
|
}
|
|
}
|
|
|
|
public willUpdate(changedProps: PropertyValues) {
|
|
super.willUpdate(changedProps);
|
|
|
|
if (!changedProps.has("stateObj")) {
|
|
return;
|
|
}
|
|
|
|
this._updateSliderValues();
|
|
}
|
|
|
|
private _ctColorCursorMoved(ev: CustomEvent) {
|
|
const ct = ev.detail.value;
|
|
|
|
if (isNaN(ct) || this._ctPickerValue === ct) {
|
|
return;
|
|
}
|
|
|
|
this._ctPickerValue = ct;
|
|
|
|
fireEvent(this, "color-hovered", {
|
|
color_temp_kelvin: ct,
|
|
});
|
|
|
|
this._throttleUpdateColorTemp();
|
|
}
|
|
|
|
private _throttleUpdateColorTemp = throttle(() => {
|
|
this._updateColorTemp();
|
|
}, 500);
|
|
|
|
private _ctColorChanged(ev: CustomEvent) {
|
|
const ct = ev.detail.value;
|
|
|
|
fireEvent(this, "color-hovered", undefined);
|
|
|
|
if (isNaN(ct) || this._ctPickerValue === ct) {
|
|
return;
|
|
}
|
|
|
|
this._ctPickerValue = ct;
|
|
|
|
this._updateColorTemp();
|
|
}
|
|
|
|
private _updateColorTemp() {
|
|
const color_temp_kelvin = this._ctPickerValue!;
|
|
|
|
this._applyColor({ color_temp_kelvin });
|
|
}
|
|
|
|
private _applyColor(color: LightColor, params?: Record<string, any>) {
|
|
fireEvent(this, "color-changed", color);
|
|
this.hass.callService("light", "turn_on", {
|
|
entity_id: this.stateObj!.entity_id,
|
|
...color,
|
|
...params,
|
|
});
|
|
}
|
|
|
|
static get styles(): CSSResultGroup {
|
|
return [
|
|
css`
|
|
:host {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
ha-control-slider {
|
|
height: 45vh;
|
|
max-height: 320px;
|
|
min-height: 200px;
|
|
--control-slider-thickness: 130px;
|
|
--control-slider-border-radius: 48px;
|
|
--control-slider-color: var(--primary-color);
|
|
--control-slider-background: -webkit-linear-gradient(
|
|
top,
|
|
var(--gradient)
|
|
);
|
|
--control-slider-tooltip-font-size: 20px;
|
|
--control-slider-background-opacity: 1;
|
|
}
|
|
`,
|
|
];
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"light-color-temp-picker": LightColorTempPicker;
|
|
}
|
|
}
|