Enhance configuration generation and merging

This commit is contained in:
2026-05-18 13:17:24 +02:00
parent 545cb824f5
commit fa36a8e4aa
9 changed files with 273 additions and 85 deletions
+15 -9
View File
@@ -2,28 +2,34 @@
A python script that generates a full ESPHome config for the Button+ V2 A python script that generates a full ESPHome config for the Button+ V2
## Project uses pipenv ## Features
- Notification Service
- And toasts!
- Generate pages and all buttons and events belonging to each page
## Setup
### Project uses pipenv
run `pipenv install` to setup the project the first time. run `pipenv install` to setup the project the first time.
run `pipenv shell` after that to open the virtual environment. run `pipenv shell` after that to open the virtual environment.
You can then use `python
You can then use `python src/main.py --help` to run the generator
## Config example ## Config example
The configuration (handmade) on which this generator is based, can be found in the `esphome` folder. The configuration (handmade) on which this generator is based, can be found in the `esphome` folder.
# Todo ## Todo
## Ideas ### Ideas
- Notification service: until dismissed, or with timeout
- Toast service, see above
- SHow image service - SHow image service
- LED effects: flash, breathe - LED effects: flash, breathe
- Top level config only for drawing, everything else hidden - Top level config only for drawing, everything else hidden
- back to using packages or includes probably - back to using packages or includes probably
- Allow adding of font and icons easily - Allow adding of font and icons easily
- LED set per page!
## Missing config ### Missing config
- Event lambda's (see config.yaml) - Event lambda's (see config.yaml)
- Implement --no-click-confirm - Implement --no-click-confirm
- Add notification LED partition, in order to update multiple at the same time
- Fix LED effects: Blink and Breathe - Fix LED effects: Blink and Breathe
- Dismiss notification button by changing page? - And make effects optional (--no-effects)
+11 -3
View File
@@ -28,6 +28,7 @@ def compose_config(
click_types: List[ClickType], click_types: List[ClickType],
click_confirm: bool, click_confirm: bool,
services: bool, services: bool,
effects: bool
) -> Dict[str, Any]: ) -> Dict[str, Any]:
# Base config # Base config
@@ -38,7 +39,7 @@ def compose_config(
# Display, if present # Display, if present
if num_bars < 4: if num_bars < 4:
config = deep_merge( config = deep_merge(
config, config_display(version=version, num_bars=num_bars, page_names=pages) config, config_display(version=version, num_bars=num_bars, page_names=pages, add_effects=effects)
) )
# Services, only available with display # Services, only available with display
@@ -47,7 +48,7 @@ def compose_config(
# Bars # Bars
config = deep_merge( config = deep_merge(
config, config_bars(version=version, num_bars=num_bars, page_names=pages) config, config_bars(version=version, num_bars=num_bars, page_names=pages, add_effects=effects)
) )
# Events # Events
@@ -160,6 +161,12 @@ def main(argv=None) -> int:
action="store_true", action="store_true",
help="Don't add any of the toast, notification or image services.", help="Don't add any of the toast, notification or image services.",
) )
parser.add_argument(
"--no-effects",
default=False,
action="store_true",
help="Don't add any effects to the LEDs.",
)
parser.add_argument( parser.add_argument(
"--output", "-o", default="buttonplus.yaml", help="Output file path" "--output", "-o", default="buttonplus.yaml", help="Output file path"
) )
@@ -171,11 +178,12 @@ def main(argv=None) -> int:
pages: List[str] = args.pages pages: List[str] = args.pages
click_types: List[ClickType] = list_to_click_type(args.click_types) click_types: List[ClickType] = list_to_click_type(args.click_types)
click_confirm: bool = not args.no_click_confirm click_confirm: bool = not args.no_click_confirm
effects: bool = not args.no_effects
services: bool = not args.no_services services: bool = not args.no_services
output: str = args.output output: str = args.output
config = compose_config( config = compose_config(
device_name, version, num_bars, pages, click_types, click_confirm, services device_name, version, num_bars, pages, click_types, click_confirm, services, effects
) )
with open(output, "w", encoding="utf-8") as fh: with open(output, "w", encoding="utf-8") as fh:
fh.writelines( fh.writelines(
+51 -3
View File
@@ -28,7 +28,7 @@ def list_merge(a: List[Any], b: List[Any]) -> List[Any]:
Steps: Steps:
1. Check if both lists contain Dicts with an `id` key in them 1. Check if both lists contain Dicts with an `id` key in them
1a. Otherwise just return a + b (extend) 1a. Otherwise do a pre-append list merge (extend)
2. For every entry in a: 2. For every entry in a:
3. Store id in a list for later 3. Store id in a list for later
4. Find the same id in b, if not found add to result without merge 4. Find the same id in b, if not found add to result without merge
@@ -47,44 +47,92 @@ def list_merge(a: List[Any], b: List[Any]) -> List[Any]:
# If there are no id-based dicts in either list, just extend # If there are no id-based dicts in either list, just extend
if not (a_has_id and b_has_id): if not (a_has_id and b_has_id):
return list(a) + list(b) return pre_append_list_merge(a, b)
result: List[Any] = [] result: List[Any] = []
seen_ids = set() seen_ids = set()
# Merge entries from `a`, matching by `id` in `b` when present # Merge entries from `a`, matching by `id` in `b` when present
for item in a: for item in a:
# There is an `id`` field
if isinstance(item, dict) and "id" in item: if isinstance(item, dict) and "id" in item:
id_val = item.get("id") id_val = item.get("id")
# find matching entry in b # find matching entry in b
match = None match = None
for bi in b: for bi in b:
# check if b also had `id` field with the same value
if isinstance(bi, dict) and bi.get("id") == id_val: if isinstance(bi, dict) and bi.get("id") == id_val:
match = bi match = bi
break break
# Both a and b have a matching `id` entry, so deep merge the dict
if match is not None: if match is not None:
# deep_merge returns a new dict # deep_merge returns a new dict
merged = deep_merge(item, match) merged = deep_merge(item, match)
result.append(merged) result.append(merged)
# No match, just copy entry from `a`
else: else:
result.append(item) result.append(item)
# Store all the id's that we have seen
if id_val is not None: if id_val is not None:
seen_ids.add(id_val) seen_ids.add(id_val)
# No `id` field, just add to the list
else: else:
# Not an id'd dict, keep as-is
result.append(item) result.append(item)
# Add remaining entries from b that were not merged or that don't have ids # Add remaining entries from b that were not merged or that don't have ids
for item in b: for item in b:
# There is an `id`` field (we only copy missing id's)
if isinstance(item, dict) and "id" in item: if isinstance(item, dict) and "id" in item:
id_val = item.get("id") id_val = item.get("id")
# Empty id, just add it
if id_val is None: if id_val is None:
result.append(item) result.append(item)
# Add the entry if is an unknown id (not already merged with an `a` entry)
elif id_val not in seen_ids: elif id_val not in seen_ids:
result.append(item) result.append(item)
# No id field, just add it
else: else:
result.append(item) result.append(item)
# Return the merged list
return result return result
def pre_append_list_merge(a: List[Any], b: List[Any]) -> List[Any]:
# Detect whether both lists contain dicts with an 'prepend' key
a_has_prepend = any(isinstance(x, dict) and "prepend" in x for x in a)
b_has_prepend = any(isinstance(x, dict) and "prepend" in x for x in b)
# If there are no `prepend` keys in either list, just extend
if not a_has_prepend and not b_has_prepend:
return list(a) + list(b)
prepend_list = []
append_list = []
def do_pre_or_append(entry: Dict[str, Any]) -> None:
# There is a `prepend` field
if isinstance(entry, dict) and "prepend" in entry:
prepend = entry.pop("prepend")
if(prepend):
# Append, so that the prepend order stays the same as how they are inserted
prepend_list.append(entry)
else:
append_list.append(entry)
else:
append_list.append(entry)
for item in a:
do_pre_or_append(item)
for item in b:
do_pre_or_append(item)
return list(prepend_list) + list(append_list)
+17 -13
View File
@@ -4,14 +4,14 @@ from buttonplus_generator.merger import deep_merge
from buttonplus_generator.utils import get_light_effects from buttonplus_generator.utils import get_light_effects
def config_bars(version: int, num_bars: int, page_names: List[str]) -> Dict[str, Any]: def config_bars(version: int, num_bars: int, page_names: List[str], add_effects: bool) -> Dict[str, Any]:
config = bars_backlight() config = bars_backlight()
config = deep_merge(config, bars_setup(num_bars=num_bars)) config = deep_merge(config, bars_setup(num_bars=num_bars))
config = deep_merge(config, bars_buttons(num_bars=num_bars)) config = deep_merge(config, bars_buttons(num_bars=num_bars))
config = deep_merge( config = deep_merge(
config, config,
bars_lights( bars_lights(
num_bars=num_bars, display_has_leds=(version == 2 and num_bars < 4) num_bars=num_bars, display_has_leds=(version == 2 and num_bars < 4), add_effects=add_effects
), ),
) )
config = deep_merge(config, bars_display_output(num_bars=num_bars)) config = deep_merge(config, bars_display_output(num_bars=num_bars))
@@ -171,7 +171,7 @@ def _bar_button_entry(bar_nr: int, base_id: str) -> List[Dict[str, Any]]:
] ]
def bars_lights(num_bars: int, display_has_leds: bool = True) -> Dict[str, Any]: def bars_lights(num_bars: int, display_has_leds: bool, add_effects: bool) -> Dict[str, Any]:
bar_dict = {"light": []} bar_dict = {"light": []}
if num_bars < 1: if num_bars < 1:
@@ -180,49 +180,52 @@ def bars_lights(num_bars: int, display_has_leds: bool = True) -> Dict[str, Any]:
display_offset = 4 if display_has_leds else 0 display_offset = 4 if display_has_leds else 0
if num_bars > 0: if num_bars > 0:
bar_dict["light"].extend(_bar_lights_entry(1, 0 + display_offset)) bar_dict["light"].extend(_bar_lights_entry(1, 0 + display_offset, add_effects))
if num_bars > 1: if num_bars > 1:
bar_dict["light"].extend(_bar_lights_entry(2, 4 + display_offset)) bar_dict["light"].extend(_bar_lights_entry(2, 4 + display_offset, add_effects))
if num_bars > 2: if num_bars > 2:
bar_dict["light"].extend(_bar_lights_entry(3, 8 + display_offset)) bar_dict["light"].extend(_bar_lights_entry(3, 8 + display_offset, add_effects))
if num_bars > 3: if num_bars > 3:
bar_dict["light"].extend(_bar_lights_entry(4, 12)) # no display bar_dict["light"].extend(_bar_lights_entry(4, 12, add_effects)) # no display
return bar_dict return bar_dict
def _bar_lights_entry(bar_nr: int, offset: int) -> List[Dict[str, Any]]: def _bar_lights_entry(bar_nr: int, offset: int, add_effects: bool) -> List[Dict[str, Any]]:
light_effects = get_light_effects(add_effects)
return [ return [
{ {
"platform": "partition", "platform": "partition",
"name": f"BAR {bar_nr} Left RGB Front", "name": f"BAR {bar_nr} Left RGB Front",
"id": f"bar{bar_nr}_left_rgb_front", "id": f"bar{bar_nr}_left_rgb_front",
"segments": [{"id": "neopixels", "from": offset + 0, "to": offset + 0}], "segments": [{"id": "neopixels", "from": offset + 0, "to": offset + 0}],
"effects": get_light_effects() "effects": light_effects
}, },
{ {
"platform": "partition", "platform": "partition",
"name": f"BAR {bar_nr} Left RGB Back", "name": f"BAR {bar_nr} Left RGB Back",
"id": f"bar{bar_nr}_left_rgb_back", "id": f"bar{bar_nr}_left_rgb_back",
"segments": [{"id": "neopixels", "from": offset + 1, "to": offset + 1}], "segments": [{"id": "neopixels", "from": offset + 1, "to": offset + 1}],
"effects": get_light_effects() "effects": light_effects
}, },
{ {
"platform": "partition", "platform": "partition",
"name": f"BAR {bar_nr} Right RGB Front", "name": f"BAR {bar_nr} Right RGB Front",
"id": f"bar{bar_nr}_right_rgb_front", "id": f"bar{bar_nr}_right_rgb_front",
"segments": [{"id": "neopixels", "from": offset + 2, "to": offset + 2}], "segments": [{"id": "neopixels", "from": offset + 2, "to": offset + 2}],
"effects": get_light_effects() "effects": light_effects
}, },
{ {
"platform": "partition", "platform": "partition",
"name": f"BAR {bar_nr} Right RGB Back", "name": f"BAR {bar_nr} Right RGB Back",
"id": f"bar{bar_nr}_right_rgb_back", "id": f"bar{bar_nr}_right_rgb_back",
"segments": [{"id": "neopixels", "from": offset + 3, "to": offset + 3}], "segments": [{"id": "neopixels", "from": offset + 3, "to": offset + 3}],
"effects": get_light_effects() "effects": light_effects
}, },
] ]
@@ -359,6 +362,7 @@ def _bars_script_update_entry(
generated_cpp = "" generated_cpp = ""
# Loop through both lists at the same time
for bar, state in zip(bars_ids, bars_state): for bar, state in zip(bars_ids, bars_state):
generated_cpp += f"id({bar}).turn_{state}();\n" generated_cpp += f"id({bar}).turn_{state}();\n"
@@ -437,7 +441,7 @@ def _bars_script_draw_entry(
f" // Page {page_num} - {page_names[page_num]}\n" f" // Page {page_num} - {page_names[page_num]}\n"
f" case {page_num}:\n" f" case {page_num}:\n"
f' id(bar_display).printf(80, 0, id(font_arial20), id(accent), TextAlign::TOP_CENTER , "Bar {bar_nr} {bar_side}");\n' f' id(bar_display).printf(80, 0, id(font_arial20), id(accent), TextAlign::TOP_CENTER , "Bar {bar_nr} {bar_side}");\n'
f' id(bar_display).printf(80, 80, id(font_arial20), id(white), TextAlign::BOTTOM_CENTER , "Page {page_num}");\n' f' id(bar_display).printf(80, 80, id(font_arial20), id(white), TextAlign::BOTTOM_CENTER , "{page_names[page_num]}");\n'
f" break;\n" f" break;\n"
) )
+90 -22
View File
@@ -29,7 +29,7 @@ def config_common(
if num_bars > 2: if num_bars > 2:
num_leds = 13 + display_add num_leds = 13 + display_add
if num_bars > 3: if num_bars > 3:
num_leds = 17 #no display num_leds = 17 # no display
config = deep_merge(config, neopixels(num_leds=num_leds)) config = deep_merge(config, neopixels(num_leds=num_leds))
config = deep_merge(config, update_scripts(num_bars=num_bars)) config = deep_merge(config, update_scripts(num_bars=num_bars))
@@ -44,7 +44,10 @@ def base_config(device_name: str) -> Dict[str, Any]:
"friendly_name": device_name, "friendly_name": device_name,
"on_boot": { "on_boot": {
"priority": -100, "priority": -100,
"then": [{"script.execute": "setup_bar_displays"}], "then": [
{"script.execute": "setup_bar_displays"},
{"script.execute": "test_all_leds"},
],
}, },
}, },
"esp32": { "esp32": {
@@ -145,22 +148,32 @@ def pages(nr_of_pages: int, page_names: List[str]) -> Dict[str, Any]:
} }
] ]
next_page_cpp = (
"id(active_page_nr) = id(active_page_nr) + 1;\n"
"if(id(active_page_nr) >= id(nr_of_pages)) {\n"
" id(active_page_nr) = 0;\n"
"}"
)
prev_page_cpp = (
"id(active_page_nr) = id(active_page_nr) - 1;\n"
"if(id(active_page_nr) < 0) {\n"
" id(active_page_nr) = id(nr_of_pages) - 1;\n"
"}"
)
page_dict["script"] = [ page_dict["script"] = [
{ {
"id": "next_page", "id": "next_page",
"then": [ "then": [
{ {"lambda": next_page_cpp},
"lambda": "id(active_page_nr) = id(active_page_nr) + 1;\nif(id(active_page_nr) >= id(nr_of_pages)) {\n id(active_page_nr) = 0;\n}"
},
{"script.execute": "update_page"}, {"script.execute": "update_page"},
], ],
}, },
{ {
"id": "prev_page", "id": "prev_page",
"then": [ "then": [
{ {"lambda": prev_page_cpp},
"lambda": "id(active_page_nr) = id(active_page_nr) - 1;\nif(id(active_page_nr) < 0) {\n id(active_page_nr) = id(nr_of_pages) - 1;\n}"
},
{"script.execute": "update_page"}, {"script.execute": "update_page"},
], ],
}, },
@@ -248,6 +261,43 @@ def spi() -> Dict[str, Any]:
def neopixels(num_leds: int) -> Dict[str, Any]: def neopixels(num_leds: int) -> Dict[str, Any]:
return { return {
"script": [
{
"id": "test_all_leds",
"then": [
{"delay": "1000ms"},
{
"light.turn_on": {
"id": "internal_all_rgb",
"red": "100%",
"green": "0%",
"blue": "0%",
"flash_length": "100ms",
}
},
{"delay": "200ms"},
{
"light.turn_on": {
"id": "internal_all_rgb",
"red": "0%",
"green": "100%",
"blue": "0%",
"flash_length": "100ms",
}
},
{"delay": "200ms"},
{
"light.turn_on": {
"id": "internal_all_rgb",
"red": "0%",
"green": "0%",
"blue": "100%",
"flash_length": "100ms",
}
},
],
}
],
"light": [ "light": [
{ {
"platform": "esp32_rmt_led_strip", "platform": "esp32_rmt_led_strip",
@@ -256,8 +306,21 @@ def neopixels(num_leds: int) -> Dict[str, Any]:
"chipset": "ws2812", "chipset": "ws2812",
"pin": GPIO_NEOPIXELS, "pin": GPIO_NEOPIXELS,
"num_leds": num_leds, "num_leds": num_leds,
"internal": True,
}, },
] {
"platform": "partition",
"id": "internal_all_rgb",
"internal": True,
"segments": [
{
"id": "neopixels",
"from": 0,
"to": num_leds - 1,
},
],
},
],
} }
@@ -328,24 +391,29 @@ def sensors(ambient_gain: str, extra_ambient_sensors: bool) -> Dict[str, Any]:
return sensors_dict return sensors_dict
def update_scripts(num_bars: int) -> Dict[str, Any]: def update_scripts(num_bars: int) -> Dict[str, Any]:
common_dict = {"script": []} common_dict = {"script": []}
update_list = [ update_list = [
{ {
"script.execute": "update_bars", "script.execute": "update_bars",
}, },
{"delay": "0.1s"}, {"delay": "0.1s"},
] ]
if num_bars < 4: if num_bars < 4:
update_list.extend([ update_list.extend(
{ [
"script.execute": "update_main", {
}, "script.execute": "update_main",
{"delay": "0.1s"}, },
]) {"delay": "0.1s"},
]
common_dict["script"].append({"id": "update_all", "then": update_list[:-1]}) )
return common_dict common_dict["script"].append(
{"id": "update_all", "mode": "restart", "then": update_list[:-1]}
)
return common_dict
+9 -7
View File
@@ -4,12 +4,12 @@ from buttonplus_generator.utils import get_display_id, get_display_address, get_
from buttonplus_generator.merger import deep_merge from buttonplus_generator.merger import deep_merge
def config_display(version: int, num_bars: int, page_names: List[str]) -> Dict[str, Any]: def config_display(version: int, num_bars: int, page_names: List[str], add_effects: bool) -> Dict[str, Any]:
config = display_backlight(num_bars=num_bars) config = display_backlight(num_bars=num_bars)
config = deep_merge(config, display_setup(num_bars=num_bars)) config = deep_merge(config, display_setup(num_bars=num_bars))
config = deep_merge(config, display_buttons(num_bars=num_bars)) config = deep_merge(config, display_buttons(num_bars=num_bars))
config = deep_merge( config = deep_merge(
config, display_lights(num_bars=num_bars, display_has_leds=(version == 2)) config, display_lights(num_bars=num_bars, display_has_leds=(version == 2), add_effects=add_effects)
) )
config = deep_merge(config, update_scripts(page_names=page_names)) config = deep_merge(config, update_scripts(page_names=page_names))
@@ -112,13 +112,15 @@ def display_buttons(num_bars: int) -> Dict[str, Any]:
} }
def display_lights(num_bars: int, display_has_leds: bool = True) -> Dict[str, Any]: def display_lights(num_bars: int, display_has_leds: bool, add_effects: bool) -> Dict[str, Any]:
if not display_has_leds: if not display_has_leds:
return {} return {}
if num_bars > 3: if num_bars > 3:
raise ValueError("At most three bars must be connected to use a display!") raise ValueError("At most three bars must be connected to use a display!")
light_effects = get_light_effects(add_effects)
return { return {
"light": [ "light": [
{ {
@@ -126,28 +128,28 @@ def display_lights(num_bars: int, display_has_leds: bool = True) -> Dict[str, An
"name": "Main Display Left RGB Front", "name": "Main Display Left RGB Front",
"id": "main_display_left_rgb_front", "id": "main_display_left_rgb_front",
"segments": [{"id": "neopixels", "from": 0, "to": 0}], "segments": [{"id": "neopixels", "from": 0, "to": 0}],
"effects": get_light_effects() "effects": light_effects
}, },
{ {
"platform": "partition", "platform": "partition",
"name": "Main Display Left RGB Back", "name": "Main Display Left RGB Back",
"id": "main_display_left_rgb_back", "id": "main_display_left_rgb_back",
"segments": [{"id": "neopixels", "from": 1, "to": 1}], "segments": [{"id": "neopixels", "from": 1, "to": 1}],
"effects": get_light_effects() "effects": light_effects
}, },
{ {
"platform": "partition", "platform": "partition",
"name": "Main Display Right RGB Front", "name": "Main Display Right RGB Front",
"id": "main_display_right_rgb_front", "id": "main_display_right_rgb_front",
"segments": [{"id": "neopixels", "from": 2, "to": 2}], "segments": [{"id": "neopixels", "from": 2, "to": 2}],
"effects": get_light_effects() "effects": light_effects
}, },
{ {
"platform": "partition", "platform": "partition",
"name": "Main Display Right RGB Back", "name": "Main Display Right RGB Back",
"id": "main_display_right_rgb_back", "id": "main_display_right_rgb_back",
"segments": [{"id": "neopixels", "from": 3, "to": 3}], "segments": [{"id": "neopixels", "from": 3, "to": 3}],
"effects": get_light_effects() "effects": light_effects
}, },
] ]
} }
+3 -3
View File
@@ -88,8 +88,8 @@ def _generate_event_names(
page_names: List[str], click_types: List[ClickType] page_names: List[str], click_types: List[ClickType]
) -> List[str]: ) -> List[str]:
return [ return [
f"{str(ct)} page {page_num}" f"{str(ct)} {page}"
for page_num in range(0, len(page_names)) for page in page_names
for ct in click_types for ct in click_types
] ]
@@ -267,7 +267,7 @@ def _timing_entry(
"event_msg": YAMLLambda( "event_msg": YAMLLambda(
'return "' 'return "'
+ event_type + event_type
+ ' page " + std::to_string(id(active_page_nr));' + ' " + id(active_page_name);'
), ),
} }
} }
+67 -17
View File
@@ -66,15 +66,19 @@ def notification_service(version: int) -> Dict[str, Any]:
"}\n" "}\n"
) )
led_left = "main_display_left_rgb_front" notification_led_partition_left = 0
led_right = "main_display_right_rgb_front" notification_led_partition_right = 2
notification_led_left = "main_display_left_rgb_front"
notification_led_right = "main_display_right_rgb_front"
# No display leds, switch to BAR 1 wall leds # No display leds, switch to BAR 1 wall leds
if version == 1: if version == 1:
led_left = "bar1_left_rgb_back" notification_led_partition_left = 1
led_right = "bar1_right_rgb_back" notification_led_partition_right = 3
notification_led_left = "bar1_left_rgb_back"
notification_led_right = "bar1_right_rgb_back"
return { service_dict = {
"globals": [ "globals": [
{"id": "internal_notify", "type": "std::string", "initial_value": ""} {"id": "internal_notify", "type": "std::string", "initial_value": ""}
], ],
@@ -100,17 +104,7 @@ def notification_service(version: int) -> Dict[str, Any]:
"then": [ "then": [
{ {
"light.turn_on": { "light.turn_on": {
"id": led_left, "id": "internal_notification_rgb",
"brightness": "100%",
"red": "100%",
"green": "100%",
"blue": "100%",
"flash_length": "1s",
}
},
{
"light.turn_on": {
"id": led_right,
"brightness": "100%", "brightness": "100%",
"red": "100%", "red": "100%",
"green": "100%", "green": "100%",
@@ -122,6 +116,15 @@ def notification_service(version: int) -> Dict[str, Any]:
], ],
} }
}, },
{
"wait_until": {
"condition": {
"lambda": "return id(internal_notify).size() == 0;"
}
}
},
{"light.control": {"id": notification_led_left}},
{"light.control": {"id": notification_led_right}},
], ],
}, },
{ {
@@ -138,4 +141,51 @@ def notification_service(version: int) -> Dict[str, Any]:
}, },
] ]
}, },
"light": [
{
"platform": "partition",
"id": "internal_notification_rgb",
"internal": True,
"segments": [
{
"id": "neopixels",
"from": notification_led_partition_left,
"to": notification_led_partition_left,
},
{
"id": "neopixels",
"from": notification_led_partition_right,
"to": notification_led_partition_right,
},
],
}
],
} }
dismiss_notification = {
"prepend": True,
"if": {
"condition": [
{"lambda": YAMLLambda("return id(internal_notify).size() > 0;")}
],
"then": [
{
"globals.set": {
"id": "internal_notify",
"value": YAMLLambda("return std::string();"),
},
},
{"script.execute": "update_main"},
{"script.stop": "next_page"},
],
},
}
service_dict["script"].extend(
[
{"id": "next_page", "then": [dismiss_notification]},
{"id": "prev_page", "then": [dismiss_notification]},
]
)
return service_dict
+10 -8
View File
@@ -32,23 +32,25 @@ def get_display_address(num_bars : int) -> str:
return 0x23 - num_bars return 0x23 - num_bars
def get_light_effects() -> List[Dict[str, Any]]: def get_light_effects(enabled: bool) -> List[Dict[str, Any]]:
if not enabled:
return []
return [ return [
{ {
"pulse": { "pulse": {
"name": "Breathe", "name": "Breathe",
"transition_length": "500ms", "transition_length": "1s",
"update_interval": "2s" "update_interval": "1.5s",
"min_brightness": "20%",
"max_brightness": "100%"
} }
}, },
{ {
"pulse": { "pulse": {
"name": "Blink", "name": "Blink",
"transition_length": { "transition_length": "50ms",
"on_length": "500ms", "update_interval": "500ms"
"off_length": "1s"
},
"update_interval": "100ms"
} }
} }
] ]