Add filters to climate and light service descriptions (#86162)
* Add filters to climate and light service descriptions * Allow specifying enums directly * Update service descriptions * Adjust test * Cache entity features * Lint * Improve error handling, add list of known base components * Don't allow specifying an entity feature as intpull/82635/head^2
parent
c81a38effb
commit
9384ec18f8
|
@ -25,7 +25,7 @@ rules:
|
||||||
comments:
|
comments:
|
||||||
level: error
|
level: error
|
||||||
require-starting-space: true
|
require-starting-space: true
|
||||||
min-spaces-from-content: 2
|
min-spaces-from-content: 1
|
||||||
comments-indentation:
|
comments-indentation:
|
||||||
level: error
|
level: error
|
||||||
document-end:
|
document-end:
|
||||||
|
|
|
@ -6,6 +6,8 @@ set_aux_heat:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: climate
|
domain: climate
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.AUX_HEAT
|
||||||
fields:
|
fields:
|
||||||
aux_heat:
|
aux_heat:
|
||||||
name: Auxiliary heating
|
name: Auxiliary heating
|
||||||
|
@ -20,6 +22,8 @@ set_preset_mode:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: climate
|
domain: climate
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.PRESET_MODE
|
||||||
fields:
|
fields:
|
||||||
preset_mode:
|
preset_mode:
|
||||||
name: Preset mode
|
name: Preset mode
|
||||||
|
@ -35,10 +39,16 @@ set_temperature:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: climate
|
domain: climate
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.TARGET_TEMPERATURE
|
||||||
|
- climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||||
fields:
|
fields:
|
||||||
temperature:
|
temperature:
|
||||||
name: Temperature
|
name: Temperature
|
||||||
description: New target temperature for HVAC.
|
description: New target temperature for HVAC.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.TARGET_TEMPERATURE
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -48,6 +58,9 @@ set_temperature:
|
||||||
target_temp_high:
|
target_temp_high:
|
||||||
name: Target temperature high
|
name: Target temperature high
|
||||||
description: New target high temperature for HVAC.
|
description: New target high temperature for HVAC.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -58,6 +71,9 @@ set_temperature:
|
||||||
target_temp_low:
|
target_temp_low:
|
||||||
name: Target temperature low
|
name: Target temperature low
|
||||||
description: New target low temperature for HVAC.
|
description: New target low temperature for HVAC.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -92,6 +108,8 @@ set_humidity:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: climate
|
domain: climate
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.TARGET_HUMIDITY
|
||||||
fields:
|
fields:
|
||||||
humidity:
|
humidity:
|
||||||
name: Humidity
|
name: Humidity
|
||||||
|
@ -109,6 +127,8 @@ set_fan_mode:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: climate
|
domain: climate
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.FAN_MODE
|
||||||
fields:
|
fields:
|
||||||
fan_mode:
|
fan_mode:
|
||||||
name: Fan mode
|
name: Fan mode
|
||||||
|
@ -152,6 +172,8 @@ set_swing_mode:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: climate
|
domain: climate
|
||||||
|
supported_features:
|
||||||
|
- climate.ClimateEntityFeature.SWING_MODE
|
||||||
fields:
|
fields:
|
||||||
swing_mode:
|
swing_mode:
|
||||||
name: Swing mode
|
name: Swing mode
|
||||||
|
|
|
@ -12,6 +12,9 @@ turn_on:
|
||||||
transition:
|
transition:
|
||||||
name: Transition
|
name: Transition
|
||||||
description: Duration it takes to get to next state.
|
description: Duration it takes to get to next state.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.TRANSITION
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -20,11 +23,27 @@ turn_on:
|
||||||
rgb_color:
|
rgb_color:
|
||||||
name: Color
|
name: Color
|
||||||
description: The color for the light (based on RGB - red, green, blue).
|
description: The color for the light (based on RGB - red, green, blue).
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
selector:
|
selector:
|
||||||
color_rgb:
|
color_rgb:
|
||||||
rgbw_color:
|
rgbw_color:
|
||||||
name: RGBW-color
|
name: RGBW-color
|
||||||
description: A list containing four integers between 0 and 255 representing the RGBW (red, green, blue, white) color for the light.
|
description: A list containing four integers between 0 and 255 representing the RGBW (red, green, blue, white) color for the light.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
example: "[255, 100, 100, 50]"
|
example: "[255, 100, 100, 50]"
|
||||||
selector:
|
selector:
|
||||||
|
@ -32,6 +51,14 @@ turn_on:
|
||||||
rgbww_color:
|
rgbww_color:
|
||||||
name: RGBWW-color
|
name: RGBWW-color
|
||||||
description: A list containing five integers between 0 and 255 representing the RGBWW (red, green, blue, cold white, warm white) color for the light.
|
description: A list containing five integers between 0 and 255 representing the RGBWW (red, green, blue, cold white, warm white) color for the light.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
example: "[255, 100, 100, 50, 70]"
|
example: "[255, 100, 100, 50, 70]"
|
||||||
selector:
|
selector:
|
||||||
|
@ -39,6 +66,14 @@ turn_on:
|
||||||
color_name:
|
color_name:
|
||||||
name: Color name
|
name: Color name
|
||||||
description: A human readable color name.
|
description: A human readable color name.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
@ -195,6 +230,14 @@ turn_on:
|
||||||
hs_color:
|
hs_color:
|
||||||
name: Hue/Sat color
|
name: Hue/Sat color
|
||||||
description: Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100.
|
description: Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
example: "[300, 70]"
|
example: "[300, 70]"
|
||||||
selector:
|
selector:
|
||||||
|
@ -202,6 +245,14 @@ turn_on:
|
||||||
xy_color:
|
xy_color:
|
||||||
name: XY-color
|
name: XY-color
|
||||||
description: Color for the light in XY-format.
|
description: Color for the light in XY-format.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
example: "[0.52, 0.43]"
|
example: "[0.52, 0.43]"
|
||||||
selector:
|
selector:
|
||||||
|
@ -209,6 +260,15 @@ turn_on:
|
||||||
color_temp:
|
color_temp:
|
||||||
name: Color temperature
|
name: Color temperature
|
||||||
description: Color temperature for the light in mireds.
|
description: Color temperature for the light in mireds.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
selector:
|
selector:
|
||||||
color_temp:
|
color_temp:
|
||||||
min_mireds: 153
|
min_mireds: 153
|
||||||
|
@ -216,6 +276,15 @@ turn_on:
|
||||||
kelvin:
|
kelvin:
|
||||||
name: Color temperature (Kelvin)
|
name: Color temperature (Kelvin)
|
||||||
description: Color temperature for the light in Kelvin.
|
description: Color temperature for the light in Kelvin.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -228,6 +297,16 @@ turn_on:
|
||||||
description: Number indicating brightness, where 0 turns the light
|
description: Number indicating brightness, where 0 turns the light
|
||||||
off, 1 is the minimum brightness and 255 is the maximum brightness
|
off, 1 is the minimum brightness and 255 is the maximum brightness
|
||||||
supported by the light.
|
supported by the light.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.BRIGHTNESS
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -238,6 +317,16 @@ turn_on:
|
||||||
description: Number indicating percentage of full brightness, where 0
|
description: Number indicating percentage of full brightness, where 0
|
||||||
turns the light off, 1 is the minimum brightness and 100 is the maximum
|
turns the light off, 1 is the minimum brightness and 100 is the maximum
|
||||||
brightness supported by the light.
|
brightness supported by the light.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.BRIGHTNESS
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -246,6 +335,16 @@ turn_on:
|
||||||
brightness_step:
|
brightness_step:
|
||||||
name: Brightness step value
|
name: Brightness step value
|
||||||
description: Change brightness by an amount.
|
description: Change brightness by an amount.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.BRIGHTNESS
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -254,6 +353,16 @@ turn_on:
|
||||||
brightness_step_pct:
|
brightness_step_pct:
|
||||||
name: Brightness step
|
name: Brightness step
|
||||||
description: Change brightness by a percentage.
|
description: Change brightness by a percentage.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.BRIGHTNESS
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: -100
|
min: -100
|
||||||
|
@ -265,6 +374,10 @@ turn_on:
|
||||||
Set the light to white mode and change its brightness, where 0 turns
|
Set the light to white mode and change its brightness, where 0 turns
|
||||||
the light off, 1 is the minimum brightness and 255 is the maximum
|
the light off, 1 is the minimum brightness and 255 is the maximum
|
||||||
brightness supported by the light.
|
brightness supported by the light.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.WHITE
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -280,6 +393,9 @@ turn_on:
|
||||||
flash:
|
flash:
|
||||||
name: Flash
|
name: Flash
|
||||||
description: If the light should flash.
|
description: If the light should flash.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.FLASH
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
@ -291,6 +407,9 @@ turn_on:
|
||||||
effect:
|
effect:
|
||||||
name: Effect
|
name: Effect
|
||||||
description: Light effect.
|
description: Light effect.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.EFFECT
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
|
||||||
|
@ -304,6 +423,9 @@ turn_off:
|
||||||
transition:
|
transition:
|
||||||
name: Transition
|
name: Transition
|
||||||
description: Duration it takes to get to next state.
|
description: Duration it takes to get to next state.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.TRANSITION
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -312,6 +434,9 @@ turn_off:
|
||||||
flash:
|
flash:
|
||||||
name: Flash
|
name: Flash
|
||||||
description: If the light should flash.
|
description: If the light should flash.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.FLASH
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
@ -333,6 +458,9 @@ toggle:
|
||||||
transition:
|
transition:
|
||||||
name: Transition
|
name: Transition
|
||||||
description: Duration it takes to get to next state.
|
description: Duration it takes to get to next state.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.TRANSITION
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -341,6 +469,14 @@ toggle:
|
||||||
rgb_color:
|
rgb_color:
|
||||||
name: RGB-color
|
name: RGB-color
|
||||||
description: Color for the light in RGB-format.
|
description: Color for the light in RGB-format.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
example: "[255, 100, 100]"
|
example: "[255, 100, 100]"
|
||||||
selector:
|
selector:
|
||||||
|
@ -348,6 +484,14 @@ toggle:
|
||||||
color_name:
|
color_name:
|
||||||
name: Color name
|
name: Color name
|
||||||
description: A human readable color name.
|
description: A human readable color name.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
@ -504,6 +648,14 @@ toggle:
|
||||||
hs_color:
|
hs_color:
|
||||||
name: Hue/Sat color
|
name: Hue/Sat color
|
||||||
description: Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100.
|
description: Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
example: "[300, 70]"
|
example: "[300, 70]"
|
||||||
selector:
|
selector:
|
||||||
|
@ -511,6 +663,14 @@ toggle:
|
||||||
xy_color:
|
xy_color:
|
||||||
name: XY-color
|
name: XY-color
|
||||||
description: Color for the light in XY-format.
|
description: Color for the light in XY-format.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
example: "[0.52, 0.43]"
|
example: "[0.52, 0.43]"
|
||||||
selector:
|
selector:
|
||||||
|
@ -518,12 +678,30 @@ toggle:
|
||||||
color_temp:
|
color_temp:
|
||||||
name: Color temperature (mireds)
|
name: Color temperature (mireds)
|
||||||
description: Color temperature for the light in mireds.
|
description: Color temperature for the light in mireds.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
color_temp:
|
color_temp:
|
||||||
kelvin:
|
kelvin:
|
||||||
name: Color temperature (Kelvin)
|
name: Color temperature (Kelvin)
|
||||||
description: Color temperature for the light in Kelvin.
|
description: Color temperature for the light in Kelvin.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -536,6 +714,16 @@ toggle:
|
||||||
description: Number indicating brightness, where 0 turns the light
|
description: Number indicating brightness, where 0 turns the light
|
||||||
off, 1 is the minimum brightness and 255 is the maximum brightness
|
off, 1 is the minimum brightness and 255 is the maximum brightness
|
||||||
supported by the light.
|
supported by the light.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.BRIGHTNESS
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
|
@ -546,6 +734,16 @@ toggle:
|
||||||
description: Number indicating percentage of full brightness, where 0
|
description: Number indicating percentage of full brightness, where 0
|
||||||
turns the light off, 1 is the minimum brightness and 100 is the maximum
|
turns the light off, 1 is the minimum brightness and 100 is the maximum
|
||||||
brightness supported by the light.
|
brightness supported by the light.
|
||||||
|
filter:
|
||||||
|
attribute:
|
||||||
|
supported_color_modes:
|
||||||
|
- light.ColorMode.BRIGHTNESS
|
||||||
|
- light.ColorMode.COLOR_TEMP
|
||||||
|
- light.ColorMode.HS
|
||||||
|
- light.ColorMode.XY
|
||||||
|
- light.ColorMode.RGB
|
||||||
|
- light.ColorMode.RGBW
|
||||||
|
- light.ColorMode.RGBWW
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -561,6 +759,9 @@ toggle:
|
||||||
flash:
|
flash:
|
||||||
name: Flash
|
name: Flash
|
||||||
description: If the light should flash.
|
description: If the light should flash.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.FLASH
|
||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
@ -572,5 +773,8 @@ toggle:
|
||||||
effect:
|
effect:
|
||||||
name: Effect
|
name: Effect
|
||||||
description: Light effect.
|
description: Light effect.
|
||||||
|
filter:
|
||||||
|
supported_features:
|
||||||
|
- light.LightEntityFeature.EFFECT
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable, Mapping, Sequence
|
from collections.abc import Callable, Mapping, Sequence
|
||||||
|
from enum import IntFlag
|
||||||
|
from functools import cache
|
||||||
from typing import Any, Generic, Literal, TypedDict, TypeVar, cast
|
from typing import Any, Generic, Literal, TypedDict, TypeVar, cast
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
@ -79,6 +81,69 @@ class Selector(Generic[_T]):
|
||||||
return {"selector": {self.selector_type: self.config}}
|
return {"selector": {self.selector_type: self.config}}
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def _entity_features() -> dict[str, type[IntFlag]]:
|
||||||
|
"""Return a cached lookup of entity feature enums."""
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
from homeassistant.components.alarm_control_panel import (
|
||||||
|
AlarmControlPanelEntityFeature,
|
||||||
|
)
|
||||||
|
from homeassistant.components.calendar import CalendarEntityFeature
|
||||||
|
from homeassistant.components.camera import CameraEntityFeature
|
||||||
|
from homeassistant.components.climate import ClimateEntityFeature
|
||||||
|
from homeassistant.components.cover import CoverEntityFeature
|
||||||
|
from homeassistant.components.fan import FanEntityFeature
|
||||||
|
from homeassistant.components.humidifier import HumidifierEntityFeature
|
||||||
|
from homeassistant.components.light import LightEntityFeature
|
||||||
|
from homeassistant.components.lock import LockEntityFeature
|
||||||
|
from homeassistant.components.media_player import MediaPlayerEntityFeature
|
||||||
|
from homeassistant.components.remote import RemoteEntityFeature
|
||||||
|
from homeassistant.components.siren import SirenEntityFeature
|
||||||
|
from homeassistant.components.update import UpdateEntityFeature
|
||||||
|
from homeassistant.components.vacuum import VacuumEntityFeature
|
||||||
|
from homeassistant.components.water_heater import WaterHeaterEntityFeature
|
||||||
|
|
||||||
|
return {
|
||||||
|
"AlarmControlPanelEntityFeature": AlarmControlPanelEntityFeature,
|
||||||
|
"CalendarEntityFeature": CalendarEntityFeature,
|
||||||
|
"CameraEntityFeature": CameraEntityFeature,
|
||||||
|
"ClimateEntityFeature": ClimateEntityFeature,
|
||||||
|
"CoverEntityFeature": CoverEntityFeature,
|
||||||
|
"FanEntityFeature": FanEntityFeature,
|
||||||
|
"HumidifierEntityFeature": HumidifierEntityFeature,
|
||||||
|
"LightEntityFeature": LightEntityFeature,
|
||||||
|
"LockEntityFeature": LockEntityFeature,
|
||||||
|
"MediaPlayerEntityFeature": MediaPlayerEntityFeature,
|
||||||
|
"RemoteEntityFeature": RemoteEntityFeature,
|
||||||
|
"SirenEntityFeature": SirenEntityFeature,
|
||||||
|
"UpdateEntityFeature": UpdateEntityFeature,
|
||||||
|
"VacuumEntityFeature": VacuumEntityFeature,
|
||||||
|
"WaterHeaterEntityFeature": WaterHeaterEntityFeature,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_supported_feature(supported_feature: int | str) -> int:
|
||||||
|
"""Validate a supported feature and resolve an enum string to its value."""
|
||||||
|
|
||||||
|
if isinstance(supported_feature, int):
|
||||||
|
return supported_feature
|
||||||
|
|
||||||
|
known_entity_features = _entity_features()
|
||||||
|
|
||||||
|
try:
|
||||||
|
_, enum, feature = supported_feature.split(".", 2)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise vol.Invalid(
|
||||||
|
f"Invalid supported feature '{supported_feature}', expected "
|
||||||
|
"<domain>.<enum>.<member>"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
try:
|
||||||
|
return cast(int, getattr(known_entity_features[enum], feature).value)
|
||||||
|
except (AttributeError, KeyError) as exc:
|
||||||
|
raise vol.Invalid(f"Unknown supported feature '{supported_feature}'") from exc
|
||||||
|
|
||||||
|
|
||||||
ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
|
ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
# Integration that provided the entity
|
# Integration that provided the entity
|
||||||
|
@ -87,6 +152,8 @@ ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
|
||||||
vol.Optional("domain"): vol.All(cv.ensure_list, [str]),
|
vol.Optional("domain"): vol.All(cv.ensure_list, [str]),
|
||||||
# Device class of the entity
|
# Device class of the entity
|
||||||
vol.Optional("device_class"): vol.All(cv.ensure_list, [str]),
|
vol.Optional("device_class"): vol.All(cv.ensure_list, [str]),
|
||||||
|
# Features supported by the entity
|
||||||
|
vol.Optional("supported_features"): [vol.All(str, _validate_supported_feature)],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,6 +164,7 @@ class EntityFilterSelectorConfig(TypedDict, total=False):
|
||||||
integration: str
|
integration: str
|
||||||
domain: str | list[str]
|
domain: str | list[str]
|
||||||
device_class: str | list[str]
|
device_class: str | list[str]
|
||||||
|
supported_features: list[str]
|
||||||
|
|
||||||
|
|
||||||
DEVICE_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
|
DEVICE_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
|
||||||
|
|
|
@ -4,9 +4,11 @@ from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Awaitable, Callable, Iterable
|
from collections.abc import Awaitable, Callable, Iterable
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from functools import partial, wraps
|
from enum import Enum
|
||||||
|
from functools import cache, partial, wraps
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Any, TypedDict, TypeGuard, TypeVar
|
from types import ModuleType
|
||||||
|
from typing import TYPE_CHECKING, Any, TypedDict, TypeGuard, TypeVar, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -42,6 +44,7 @@ from . import (
|
||||||
entity_registry,
|
entity_registry,
|
||||||
template,
|
template,
|
||||||
)
|
)
|
||||||
|
from .selector import TargetSelector
|
||||||
from .typing import ConfigType, TemplateVarsType
|
from .typing import ConfigType, TemplateVarsType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -58,6 +61,112 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
SERVICE_DESCRIPTION_CACHE = "service_description_cache"
|
SERVICE_DESCRIPTION_CACHE = "service_description_cache"
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def _base_components() -> dict[str, ModuleType]:
|
||||||
|
"""Return a cached lookup of base components."""
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
from homeassistant.components import (
|
||||||
|
alarm_control_panel,
|
||||||
|
calendar,
|
||||||
|
camera,
|
||||||
|
climate,
|
||||||
|
cover,
|
||||||
|
fan,
|
||||||
|
humidifier,
|
||||||
|
light,
|
||||||
|
lock,
|
||||||
|
media_player,
|
||||||
|
remote,
|
||||||
|
siren,
|
||||||
|
update,
|
||||||
|
vacuum,
|
||||||
|
water_heater,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"alarm_control_panel": alarm_control_panel,
|
||||||
|
"calendar": calendar,
|
||||||
|
"camera": camera,
|
||||||
|
"climate": climate,
|
||||||
|
"cover": cover,
|
||||||
|
"fan": fan,
|
||||||
|
"humidifier": humidifier,
|
||||||
|
"light": light,
|
||||||
|
"lock": lock,
|
||||||
|
"media_player": media_player,
|
||||||
|
"remote": remote,
|
||||||
|
"siren": siren,
|
||||||
|
"update": update,
|
||||||
|
"vacuum": vacuum,
|
||||||
|
"water_heater": water_heater,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_option_or_feature(option_or_feature: str, label: str) -> Any:
|
||||||
|
"""Validate attribute option or supported feature."""
|
||||||
|
try:
|
||||||
|
domain, enum, option = option_or_feature.split(".", 2)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise vol.Invalid(
|
||||||
|
f"Invalid {label} '{option_or_feature}', expected "
|
||||||
|
"<domain>.<enum>.<member>"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
base_components = _base_components()
|
||||||
|
if not (base_component := base_components.get(domain)):
|
||||||
|
raise vol.Invalid(f"Unknown base component '{domain}'")
|
||||||
|
|
||||||
|
try:
|
||||||
|
attribute_enum = getattr(base_component, enum)
|
||||||
|
except AttributeError as exc:
|
||||||
|
raise vol.Invalid(f"Unknown {label} enum '{domain}.{enum}'") from exc
|
||||||
|
|
||||||
|
if not issubclass(attribute_enum, Enum):
|
||||||
|
raise vol.Invalid(f"Expected {label} '{domain}.{enum}' to be an enum")
|
||||||
|
|
||||||
|
try:
|
||||||
|
return getattr(attribute_enum, option).value
|
||||||
|
except AttributeError as exc:
|
||||||
|
raise vol.Invalid(f"Unknown {label} '{enum}.{option}'") from exc
|
||||||
|
|
||||||
|
|
||||||
|
def validate_attribute_option(attribute_option: str) -> Any:
|
||||||
|
"""Validate attribute option."""
|
||||||
|
return _validate_option_or_feature(attribute_option, "attribute option")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_supported_feature(supported_feature: str) -> Any:
|
||||||
|
"""Validate supported feature."""
|
||||||
|
return _validate_option_or_feature(supported_feature, "supported feature")
|
||||||
|
|
||||||
|
|
||||||
|
# Basic schemas which translate attribute and supported feature enum names
|
||||||
|
# to their values. Full validation is done by hassfest.services
|
||||||
|
_FIELD_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional("filter"): {
|
||||||
|
vol.Optional("attribute"): {
|
||||||
|
vol.Required(str): [vol.All(str, validate_attribute_option)],
|
||||||
|
},
|
||||||
|
vol.Optional("supported_features"): [
|
||||||
|
vol.All(str, validate_supported_feature)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
)
|
||||||
|
|
||||||
|
_SERVICE_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional("target"): vol.Any(TargetSelector.CONFIG_SCHEMA, None),
|
||||||
|
vol.Optional("fields"): vol.Schema({str: _FIELD_SCHEMA}),
|
||||||
|
},
|
||||||
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
)
|
||||||
|
|
||||||
|
_SERVICES_SCHEMA = vol.Schema({cv.slug: _SERVICE_SCHEMA})
|
||||||
|
|
||||||
|
|
||||||
class ServiceParams(TypedDict):
|
class ServiceParams(TypedDict):
|
||||||
"""Type for service call parameters."""
|
"""Type for service call parameters."""
|
||||||
|
|
||||||
|
@ -421,13 +530,16 @@ async def async_extract_config_entry_ids(
|
||||||
def _load_services_file(hass: HomeAssistant, integration: Integration) -> JSON_TYPE:
|
def _load_services_file(hass: HomeAssistant, integration: Integration) -> JSON_TYPE:
|
||||||
"""Load services file for an integration."""
|
"""Load services file for an integration."""
|
||||||
try:
|
try:
|
||||||
return load_yaml(str(integration.file_path / "services.yaml"))
|
return cast(
|
||||||
|
JSON_TYPE,
|
||||||
|
_SERVICES_SCHEMA(load_yaml(str(integration.file_path / "services.yaml"))),
|
||||||
|
)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Unable to find services.yaml for the %s integration", integration.domain
|
"Unable to find services.yaml for the %s integration", integration.domain
|
||||||
)
|
)
|
||||||
return {}
|
return {}
|
||||||
except HomeAssistantError:
|
except (HomeAssistantError, vol.Invalid):
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Unable to parse services.yaml for the %s integration", integration.domain
|
"Unable to parse services.yaml for the %s integration", integration.domain
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from voluptuous.humanize import humanize_error
|
||||||
|
|
||||||
from homeassistant.const import CONF_SELECTOR
|
from homeassistant.const import CONF_SELECTOR
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import config_validation as cv, selector
|
from homeassistant.helpers import config_validation as cv, selector, service
|
||||||
from homeassistant.util.yaml import load_yaml
|
from homeassistant.util.yaml import load_yaml
|
||||||
|
|
||||||
from .model import Config, Integration
|
from .model import Config, Integration
|
||||||
|
@ -33,6 +33,14 @@ FIELD_SCHEMA = vol.Schema(
|
||||||
vol.Optional("required"): bool,
|
vol.Optional("required"): bool,
|
||||||
vol.Optional("advanced"): bool,
|
vol.Optional("advanced"): bool,
|
||||||
vol.Optional(CONF_SELECTOR): selector.validate_selector,
|
vol.Optional(CONF_SELECTOR): selector.validate_selector,
|
||||||
|
vol.Optional("filter"): {
|
||||||
|
vol.Optional("attribute"): {
|
||||||
|
vol.Required(str): [vol.All(str, service.validate_attribute_option)],
|
||||||
|
},
|
||||||
|
vol.Optional("supported_features"): [
|
||||||
|
vol.All(str, service.validate_supported_feature)
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,9 +48,7 @@ SERVICE_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required("description"): str,
|
vol.Required("description"): str,
|
||||||
vol.Optional("name"): str,
|
vol.Optional("name"): str,
|
||||||
vol.Optional("target"): vol.Any(
|
vol.Optional("target"): vol.Any(selector.TargetSelector.CONFIG_SCHEMA, None),
|
||||||
selector.TargetSelector.CONFIG_SCHEMA, None # pylint: disable=no-member
|
|
||||||
),
|
|
||||||
vol.Optional("fields"): vol.Schema({str: FIELD_SCHEMA}),
|
vol.Optional("fields"): vol.Schema({str: FIELD_SCHEMA}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -69,10 +69,9 @@ def _test_selector(
|
||||||
|
|
||||||
# Serialize selector
|
# Serialize selector
|
||||||
selector_instance = selector.selector({selector_type: schema})
|
selector_instance = selector.selector({selector_type: schema})
|
||||||
assert (
|
assert selector_instance.serialize() == {
|
||||||
selector.selector(selector_instance.serialize()["selector"]).config
|
"selector": {selector_type: selector_instance.config}
|
||||||
== selector_instance.config
|
}
|
||||||
)
|
|
||||||
# Test serialized selector can be dumped to YAML
|
# Test serialized selector can be dumped to YAML
|
||||||
yaml.dump(selector_instance.serialize())
|
yaml.dump(selector_instance.serialize())
|
||||||
|
|
||||||
|
@ -227,6 +226,29 @@ def test_device_selector_schema(schema, valid_selections, invalid_selections) ->
|
||||||
("light.abc123", "binary_sensor.abc123", FAKE_UUID),
|
("light.abc123", "binary_sensor.abc123", FAKE_UUID),
|
||||||
(None,),
|
(None,),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"filter": [
|
||||||
|
{"supported_features": ["light.LightEntityFeature.EFFECT"]},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
("light.abc123", "blah.blah", FAKE_UUID),
|
||||||
|
(None,),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"filter": [
|
||||||
|
{
|
||||||
|
"supported_features": [
|
||||||
|
"light.LightEntityFeature.EFFECT",
|
||||||
|
"light.LightEntityFeature.TRANSITION",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
("light.abc123", "blah.blah", FAKE_UUID),
|
||||||
|
(None,),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_entity_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
def test_entity_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
||||||
|
@ -234,6 +256,25 @@ def test_entity_selector_schema(schema, valid_selections, invalid_selections) ->
|
||||||
_test_selector("entity", schema, valid_selections, invalid_selections)
|
_test_selector("entity", schema, valid_selections, invalid_selections)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"schema",
|
||||||
|
(
|
||||||
|
# Feature should be string specifying an enum member, not an int
|
||||||
|
{"filter": [{"supported_features": [1]}]},
|
||||||
|
# Invalid feature
|
||||||
|
{"filter": [{"supported_features": ["blah"]}]},
|
||||||
|
# Unknown feature enum
|
||||||
|
{"filter": [{"supported_features": ["blah.FooEntityFeature.blah"]}]},
|
||||||
|
# Unknown feature enum member
|
||||||
|
{"filter": [{"supported_features": ["light.LightEntityFeature.blah"]}]},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_entity_selector_schema_error(schema) -> None:
|
||||||
|
"""Test number selector."""
|
||||||
|
with pytest.raises(vol.Invalid):
|
||||||
|
selector.validate_selector({"entity": schema})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("schema", "valid_selections", "invalid_selections"),
|
("schema", "valid_selections", "invalid_selections"),
|
||||||
(
|
(
|
||||||
|
@ -359,7 +400,7 @@ def test_addon_selector_schema(schema, valid_selections, invalid_selections) ->
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("schema", "valid_selections", "invalid_selections"),
|
("schema", "valid_selections", "invalid_selections"),
|
||||||
(({}, (1, "one", None), ()),), # Everything can be coarced to bool
|
(({}, (1, "one", None), ()),), # Everything can be coerced to bool
|
||||||
)
|
)
|
||||||
def test_boolean_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
def test_boolean_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
||||||
"""Test boolean selector."""
|
"""Test boolean selector."""
|
||||||
|
|
Loading…
Reference in New Issue