Enhance configuration generation and merging
This commit is contained in:
@@ -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)
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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,6 +391,7 @@ 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": []}
|
||||||
|
|
||||||
@@ -339,13 +403,17 @@ def update_scripts(num_bars: int) -> Dict[str, Any]:
|
|||||||
]
|
]
|
||||||
|
|
||||||
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]})
|
common_dict["script"].append(
|
||||||
|
{"id": "update_all", "mode": "restart", "then": update_list[:-1]}
|
||||||
|
)
|
||||||
|
|
||||||
return common_dict
|
return common_dict
|
||||||
@@ -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
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);'
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user