mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-10 02:46:38 +00:00
Use supported_color_modes
to determine what UI elements to show (#8961)
This commit is contained in:
parent
20c351949f
commit
36586b798e
@ -9,13 +9,10 @@ import {
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import {
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
LightColorModes,
|
||||
SUPPORT_EFFECT,
|
||||
SUPPORT_FLASH,
|
||||
SUPPORT_TRANSITION,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
} from "../../../src/data/light";
|
||||
import "../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
@ -32,7 +29,8 @@ const ENTITIES = [
|
||||
getEntity("light", "kitchen_light", "on", {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_features: SUPPORT_BRIGHTNESS,
|
||||
supported_color_modes: [LightColorModes.BRIGHTNESS],
|
||||
color_mode: LightColorModes.BRIGHTNESS,
|
||||
}),
|
||||
getEntity("light", "color_temperature_light", "on", {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
@ -40,20 +38,96 @@ const ENTITIES = [
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
],
|
||||
color_mode: LightColorModes.COLOR_TEMP,
|
||||
}),
|
||||
getEntity("light", "color_effectslight", "on", {
|
||||
friendly_name: "Color Effets Light",
|
||||
getEntity("light", "color_hs_light", "on", {
|
||||
friendly_name: "Color HS Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
white_value: 36,
|
||||
supported_features:
|
||||
SUPPORT_BRIGHTNESS +
|
||||
SUPPORT_EFFECT +
|
||||
SUPPORT_FLASH +
|
||||
SUPPORT_COLOR +
|
||||
SUPPORT_TRANSITION +
|
||||
SUPPORT_WHITE_VALUE,
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.HS,
|
||||
],
|
||||
color_mode: LightColorModes.HS,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgb_ct_light", "on", {
|
||||
friendly_name: "Color RGB + CT Light",
|
||||
brightness: 255,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGB,
|
||||
],
|
||||
color_mode: LightColorModes.COLOR_TEMP,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_RGB_light", "on", {
|
||||
friendly_name: "Color Effets Light",
|
||||
brightness: 255,
|
||||
rgb_color: [30, 100, 255],
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB],
|
||||
color_mode: LightColorModes.RGB,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbw_light", "on", {
|
||||
friendly_name: "Color RGBW Light",
|
||||
brightness: 255,
|
||||
rgbw_color: [30, 100, 255, 125],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGBW,
|
||||
],
|
||||
color_mode: LightColorModes.RGBW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbww_light", "on", {
|
||||
friendly_name: "Color RGBWW Light",
|
||||
brightness: 255,
|
||||
rgbww_color: [30, 100, 255, 125, 10],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGBWW,
|
||||
],
|
||||
color_mode: LightColorModes.RGBWW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_xy_light", "on", {
|
||||
friendly_name: "Color XY Light",
|
||||
brightness: 255,
|
||||
xy_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.XY,
|
||||
],
|
||||
color_mode: LightColorModes.XY,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
];
|
||||
|
@ -102,3 +102,18 @@ export const lab2hex = (lab: [number, number, number]): string => {
|
||||
const rgb = lab2rgb(lab);
|
||||
return rgb2hex(rgb);
|
||||
};
|
||||
|
||||
export const rgb2hsv = (
|
||||
rgb: [number, number, number]
|
||||
): [number, number, number] => {
|
||||
const [r, g, b] = rgb;
|
||||
const v = Math.max(r, g, b);
|
||||
const c = v - Math.min(r, g, b);
|
||||
const h =
|
||||
c && (v === r ? (g - b) / c : v === g ? 2 + (b - r) / c : 4 + (r - g) / c);
|
||||
return [60 * (h < 0 ? h + 6 : h), v && c / v, v];
|
||||
};
|
||||
|
||||
export const rgb2hs = (rgb: [number, number, number]): [number, number] => {
|
||||
return rgb2hsv(rgb).slice(0, 2) as [number, number];
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ import { computeActiveState } from "../../common/entity/compute_active_state";
|
||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||
import { stateIcon } from "../../common/entity/state_icon";
|
||||
import { iconColorCSS } from "../../common/style/icon_color_css";
|
||||
import { getLightRgbColor, LightEntity } from "../../data/light";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-icon";
|
||||
|
||||
@ -99,11 +100,13 @@ export class StateBadge extends LitElement {
|
||||
hostStyle.backgroundImage = `url(${imageUrl})`;
|
||||
this._showIcon = false;
|
||||
} else if (stateObj.state === "on") {
|
||||
if (stateObj.attributes.hs_color && this.stateColor !== false) {
|
||||
const hue = stateObj.attributes.hs_color[0];
|
||||
const sat = stateObj.attributes.hs_color[1];
|
||||
if (sat > 10) {
|
||||
iconStyle.color = `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
|
||||
if (
|
||||
computeStateDomain(stateObj) === "light" &&
|
||||
this.stateColor !== false
|
||||
) {
|
||||
const rgb = getLightRgbColor(stateObj as LightEntity);
|
||||
if (rgb) {
|
||||
iconStyle.color = `rgb(${rgb.slice(0, 3).join(",")})`;
|
||||
}
|
||||
}
|
||||
if (stateObj.attributes.brightness && this.stateColor !== false) {
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { styleMap } from "lit-html/directives/style-map";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { ToggleButton } from "../types";
|
||||
import "./ha-svg-icon";
|
||||
@ -19,6 +20,8 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
|
||||
@property() public active?: string;
|
||||
|
||||
@property({ type: Boolean }) public fullWidth = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div>
|
||||
@ -33,6 +36,11 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
|
||||
</mwc-icon-button>`
|
||||
: html`<mwc-button
|
||||
style=${styleMap({
|
||||
width: this.fullWidth
|
||||
? `${100 / this.buttons.length}%`
|
||||
: "initial",
|
||||
})}
|
||||
.value=${button.value}
|
||||
?active=${this.active === button.value}
|
||||
@click=${this._handleClick}
|
||||
|
@ -2,7 +2,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { EventsMixin } from "../mixins/events-mixin";
|
||||
|
||||
import { rgb2hs } from "../common/color/convert-color";
|
||||
/**
|
||||
* Color-picker custom element
|
||||
*
|
||||
@ -114,6 +114,12 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
observer: "applyHsColor",
|
||||
},
|
||||
|
||||
// use these properties to update the state via attributes
|
||||
desiredRgbColor: {
|
||||
type: Object,
|
||||
observer: "applyRgbColor",
|
||||
},
|
||||
|
||||
// width, height and radius apply to the coordinates of
|
||||
// of the canvas.
|
||||
// border width are relative to these numbers
|
||||
@ -177,8 +183,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
this.drawMarker();
|
||||
|
||||
if (this.desiredHsColor) {
|
||||
this.setMarkerOnColor(this.desiredHsColor);
|
||||
this.applyColorToCanvas(this.desiredHsColor);
|
||||
this.applyHsColor(this.desiredHsColor);
|
||||
}
|
||||
|
||||
if (this.desiredRgbColor) {
|
||||
this.applyRgbColor(this.desiredRgbColor);
|
||||
}
|
||||
|
||||
this.interactionLayer.addEventListener("mousedown", (ev) =>
|
||||
@ -282,12 +291,13 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
processUserSelect(ev) {
|
||||
const canvasXY = this.convertToCanvasCoordinates(ev.clientX, ev.clientY);
|
||||
const hs = this.getColor(canvasXY.x, canvasXY.y);
|
||||
this.onColorSelect(hs);
|
||||
const rgb = this.getRgbColor(canvasXY.x, canvasXY.y);
|
||||
this.onColorSelect(hs, rgb);
|
||||
}
|
||||
|
||||
// apply color to marker position and canvas
|
||||
onColorSelect(hs) {
|
||||
this.setMarkerOnColor(hs); // marker always follows mounse 'raw' hs value (= mouse position)
|
||||
onColorSelect(hs, rgb) {
|
||||
this.setMarkerOnColor(hs); // marker always follows mouse 'raw' hs value (= mouse position)
|
||||
if (!this.ignoreSegments) {
|
||||
// apply segments if needed
|
||||
hs = this.applySegmentFilter(hs);
|
||||
@ -301,11 +311,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
// eventually after throttle limit has passed
|
||||
clearTimeout(this.ensureFinalSelect);
|
||||
this.ensureFinalSelect = setTimeout(() => {
|
||||
this.fireColorSelected(hs); // do it for the final time
|
||||
this.fireColorSelected(hs, rgb); // do it for the final time
|
||||
}, this.throttle);
|
||||
return;
|
||||
}
|
||||
this.fireColorSelected(hs); // do it
|
||||
this.fireColorSelected(hs, rgb); // do it
|
||||
this.colorSelectIsThrottled = true;
|
||||
setTimeout(() => {
|
||||
this.colorSelectIsThrottled = false;
|
||||
@ -313,9 +323,9 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
|
||||
// set color values and fire colorselected event
|
||||
fireColorSelected(hs) {
|
||||
fireColorSelected(hs, rgb) {
|
||||
this.hsColor = hs;
|
||||
this.fire("colorselected", { hs: { h: hs.h, s: hs.s } });
|
||||
this.fire("colorselected", { hs, rgb });
|
||||
}
|
||||
|
||||
/*
|
||||
@ -363,6 +373,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
this.applyColorToCanvas(hs);
|
||||
}
|
||||
|
||||
applyRgbColor(rgb) {
|
||||
const [h, s] = rgb2hs(rgb);
|
||||
this.applyHsColor({ h, s });
|
||||
}
|
||||
|
||||
/*
|
||||
* input processing helpers
|
||||
*/
|
||||
@ -395,6 +410,15 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
return { h: hue, s: sat };
|
||||
}
|
||||
|
||||
getRgbColor(x, y) {
|
||||
// get current pixel
|
||||
const imageData = this.backgroundLayer
|
||||
.getContext("2d")
|
||||
.getImageData(x + 250, y + 250, 1, 1);
|
||||
const pixel = imageData.data;
|
||||
return { r: pixel[0], g: pixel[1], b: pixel[2] };
|
||||
}
|
||||
|
||||
applySegmentFilter(hs) {
|
||||
// apply hue segment steps
|
||||
if (this.hueSegments) {
|
||||
@ -468,7 +492,7 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
|
||||
.getPropertyValue("--wheel-bordercolor")
|
||||
.trim();
|
||||
const wheelShadow = wheelStyle.getPropertyValue("--wheel-shadow").trim();
|
||||
// extract shadow properties from CCS variable
|
||||
// extract shadow properties from CSS variable
|
||||
// the shadow should be defined as: "10px 5px 5px 0px COLOR"
|
||||
if (wheelShadow !== "none") {
|
||||
const values = wheelShadow.split("px ");
|
||||
|
@ -3,26 +3,82 @@ import {
|
||||
HassEntityBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
|
||||
export enum LightColorModes {
|
||||
UNKNOWN = "unknown",
|
||||
ONOFF = "onoff",
|
||||
BRIGHTNESS = "brightness",
|
||||
COLOR_TEMP = "color_temp",
|
||||
HS = "hs",
|
||||
XY = "xy",
|
||||
RGB = "rgb",
|
||||
RGBW = "rgbw",
|
||||
RGBWW = "rgbww",
|
||||
}
|
||||
|
||||
const modesSupportingColor = [
|
||||
LightColorModes.HS,
|
||||
LightColorModes.XY,
|
||||
LightColorModes.RGB,
|
||||
LightColorModes.RGBW,
|
||||
LightColorModes.RGBWW,
|
||||
];
|
||||
|
||||
const modesSupportingDimming = [
|
||||
...modesSupportingColor,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.BRIGHTNESS,
|
||||
];
|
||||
|
||||
export const SUPPORT_EFFECT = 4;
|
||||
export const SUPPORT_FLASH = 8;
|
||||
export const SUPPORT_TRANSITION = 32;
|
||||
|
||||
export const lightSupportsColorMode = (
|
||||
entity: LightEntity,
|
||||
mode: LightColorModes
|
||||
) => {
|
||||
return entity.attributes.supported_color_modes?.includes(mode);
|
||||
};
|
||||
|
||||
export const lightIsInColorMode = (entity: LightEntity) => {
|
||||
return modesSupportingColor.includes(entity.attributes.color_mode);
|
||||
};
|
||||
|
||||
export const lightSupportsColor = (entity: LightEntity) => {
|
||||
return entity.attributes.supported_color_modes?.some((mode) =>
|
||||
modesSupportingColor.includes(mode)
|
||||
);
|
||||
};
|
||||
|
||||
export const lightSupportsDimming = (entity: LightEntity) => {
|
||||
return entity.attributes.supported_color_modes?.some((mode) =>
|
||||
modesSupportingDimming.includes(mode)
|
||||
);
|
||||
};
|
||||
|
||||
export const getLightRgbColor = (entity: LightEntity): number[] | undefined =>
|
||||
entity.attributes.color_mode === LightColorModes.RGBWW
|
||||
? entity.attributes.rgbww_color
|
||||
: entity.attributes.color_mode === LightColorModes.RGBW
|
||||
? entity.attributes.rgbw_color
|
||||
: entity.attributes.rgb_color;
|
||||
|
||||
interface LightEntityAttributes extends HassEntityAttributeBase {
|
||||
min_mireds: number;
|
||||
max_mireds: number;
|
||||
friendly_name: string;
|
||||
brightness: number;
|
||||
hs_color: number[];
|
||||
hs_color: [number, number];
|
||||
rgb_color: [number, number, number];
|
||||
rgbw_color: [number, number, number, number];
|
||||
rgbww_color: [number, number, number, number, number];
|
||||
color_temp: number;
|
||||
white_value: number;
|
||||
effect?: string;
|
||||
effect_list: string[] | null;
|
||||
supported_color_modes: LightColorModes[];
|
||||
color_mode: LightColorModes;
|
||||
}
|
||||
|
||||
export interface LightEntity extends HassEntityBase {
|
||||
attributes: LightEntityAttributes;
|
||||
}
|
||||
|
||||
export const SUPPORT_BRIGHTNESS = 1;
|
||||
export const SUPPORT_COLOR_TEMP = 2;
|
||||
export const SUPPORT_EFFECT = 4;
|
||||
export const SUPPORT_FLASH = 8;
|
||||
export const SUPPORT_COLOR = 16;
|
||||
export const SUPPORT_TRANSITION = 32;
|
||||
export const SUPPORT_WHITE_VALUE = 128;
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-attributes";
|
||||
import "../../../components/ha-color-picker";
|
||||
@ -19,20 +18,22 @@ import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-labeled-slider";
|
||||
import "../../../components/ha-paper-dropdown-menu";
|
||||
import {
|
||||
getLightRgbColor,
|
||||
LightColorModes,
|
||||
LightEntity,
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
lightIsInColorMode,
|
||||
lightSupportsColor,
|
||||
lightSupportsColorMode,
|
||||
lightSupportsDimming,
|
||||
SUPPORT_EFFECT,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
} from "../../../data/light";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../../../components/ha-button-toggle-group";
|
||||
|
||||
interface HueSatColor {
|
||||
h: number;
|
||||
s: number;
|
||||
}
|
||||
|
||||
const toggleButtons = [
|
||||
{ label: "Color", value: "color" },
|
||||
{ label: "Temperature", value: LightColorModes.COLOR_TEMP },
|
||||
];
|
||||
@customElement("more-info-light")
|
||||
class MoreInfoLight extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -41,28 +42,51 @@ class MoreInfoLight extends LitElement {
|
||||
|
||||
@internalProperty() private _brightnessSliderValue = 0;
|
||||
|
||||
@internalProperty() private _ctSliderValue = 0;
|
||||
@internalProperty() private _ctSliderValue?: number;
|
||||
|
||||
@internalProperty() private _wvSliderValue = 0;
|
||||
@internalProperty() private _cwSliderValue?: number;
|
||||
|
||||
@internalProperty() private _wwSliderValue?: number;
|
||||
|
||||
@internalProperty() private _wvSliderValue?: number;
|
||||
|
||||
@internalProperty() private _colorBrightnessSliderValue?: number;
|
||||
|
||||
@internalProperty() private _brightnessAdjusted?: number;
|
||||
|
||||
@internalProperty() private _hueSegments = 24;
|
||||
|
||||
@internalProperty() private _saturationSegments = 8;
|
||||
|
||||
@internalProperty() private _colorPickerColor?: HueSatColor;
|
||||
@internalProperty() private _colorPickerColor?: [number, number, number];
|
||||
|
||||
@internalProperty() private _mode?: "color" | LightColorModes.COLOR_TEMP;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const supportsTemp = lightSupportsColorMode(
|
||||
this.stateObj,
|
||||
LightColorModes.COLOR_TEMP
|
||||
);
|
||||
|
||||
const supportsRgbww = lightSupportsColorMode(
|
||||
this.stateObj,
|
||||
LightColorModes.RGBWW
|
||||
);
|
||||
|
||||
const supportsRgbw =
|
||||
!supportsRgbww &&
|
||||
lightSupportsColorMode(this.stateObj, LightColorModes.RGBW);
|
||||
|
||||
const supportsColor =
|
||||
supportsRgbww || supportsRgbw || lightSupportsColor(this.stateObj);
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="content ${classMap({
|
||||
"is-on": this.stateObj.state === "on",
|
||||
})}"
|
||||
>
|
||||
${supportsFeature(this.stateObj!, SUPPORT_BRIGHTNESS)
|
||||
<div class="content">
|
||||
${lightSupportsDimming(this.stateObj)
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
caption=${this.hass.localize("ui.card.light.brightness")}
|
||||
@ -77,7 +101,17 @@ class MoreInfoLight extends LitElement {
|
||||
: ""}
|
||||
${this.stateObj.state === "on"
|
||||
? html`
|
||||
${supportsFeature(this.stateObj, SUPPORT_COLOR_TEMP)
|
||||
${supportsTemp || supportsColor ? html`<hr></hr>` : ""}
|
||||
${supportsTemp && supportsColor
|
||||
? html`<ha-button-toggle-group
|
||||
fullWidth
|
||||
.buttons=${toggleButtons}
|
||||
.active=${this._mode}
|
||||
@value-changed=${this._modeChanged}
|
||||
></ha-button-toggle-group>`
|
||||
: ""}
|
||||
${supportsTemp &&
|
||||
(!supportsColor || this._mode === LightColorModes.COLOR_TEMP)
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
class="color_temp"
|
||||
@ -91,27 +125,16 @@ class MoreInfoLight extends LitElement {
|
||||
@change=${this._ctSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
<hr></hr>
|
||||
`
|
||||
: ""}
|
||||
${supportsFeature(this.stateObj, SUPPORT_WHITE_VALUE)
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
caption=${this.hass.localize("ui.card.light.white_value")}
|
||||
icon="hass:file-word-box"
|
||||
max="255"
|
||||
.value=${this._wvSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
`
|
||||
: ""}
|
||||
${supportsFeature(this.stateObj, SUPPORT_COLOR)
|
||||
${supportsColor && (!supportsTemp || this._mode === "color")
|
||||
? html`
|
||||
<div class="segmentationContainer">
|
||||
<ha-color-picker
|
||||
class="color"
|
||||
@colorselected=${this._colorPicked}
|
||||
.desiredHsColor=${this._colorPickerColor}
|
||||
.desiredRgbColor=${this._colorPickerColor}
|
||||
throttle="500"
|
||||
.hueSegments=${this._hueSegments}
|
||||
.saturationSegments=${this._saturationSegments}
|
||||
@ -123,6 +146,67 @@ class MoreInfoLight extends LitElement {
|
||||
class="segmentationButton"
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
|
||||
${
|
||||
supportsRgbw || supportsRgbww
|
||||
? html`<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.color_brightness"
|
||||
)}
|
||||
icon="hass:brightness-7"
|
||||
max="100"
|
||||
.value=${this._colorBrightnessSliderValue ?? 255}
|
||||
@change=${this._colorBrightnessSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
supportsRgbw
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.white_value"
|
||||
)}
|
||||
icon="hass:file-word-box"
|
||||
max="100"
|
||||
.name=${"wv"}
|
||||
.value=${this._wvSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
supportsRgbww
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.cold_white_value"
|
||||
)}
|
||||
icon="hass:file-word-box-outline"
|
||||
max="100"
|
||||
.name=${"cw"}
|
||||
.value=${this._cwSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.warm_white_value"
|
||||
)}
|
||||
icon="hass:file-word-box"
|
||||
max="100"
|
||||
.name=${"ww"}
|
||||
.value=${this._wwSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
<hr></hr>
|
||||
`
|
||||
: ""}
|
||||
${supportsFeature(this.stateObj, SUPPORT_EFFECT) &&
|
||||
@ -151,34 +235,85 @@ class MoreInfoLight extends LitElement {
|
||||
: ""}
|
||||
<ha-attributes
|
||||
.stateObj=${this.stateObj}
|
||||
extra-filters="brightness,color_temp,white_value,effect_list,effect,hs_color,rgb_color,xy_color,min_mireds,max_mireds,entity_id,supported_color_modes,color_mode"
|
||||
extra-filters="brightness,color_temp,white_value,effect_list,effect,hs_color,rgb_color,rgbw_color,rgbww_color,xy_color,min_mireds,max_mireds,entity_id,supported_color_modes,color_mode"
|
||||
></ha-attributes>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
protected updated(changedProps: PropertyValues<this>) {
|
||||
if (!changedProps.has("stateObj")) {
|
||||
return;
|
||||
}
|
||||
const stateObj = this.stateObj! as LightEntity;
|
||||
if (changedProps.has("stateObj")) {
|
||||
if (stateObj.state === "on") {
|
||||
this._brightnessSliderValue = Math.round(
|
||||
(stateObj.attributes.brightness * 100) / 255
|
||||
);
|
||||
this._ctSliderValue = stateObj.attributes.color_temp;
|
||||
this._wvSliderValue = stateObj.attributes.white_value;
|
||||
const oldStateObj = changedProps.get("stateObj") as LightEntity | undefined;
|
||||
|
||||
if (stateObj.attributes.hs_color) {
|
||||
this._colorPickerColor = {
|
||||
h: stateObj.attributes.hs_color[0],
|
||||
s: stateObj.attributes.hs_color[1] / 100,
|
||||
};
|
||||
if (stateObj.state === "on") {
|
||||
// Don't change tab when the color mode changes
|
||||
if (
|
||||
oldStateObj?.entity_id !== stateObj.entity_id ||
|
||||
oldStateObj?.state !== stateObj.state
|
||||
) {
|
||||
this._mode = lightIsInColorMode(this.stateObj!)
|
||||
? "color"
|
||||
: LightColorModes.COLOR_TEMP;
|
||||
}
|
||||
|
||||
let brightnessAdjust = 100;
|
||||
if (
|
||||
stateObj.attributes.color_mode === LightColorModes.RGB &&
|
||||
!lightSupportsColorMode(stateObj, LightColorModes.RGBWW) &&
|
||||
!lightSupportsColorMode(stateObj, LightColorModes.RGBW)
|
||||
) {
|
||||
const maxVal = Math.max(...stateObj.attributes.rgb_color);
|
||||
if (maxVal < 255) {
|
||||
this._brightnessAdjusted = maxVal;
|
||||
brightnessAdjust = (this._brightnessAdjusted / 255) * 100;
|
||||
}
|
||||
} else {
|
||||
this._brightnessSliderValue = 0;
|
||||
this._brightnessAdjusted = undefined;
|
||||
}
|
||||
this._brightnessSliderValue = Math.round(
|
||||
(stateObj.attributes.brightness * brightnessAdjust) / 255
|
||||
);
|
||||
this._ctSliderValue = stateObj.attributes.color_temp;
|
||||
this._wvSliderValue =
|
||||
stateObj.attributes.color_mode === LightColorModes.RGBW
|
||||
? Math.round((stateObj.attributes.rgbw_color[3] * 100) / 255)
|
||||
: undefined;
|
||||
this._cwSliderValue =
|
||||
stateObj.attributes.color_mode === LightColorModes.RGBWW
|
||||
? Math.round((stateObj.attributes.rgbww_color[3] * 100) / 255)
|
||||
: undefined;
|
||||
this._wwSliderValue =
|
||||
stateObj.attributes.color_mode === LightColorModes.RGBWW
|
||||
? Math.round((stateObj.attributes.rgbww_color[4] * 100) / 255)
|
||||
: undefined;
|
||||
this._colorBrightnessSliderValue =
|
||||
stateObj.attributes.color_mode === LightColorModes.RGBWW
|
||||
? Math.round(
|
||||
(Math.max(...stateObj.attributes.rgbww_color.slice(0, 3)) * 100) /
|
||||
255
|
||||
)
|
||||
: stateObj.attributes.color_mode === LightColorModes.RGBW
|
||||
? Math.round(
|
||||
(Math.max(...stateObj.attributes.rgbw_color.slice(0, 3)) * 100) /
|
||||
255
|
||||
)
|
||||
: undefined;
|
||||
|
||||
this._colorPickerColor = getLightRgbColor(stateObj)?.slice(0, 3) as
|
||||
| [number, number, number]
|
||||
| undefined;
|
||||
} else {
|
||||
this._brightnessSliderValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private _modeChanged(ev: CustomEvent) {
|
||||
this._mode = ev.detail.value;
|
||||
}
|
||||
|
||||
private _effectChanged(ev: CustomEvent) {
|
||||
const newVal = ev.detail.item.itemName;
|
||||
|
||||
@ -193,12 +328,29 @@ class MoreInfoLight extends LitElement {
|
||||
}
|
||||
|
||||
private _brightnessSliderChanged(ev: CustomEvent) {
|
||||
const bri = parseInt((ev.target as any).value, 10);
|
||||
const bri = Number((ev.target as any).value);
|
||||
|
||||
if (isNaN(bri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._brightnessAdjusted) {
|
||||
const rgb =
|
||||
this.stateObj!.attributes.rgb_color ||
|
||||
([0, 0, 0] as [number, number, number]);
|
||||
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
brightness_pct: bri,
|
||||
rgb_color: this._adjustColorBrightness(
|
||||
rgb,
|
||||
this._brightnessAdjusted,
|
||||
true
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
brightness_pct: bri,
|
||||
@ -206,7 +358,7 @@ class MoreInfoLight extends LitElement {
|
||||
}
|
||||
|
||||
private _ctSliderChanged(ev: CustomEvent) {
|
||||
const ct = parseInt((ev.target as any).value, 10);
|
||||
const ct = Number((ev.target as any).value);
|
||||
|
||||
if (isNaN(ct)) {
|
||||
return;
|
||||
@ -219,18 +371,64 @@ class MoreInfoLight extends LitElement {
|
||||
}
|
||||
|
||||
private _wvSliderChanged(ev: CustomEvent) {
|
||||
const wv = parseInt((ev.target as any).value, 10);
|
||||
const target = ev.target as any;
|
||||
let wv = Number(target.value);
|
||||
const name = target.name;
|
||||
|
||||
if (isNaN(wv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wv = (wv * 255) / 100;
|
||||
|
||||
const rgb = getLightRgbColor(this.stateObj!);
|
||||
|
||||
if (name === "wv") {
|
||||
const rgbw_color = rgb || [0, 0, 0, 0];
|
||||
rgbw_color[3] = wv;
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
rgbw_color,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const rgbww_color = rgb || [0, 0, 0, 0, 0];
|
||||
while (rgbww_color.length < 5) {
|
||||
rgbww_color.push(0);
|
||||
}
|
||||
rgbww_color[name === "cw" ? 3 : 4] = wv;
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
white_value: wv,
|
||||
rgbww_color,
|
||||
});
|
||||
}
|
||||
|
||||
private _colorBrightnessSliderChanged(ev: CustomEvent) {
|
||||
const target = ev.target as any;
|
||||
const value = Number(target.value);
|
||||
|
||||
const rgb = (getLightRgbColor(this.stateObj!)?.slice(0, 3) || [
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
]) as [number, number, number];
|
||||
|
||||
this._setRgbColor(
|
||||
this._adjustColorBrightness(
|
||||
// first normalize the value
|
||||
this._colorBrightnessSliderValue
|
||||
? this._adjustColorBrightness(
|
||||
rgb,
|
||||
this._colorBrightnessSliderValue,
|
||||
true
|
||||
)
|
||||
: rgb,
|
||||
value
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _segmentClick() {
|
||||
if (this._hueSegments === 24 && this._saturationSegments === 8) {
|
||||
this._hueSegments = 0;
|
||||
@ -241,15 +439,90 @@ class MoreInfoLight extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _adjustColorBrightness(
|
||||
rgbColor: [number, number, number],
|
||||
value?: number,
|
||||
invert = false
|
||||
) {
|
||||
if (value !== undefined && value !== 255) {
|
||||
let ratio = value / 255;
|
||||
if (invert) {
|
||||
ratio = 1 / ratio;
|
||||
}
|
||||
rgbColor[0] *= ratio;
|
||||
rgbColor[1] *= ratio;
|
||||
rgbColor[2] *= ratio;
|
||||
}
|
||||
return rgbColor;
|
||||
}
|
||||
|
||||
private _setRgbColor(rgbColor: [number, number, number]) {
|
||||
if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGBWW)) {
|
||||
const rgbww_color: [number, number, number, number, number] = this
|
||||
.stateObj!.attributes.rgbww_color
|
||||
? [...this.stateObj!.attributes.rgbww_color]
|
||||
: [0, 0, 0, 0, 0];
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
rgbww_color: rgbColor.concat(rgbww_color.slice(3)),
|
||||
});
|
||||
} else if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGBW)) {
|
||||
const rgbw_color: [number, number, number, number] = this.stateObj!
|
||||
.attributes.rgbw_color
|
||||
? [...this.stateObj!.attributes.rgbw_color]
|
||||
: [0, 0, 0, 0];
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
rgbw_color: rgbColor.concat(rgbw_color.slice(3)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new color has been picked.
|
||||
* should be throttled with the 'throttle=' attribute of the color picker
|
||||
*/
|
||||
private _colorPicked(ev: CustomEvent) {
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
hs_color: [ev.detail.hs.h, ev.detail.hs.s * 100],
|
||||
});
|
||||
if (
|
||||
lightSupportsColorMode(this.stateObj!, LightColorModes.RGBWW) ||
|
||||
lightSupportsColorMode(this.stateObj!, LightColorModes.RGBW)
|
||||
) {
|
||||
this._setRgbColor(
|
||||
this._colorBrightnessSliderValue
|
||||
? this._adjustColorBrightness(
|
||||
[ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b],
|
||||
this._colorBrightnessSliderValue
|
||||
)
|
||||
: [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b]
|
||||
);
|
||||
} else if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGB)) {
|
||||
const rgb_color = [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b] as [
|
||||
number,
|
||||
number,
|
||||
number
|
||||
];
|
||||
if (this._brightnessAdjusted) {
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
brightness_pct: this._brightnessSliderValue,
|
||||
rgb_color: this._adjustColorBrightness(
|
||||
rgb_color,
|
||||
this._brightnessAdjusted,
|
||||
true
|
||||
),
|
||||
});
|
||||
} else {
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
rgb_color,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
hs_color: [ev.detail.hs.h, ev.detail.hs.s * 100],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
@ -275,11 +548,18 @@ class MoreInfoLight extends LitElement {
|
||||
);
|
||||
/* The color temp minimum value shouldn't be rendered differently. It's not "off". */
|
||||
--paper-slider-knob-start-border-color: var(--primary-color);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.segmentationContainer {
|
||||
position: relative;
|
||||
max-height: 500px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
ha-button-toggle-group {
|
||||
margin: 8px 0px;
|
||||
}
|
||||
|
||||
ha-color-picker {
|
||||
@ -293,12 +573,19 @@ class MoreInfoLight extends LitElement {
|
||||
.segmentationButton {
|
||||
position: absolute;
|
||||
top: 5%;
|
||||
left: 0;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: var(--divider-color);
|
||||
border-bottom: none;
|
||||
margin: 8px 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import { stateIcon } from "../../../common/entity/state_icon";
|
||||
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
|
||||
import { iconColorCSS } from "../../../common/style/icon_color_css";
|
||||
import "../../../components/ha-card";
|
||||
import { LightEntity } from "../../../data/light";
|
||||
import { getLightRgbColor, LightEntity } from "../../../data/light";
|
||||
import { ActionHandlerEvent } from "../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
@ -301,14 +301,14 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
private _computeColor(stateObj: HassEntity | LightEntity): string {
|
||||
if (!stateObj.attributes.hs_color || !this._config?.state_color) {
|
||||
if (
|
||||
!this._config?.state_color ||
|
||||
computeStateDomain(stateObj) !== "light"
|
||||
) {
|
||||
return "";
|
||||
}
|
||||
const [hue, sat] = stateObj.attributes.hs_color;
|
||||
if (sat <= 10) {
|
||||
return "";
|
||||
}
|
||||
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
|
||||
const rgb = getLightRgbColor(stateObj as LightEntity);
|
||||
return rgb ? `rgb(${rgb.slice(0, 3).join(",")})` : "";
|
||||
}
|
||||
|
||||
private _handleAction(ev: ActionHandlerEvent) {
|
||||
|
@ -18,11 +18,14 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { stateIcon } from "../../../common/entity/state_icon";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";
|
||||
import { LightEntity, SUPPORT_BRIGHTNESS } from "../../../data/light";
|
||||
import {
|
||||
getLightRgbColor,
|
||||
LightEntity,
|
||||
lightSupportsDimming,
|
||||
} from "../../../data/light";
|
||||
import { ActionHandlerEvent } from "../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
@ -121,17 +124,14 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
|
||||
@value-changing=${this._dragEvent}
|
||||
@value-changed=${this._setBrightness}
|
||||
style=${styleMap({
|
||||
visibility: supportsFeature(stateObj, SUPPORT_BRIGHTNESS)
|
||||
visibility: lightSupportsDimming(stateObj)
|
||||
? "visible"
|
||||
: "hidden",
|
||||
})}
|
||||
></round-slider>
|
||||
<ha-icon-button
|
||||
class="light-button ${classMap({
|
||||
"slider-center": supportsFeature(
|
||||
stateObj,
|
||||
SUPPORT_BRIGHTNESS
|
||||
),
|
||||
"slider-center": lightSupportsDimming(stateObj),
|
||||
"state-on": stateObj.state === "on",
|
||||
"state-unavailable": stateObj.state === UNAVAILABLE,
|
||||
})}"
|
||||
@ -244,14 +244,11 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
private _computeColor(stateObj: LightEntity): string {
|
||||
if (stateObj.state === "off" || !stateObj.attributes.hs_color) {
|
||||
if (stateObj.state === "off") {
|
||||
return "";
|
||||
}
|
||||
const [hue, sat] = stateObj.attributes.hs_color;
|
||||
if (sat <= 10) {
|
||||
return "";
|
||||
}
|
||||
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
|
||||
const rgb = getLightRgbColor(stateObj);
|
||||
return rgb ? `rgb(${rgb.slice(0, 3).join(",")})` : "";
|
||||
}
|
||||
|
||||
private _handleAction(ev: ActionHandlerEvent) {
|
||||
|
@ -172,7 +172,10 @@
|
||||
"light": {
|
||||
"brightness": "Brightness",
|
||||
"color_temperature": "Color temperature",
|
||||
"white_value": "White value",
|
||||
"white_value": "White brightness",
|
||||
"color_brightness": "Color brightness",
|
||||
"cold_white_value": "Cold white brightness",
|
||||
"warm_white_value": "Warm white brightness",
|
||||
"effect": "Effect"
|
||||
},
|
||||
"lock": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user