- ${supportsBrightness
+ ${lightSupportsBrightness(this.stateObj)
? html`
-
-
+
`
: ""}
- ${supportsColorTemp || supportsColor || supportsEffects
+ ${this.stateObj.state === "on"
? html`
-
+ icon="hass:brightness-7"
+ max="100"
+ .value=${this._colorBrightnessSliderValue}
+ @change=${this._colorBrightnessSliderChanged}
+ pin
+ >`
+ : ""}
+ ${supportsRgbw
+ ? html`
+
+ `
+ : ""}
+ ${supportsRgbww
+ ? html`
+
+
+ `
+ : ""}
+ `
+ : ""}
+ ${supportsFeature(this.stateObj, LightEntityFeature.EFFECT) &&
+ this.stateObj!.attributes.effect_list?.length
+ ? html`
+
+
+ ${this.stateObj.attributes.effect_list.map(
+ (effect: string) => html`
+
+ ${effect}
+
+ `
+ )}
+
+ `
+ : ""}
`
- : null}
+ : ""}
{
- showLightColorPickerDialog(this, { entityId: this.stateObj!.entity_id });
- };
+ public willUpdate(changedProps: PropertyValues) {
+ super.willUpdate(changedProps);
- private _handleEffectButton(ev) {
- ev.stopPropagation();
- ev.preventDefault();
+ if (!changedProps.has("stateObj")) {
+ return;
+ }
+ const stateObj = this.stateObj! as LightEntity;
+ const oldStateObj = changedProps.get("stateObj") as LightEntity | undefined;
- const index = ev.detail.index;
- const effect = this.stateObj!.attributes.effect_list![index];
+ 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"
+ : this.stateObj!.attributes.color_mode;
+ }
- if (!effect || this.stateObj!.attributes.effect === effect) {
+ let brightnessAdjust = 100;
+ this._brightnessAdjusted = undefined;
+ if (
+ stateObj.attributes.color_mode === LightColorMode.RGB &&
+ !lightSupportsColorMode(stateObj, LightColorMode.RGBWW) &&
+ !lightSupportsColorMode(stateObj, LightColorMode.RGBW)
+ ) {
+ const maxVal = Math.max(...stateObj.attributes.rgb_color!);
+ if (maxVal < 255) {
+ this._brightnessAdjusted = maxVal;
+ brightnessAdjust = (this._brightnessAdjusted / 255) * 100;
+ }
+ }
+ this._brightnessSliderValue = Math.round(
+ ((stateObj.attributes.brightness || 0) * brightnessAdjust) / 255
+ );
+ this._ctSliderValue = stateObj.attributes.color_temp_kelvin;
+ this._wvSliderValue =
+ stateObj.attributes.color_mode === LightColorMode.RGBW
+ ? Math.round((stateObj.attributes.rgbw_color![3] * 100) / 255)
+ : undefined;
+ this._cwSliderValue =
+ stateObj.attributes.color_mode === LightColorMode.RGBWW
+ ? Math.round((stateObj.attributes.rgbww_color![3] * 100) / 255)
+ : undefined;
+ this._wwSliderValue =
+ stateObj.attributes.color_mode === LightColorMode.RGBWW
+ ? Math.round((stateObj.attributes.rgbww_color![4] * 100) / 255)
+ : undefined;
+
+ const currentRgbColor = getLightCurrentModeRgbColor(stateObj);
+
+ this._colorBrightnessSliderValue = currentRgbColor
+ ? Math.round((Math.max(...currentRgbColor.slice(0, 3)) * 100) / 255)
+ : undefined;
+
+ this._colorPickerColor = currentRgbColor?.slice(0, 3) as [
+ number,
+ number,
+ number
+ ];
+ } else {
+ this._brightnessSliderValue = 0;
+ }
+ }
+
+ private _toggleButtons = memoizeOne(
+ (supportsTemp: boolean, supportsWhite: boolean) => {
+ const modes = [{ label: "Color", value: "color" }];
+ if (supportsTemp) {
+ modes.push({ label: "Temperature", value: LightColorMode.COLOR_TEMP });
+ }
+ if (supportsWhite) {
+ modes.push({ label: "White", value: LightColorMode.WHITE });
+ }
+ return modes;
+ }
+ );
+
+ private _modeChanged(ev: CustomEvent) {
+ this._mode = ev.detail.value;
+ }
+
+ private _effectChanged(ev) {
+ const newVal = ev.target.value;
+
+ if (!newVal || this.stateObj!.attributes.effect === newVal) {
return;
}
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
- effect,
+ effect: newVal,
});
}
+ private _brightnessSliderChanged(ev: CustomEvent) {
+ const bri = Number((ev.target as any).value);
+
+ if (isNaN(bri)) {
+ return;
+ }
+
+ this._brightnessSliderValue = bri;
+
+ if (this._mode === LightColorMode.WHITE) {
+ this.hass.callService("light", "turn_on", {
+ entity_id: this.stateObj!.entity_id,
+ white: Math.min(255, Math.round((bri * 255) / 100)),
+ });
+ 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,
+ });
+ }
+
+ private _ctSliderChanged(ev: CustomEvent) {
+ const ct = Number((ev.target as any).value);
+
+ if (isNaN(ct)) {
+ return;
+ }
+
+ this._ctSliderValue = ct;
+
+ this.hass.callService("light", "turn_on", {
+ entity_id: this.stateObj!.entity_id,
+ color_temp_kelvin: ct,
+ });
+ }
+
+ private _wvSliderChanged(ev: CustomEvent) {
+ const target = ev.target as any;
+ let wv = Number(target.value);
+ const name = target.name;
+
+ if (isNaN(wv)) {
+ return;
+ }
+
+ if (name === "wv") {
+ this._wvSliderValue = wv;
+ } else if (name === "cw") {
+ this._cwSliderValue = wv;
+ } else if (name === "ww") {
+ this._wwSliderValue = wv;
+ }
+
+ wv = Math.min(255, Math.round((wv * 255) / 100));
+
+ const rgb = getLightCurrentModeRgbColor(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,
+ rgbww_color,
+ });
+ }
+
+ private _colorBrightnessSliderChanged(ev: CustomEvent) {
+ const target = ev.target as any;
+ let value = Number(target.value);
+
+ if (isNaN(value)) {
+ return;
+ }
+
+ const oldValue = this._colorBrightnessSliderValue;
+ this._colorBrightnessSliderValue = value;
+
+ value = (value * 255) / 100;
+
+ const rgb = (getLightCurrentModeRgbColor(this.stateObj!)?.slice(0, 3) || [
+ 255, 255, 255,
+ ]) as [number, number, number];
+
+ this._setRgbWColor(
+ this._adjustColorBrightness(
+ // first normalize the value
+ oldValue
+ ? this._adjustColorBrightness(rgb, (oldValue * 255) / 100, true)
+ : rgb,
+ value
+ )
+ );
+ }
+
+ private _segmentClick() {
+ if (this._hueSegments === 24 && this._saturationSegments === 8) {
+ this._hueSegments = 0;
+ this._saturationSegments = 0;
+ } else {
+ this._hueSegments = 24;
+ this._saturationSegments = 8;
+ }
+ }
+
+ 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] = Math.min(255, Math.round(rgbColor[0] * ratio));
+ rgbColor[1] = Math.min(255, Math.round(rgbColor[1] * ratio));
+ rgbColor[2] = Math.min(255, Math.round(rgbColor[2] * ratio));
+ }
+ return rgbColor;
+ }
+
+ private _setRgbWColor(rgbColor: [number, number, number]) {
+ if (lightSupportsColorMode(this.stateObj!, LightColorMode.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!, LightColorMode.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<{
+ hs: { h: number; s: number };
+ rgb: { r: number; g: number; b: number };
+ }>
+ ) {
+ this._colorPickerColor = [
+ ev.detail.rgb.r,
+ ev.detail.rgb.g,
+ ev.detail.rgb.b,
+ ];
+
+ if (
+ lightSupportsColorMode(this.stateObj!, LightColorMode.RGBWW) ||
+ lightSupportsColorMode(this.stateObj!, LightColorMode.RGBW)
+ ) {
+ this._setRgbWColor(
+ this._colorBrightnessSliderValue
+ ? this._adjustColorBrightness(
+ [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b],
+ (this._colorBrightnessSliderValue * 255) / 100
+ )
+ : [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b]
+ );
+ } else if (lightSupportsColorMode(this.stateObj!, LightColorMode.RGB)) {
+ const rgb_color: [number, number, number] = [
+ ev.detail.rgb.r,
+ ev.detail.rgb.g,
+ ev.detail.rgb.b,
+ ];
+ 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(): CSSResultGroup {
return css`
.content {
@@ -152,18 +577,48 @@ class MoreInfoLight extends LitElement {
width: 100%;
}
- .buttons {
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 12px;
- }
- .buttons > * {
- margin: 4px;
+ .color_temp {
+ --ha-slider-background: -webkit-linear-gradient(
+ var(--float-end),
+ rgb(166, 209, 255) 0%,
+ white 50%,
+ rgb(255, 160, 0) 100%
+ );
+ /* 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;
}
- ha-more-info-light-brightness {
- margin-bottom: 16px;
+ .segmentationContainer {
+ position: relative;
+ max-height: 500px;
+ display: flex;
+ justify-content: center;
+ }
+
+ ha-button-toggle-group {
+ margin-bottom: 8px;
+ }
+
+ ha-color-picker {
+ --ha-color-picker-wheel-borderwidth: 5;
+ --ha-color-picker-wheel-bordercolor: white;
+ --ha-color-picker-wheel-shadow: none;
+ --ha-color-picker-marker-borderwidth: 2;
+ --ha-color-picker-marker-bordercolor: white;
+ }
+
+ .segmentationButton {
+ position: absolute;
+ top: 5%;
+ left: 0;
+ color: var(--secondary-text-color);
+ }
+
+ hr {
+ border-color: var(--divider-color);
+ border-bottom: none;
+ margin: 16px 0;
}
`;
}
diff --git a/src/dialogs/more-info/state_more_info_control.ts b/src/dialogs/more-info/state_more_info_control.ts
index ad5d0b64e0..d4ebaed326 100644
--- a/src/dialogs/more-info/state_more_info_control.ts
+++ b/src/dialogs/more-info/state_more_info_control.ts
@@ -1,9 +1,9 @@
import type { HassEntity } from "home-assistant-js-websocket";
-import { computeStateDomain } from "../../common/entity/compute_state_domain";
import {
- DOMAINS_HIDE_DEFAULT_MORE_INFO,
DOMAINS_WITH_MORE_INFO,
+ DOMAINS_HIDE_DEFAULT_MORE_INFO,
} from "./const";
+import { computeStateDomain } from "../../common/entity/compute_state_domain";
const LAZY_LOADED_MORE_INFO_CONTROL = {
alarm_control_panel: () => import("./controls/more-info-alarm_control_panel"),
@@ -33,6 +33,7 @@ const LAZY_LOADED_MORE_INFO_CONTROL = {
export const stateMoreInfoType = (stateObj: HassEntity): string => {
const domain = computeStateDomain(stateObj);
+
return domainMoreInfoType(domain);
};
diff --git a/src/translations/en.json b/src/translations/en.json
index 772462f2a2..c063af0864 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -889,18 +889,6 @@
},
"zone": {
"graph_unit": "People home"
- },
- "light": {
- "change_color": "Change color",
- "select_effect": "Select effect",
- "color_picker": {
- "title": "Change color",
- "mode": {
- "color": "Color",
- "color_temp": "Temperature",
- "white": "White"
- }
- }
}
},
"entity_registry": {
diff --git a/yarn.lock b/yarn.lock
index f4446890ba..621c90a886 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3117,16 +3117,6 @@ __metadata:
languageName: node
linkType: hard
-"@material/web@npm:^1.0.0-pre.1":
- version: 1.0.0-pre.1
- resolution: "@material/web@npm:1.0.0-pre.1"
- dependencies:
- lit: ^2.3.0
- tslib: ^2.4.0
- checksum: 87716cb760020380f797feebb066a6e22ecd9d97b2fdb697977ca9af2b58b7422737a38cb0639cdb1124669d650ce3cfe1e77642738ef36556116ced57d82a1f
- languageName: node
- linkType: hard
-
"@mdi/js@npm:7.1.96":
version: 7.1.96
resolution: "@mdi/js@npm:7.1.96"
@@ -9503,7 +9493,6 @@ fsevents@^1.2.7:
"@material/mwc-textfield": ^0.27.0
"@material/mwc-top-app-bar-fixed": ^0.27.0
"@material/top-app-bar": =14.0.0-canary.53b3cad2f.0
- "@material/web": ^1.0.0-pre.1
"@mdi/js": 7.1.96
"@mdi/svg": 7.1.96
"@octokit/auth-oauth-device": ^4.0.2
@@ -11225,7 +11214,7 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
-"lit@npm:^2.0.0, lit@npm:^2.0.0-rc.2, lit@npm:^2.2.1, lit@npm:^2.3.0, lit@npm:^2.5.0, lit@npm:^2.6.1":
+"lit@npm:^2.0.0, lit@npm:^2.0.0-rc.2, lit@npm:^2.2.1, lit@npm:^2.5.0, lit@npm:^2.6.1":
version: 2.6.1
resolution: "lit@npm:2.6.1"
dependencies: