Implement toast and notification service
This commit is contained in:
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Python Debugger: Main",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "src/main.py",
|
||||
"args": ["-v", "1", "-b", "3", "-p", "Start", "Music", "Lights", "Suus", "-c", "single", "double", "hold"],
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,6 +14,8 @@ The configuration (handmade) on which this generator is based, can be found in t
|
||||
|
||||
## Ideas
|
||||
- Notification service: until dismissed, or with timeout
|
||||
- Toast service, see above
|
||||
- SHow image service
|
||||
- LED effects: flash, breathe
|
||||
- Top level config only for drawing, everything else hidden
|
||||
- back to using packages or includes probably
|
||||
@@ -21,4 +23,7 @@ The configuration (handmade) on which this generator is based, can be found in t
|
||||
|
||||
## Missing config
|
||||
- Event lambda's (see config.yaml)
|
||||
- Allow turn off 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
|
||||
- Dismiss notification button by changing page?
|
||||
@@ -15,21 +15,56 @@ from buttonplus_generator.snippets.common import config_common
|
||||
from buttonplus_generator.snippets.bars import config_bars
|
||||
from buttonplus_generator.snippets.display import config_display
|
||||
from buttonplus_generator.snippets.events import config_events
|
||||
from buttonplus_generator.snippets.services import config_services
|
||||
from buttonplus_generator.merger import deep_merge
|
||||
from buttonplus_generator.utils import list_to_click_type
|
||||
|
||||
|
||||
def compose_config(device_name: str, version: int, num_bars: int, pages: List[str], click_types: List[ClickType], click_confirm: bool) -> Dict[str, Any]:
|
||||
config = config_common(device_name=device_name, version=version, page_names=pages, num_bars=num_bars)
|
||||
def compose_config(
|
||||
device_name: str,
|
||||
version: int,
|
||||
num_bars: int,
|
||||
pages: List[str],
|
||||
click_types: List[ClickType],
|
||||
click_confirm: bool,
|
||||
services: bool,
|
||||
) -> Dict[str, Any]:
|
||||
|
||||
if(num_bars < 4): # Always assume display if less than 4 bars
|
||||
config = deep_merge(config, config_display(version=version, num_bars=num_bars, page_names=pages))
|
||||
# Base config
|
||||
config = config_common(
|
||||
device_name=device_name, version=version, page_names=pages, num_bars=num_bars
|
||||
)
|
||||
|
||||
config = deep_merge(config, config_bars(version=version, num_bars=num_bars, page_names=pages))
|
||||
config = deep_merge(config, config_events(version=version, num_bars=num_bars, page_names=pages, click_types=click_types, click_confirm=click_confirm))
|
||||
# Display, if present
|
||||
if num_bars < 4:
|
||||
config = deep_merge(
|
||||
config, config_display(version=version, num_bars=num_bars, page_names=pages)
|
||||
)
|
||||
|
||||
# Services, only available with display
|
||||
if services:
|
||||
config = deep_merge(config, config_services(version=version))
|
||||
|
||||
# Bars
|
||||
config = deep_merge(
|
||||
config, config_bars(version=version, num_bars=num_bars, page_names=pages)
|
||||
)
|
||||
|
||||
# Events
|
||||
config = deep_merge(
|
||||
config,
|
||||
config_events(
|
||||
version=version,
|
||||
num_bars=num_bars,
|
||||
page_names=pages,
|
||||
click_types=click_types,
|
||||
click_confirm=click_confirm,
|
||||
),
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def yaml_dumper(config: Dict[str, Any], fh) -> str:
|
||||
|
||||
## Handle multiline strings
|
||||
@@ -38,17 +73,17 @@ def yaml_dumper(config: Dict[str, Any], fh) -> str:
|
||||
Ref: https://stackoverflow.com/questions/8640959/how-can-i-control-what-scalar-form-pyyaml-uses-for-my-data
|
||||
"""
|
||||
if len(data.splitlines()) > 1: # check for multiline string
|
||||
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
|
||||
return dumper.represent_scalar('tag:yaml.org,2002:str', data)
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||
|
||||
def represent_extend(dumper, data):
|
||||
return dumper.represent_scalar('!extend', data.value)
|
||||
return dumper.represent_scalar("!extend", data.value)
|
||||
|
||||
def represent_lambda(dumper, data):
|
||||
return dumper.represent_scalar('!lambda', data.value, style='"')
|
||||
return dumper.represent_scalar("!lambda", data.value, style='"')
|
||||
|
||||
def represent_secret(dumper, data):
|
||||
return dumper.represent_scalar('!secret', data.value, style='')
|
||||
return dumper.represent_scalar("!secret", data.value, style="")
|
||||
|
||||
yaml.add_representer(str, represent_multistr)
|
||||
yaml.add_representer(YAMLExtend, represent_extend)
|
||||
@@ -59,12 +94,20 @@ def yaml_dumper(config: Dict[str, Any], fh) -> str:
|
||||
def ignore_aliases(self, data):
|
||||
return True
|
||||
|
||||
yaml.dump(config, fh, sort_keys=False, encoding="utf-8", default_flow_style=False, Dumper=NoAliasDumper)
|
||||
yaml.dump(
|
||||
config,
|
||||
fh,
|
||||
sort_keys=False,
|
||||
encoding="utf-8",
|
||||
default_flow_style=False,
|
||||
Dumper=NoAliasDumper,
|
||||
)
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate ESPHome YAML for Button+ V2",
|
||||
epilog="The version of the display and bars matters because the V1 Display does not have LEDs.\n\nMade by Kenneth van Ewijk (kennyboy55)",
|
||||
epilog="The version of the display and bars matters because the V1 Display does not have LEDs.\nMade by Kenneth van Ewijk (kennyboy55)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--device-name",
|
||||
@@ -76,10 +119,10 @@ def main(argv=None) -> int:
|
||||
"--version",
|
||||
"-v",
|
||||
type=int,
|
||||
choices=[1,2],
|
||||
choices=[1, 2],
|
||||
required=True,
|
||||
help="The version of the display and bars. Only V2 PCB is supported.",
|
||||
metavar="[1|2]"
|
||||
metavar="[1|2]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--bars",
|
||||
@@ -87,7 +130,7 @@ def main(argv=None) -> int:
|
||||
type=int,
|
||||
required=True,
|
||||
help="Number of bars attached, at least 1, at most 4. A display is assumed for up to three bars attached.",
|
||||
metavar="NUM"
|
||||
metavar="NUM",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pages",
|
||||
@@ -95,7 +138,7 @@ def main(argv=None) -> int:
|
||||
nargs="+",
|
||||
required=True,
|
||||
help="List of page names",
|
||||
metavar="NAME"
|
||||
metavar="NAME",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--click-types",
|
||||
@@ -103,13 +146,19 @@ def main(argv=None) -> int:
|
||||
nargs="+",
|
||||
default=["single", "double", "triple", "hold"],
|
||||
help="List click types buttons, choices are [single|double|triple|hold].",
|
||||
metavar="TYPE"
|
||||
metavar="TYPE",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-click-confirm",
|
||||
default=True,
|
||||
action="store_false",
|
||||
help="Dont' flash the LED as a confirmation of the click event."
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Don't flash the LED as a confirmation of the click event.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-services",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Don't add any of the toast, notification or image services.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output", "-o", default="buttonplus.yaml", help="Output file path"
|
||||
@@ -121,10 +170,13 @@ def main(argv=None) -> int:
|
||||
num_bars: int = args.bars
|
||||
pages: List[str] = args.pages
|
||||
click_types: List[ClickType] = list_to_click_type(args.click_types)
|
||||
click_confirm: bool = args.click_confirm
|
||||
click_confirm: bool = not args.no_click_confirm
|
||||
services: bool = not args.no_services
|
||||
output: str = args.output
|
||||
|
||||
config = compose_config(device_name, version, num_bars, pages, click_types, click_confirm)
|
||||
config = compose_config(
|
||||
device_name, version, num_bars, pages, click_types, click_confirm, services
|
||||
)
|
||||
with open(output, "w", encoding="utf-8") as fh:
|
||||
fh.writelines(
|
||||
[
|
||||
|
||||
@@ -88,4 +88,3 @@ def list_merge(a: List[Any], b: List[Any]) -> List[Any]:
|
||||
result.append(item)
|
||||
|
||||
return result
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Dict, Any, List
|
||||
from buttonplus_generator.constants import *
|
||||
from buttonplus_generator.merger import deep_merge
|
||||
from buttonplus_generator.utils import get_light_effects
|
||||
|
||||
|
||||
def config_bars(version: int, num_bars: int, page_names: List[str]) -> Dict[str, Any]:
|
||||
@@ -200,24 +201,28 @@ def _bar_lights_entry(bar_nr: int, offset: int) -> List[Dict[str, Any]]:
|
||||
"name": f"BAR {bar_nr} Left RGB Front",
|
||||
"id": f"bar{bar_nr}_left_rgb_front",
|
||||
"segments": [{"id": "neopixels", "from": offset + 0, "to": offset + 0}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
{
|
||||
"platform": "partition",
|
||||
"name": f"BAR {bar_nr} Left RGB Back",
|
||||
"id": f"bar{bar_nr}_left_rgb_back",
|
||||
"segments": [{"id": "neopixels", "from": offset + 1, "to": offset + 1}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
{
|
||||
"platform": "partition",
|
||||
"name": f"BAR {bar_nr} Right RGB Front",
|
||||
"id": f"bar{bar_nr}_right_rgb_front",
|
||||
"segments": [{"id": "neopixels", "from": offset + 2, "to": offset + 2}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
{
|
||||
"platform": "partition",
|
||||
"name": f"BAR {bar_nr} Right RGB Back",
|
||||
"id": f"bar{bar_nr}_right_rgb_back",
|
||||
"segments": [{"id": "neopixels", "from": offset + 3, "to": offset + 3}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ def base_config(device_name: str) -> Dict[str, Any]:
|
||||
"password": YAMLSecret("wifi_password"),
|
||||
},
|
||||
"logger": {"level": "DEBUG"},
|
||||
"api": {"encryption": {"key": YAMLSecret("encryption_key")}},
|
||||
"ota": [{"platform": "esphome", "password": YAMLSecret("ota_password")}],
|
||||
"api": {"encryption": {"key": YAMLSecret("encryption_key")}},
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ def colors(accent_color: str) -> Dict[str, Any]:
|
||||
"color": [
|
||||
{"id": "accent", "hex": accent_color},
|
||||
{"id": "white", "hex": "FFFFFF"},
|
||||
{"id": "black", "hex": "000000"},
|
||||
{"id": "grey", "hex": "A6A6A6"},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Dict, Any, List
|
||||
from buttonplus_generator.constants import *
|
||||
from buttonplus_generator.utils import get_display_id, get_display_address
|
||||
from buttonplus_generator.utils import get_display_id, get_display_address, get_light_effects
|
||||
from buttonplus_generator.merger import deep_merge
|
||||
|
||||
|
||||
@@ -126,29 +126,32 @@ def display_lights(num_bars: int, display_has_leds: bool = True) -> Dict[str, An
|
||||
"name": "Main Display Left RGB Front",
|
||||
"id": "main_display_left_rgb_front",
|
||||
"segments": [{"id": "neopixels", "from": 0, "to": 0}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
{
|
||||
"platform": "partition",
|
||||
"name": "Main Display Left RGB Back",
|
||||
"id": "main_display_left_rgb_back",
|
||||
"segments": [{"id": "neopixels", "from": 1, "to": 1}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
{
|
||||
"platform": "partition",
|
||||
"name": "Main Display Right RGB Front",
|
||||
"id": "main_display_right_rgb_front",
|
||||
"segments": [{"id": "neopixels", "from": 2, "to": 2}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
{
|
||||
"platform": "partition",
|
||||
"name": "Main Display Right RGB Back",
|
||||
"id": "main_display_right_rgb_back",
|
||||
"segments": [{"id": "neopixels", "from": 3, "to": 3}],
|
||||
"effects": get_light_effects()
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def update_scripts(page_names: List[str]) -> Dict[str, Any]:
|
||||
display_dict = {"script": []}
|
||||
|
||||
@@ -167,6 +170,7 @@ def update_scripts(page_names: List[str]) -> Dict[str, Any]:
|
||||
"// All pages\n"
|
||||
"id(main_display).image(0, 235, id(icon_prev_page), ImageAlign::BOTTOM_LEFT, id(grey));\n"
|
||||
"id(main_display).image(320, 235, id(icon_next_page), ImageAlign::BOTTOM_RIGHT, id(grey));\n"
|
||||
"id(main_display).print(160, 235, id(font_arial20), id(white), TextAlign::BOTTOM_CENTER, id(active_page_name).c_str());\n"
|
||||
"\n"
|
||||
"id(main_display).strftime(160, 5, id(font_arial20), TextAlign::TOP_CENTER, \"%H:%M\", id(datetime).now());\n"
|
||||
"id(main_display).strftime(160, 80, id(font_arial20), TextAlign::TOP_CENTER, \"%d-%m-%Y\", id(datetime).now());\n"
|
||||
@@ -178,7 +182,6 @@ def update_scripts(page_names: List[str]) -> Dict[str, Any]:
|
||||
generated_cpp += (
|
||||
f" // Page {page_num} - {page_names[page_num]}\n"
|
||||
f" case {page_num}:\n"
|
||||
f" id(main_display).print(160, 235, id(font_arial20), id(white), TextAlign::BOTTOM_CENTER, id(active_page_name).c_str());\n"
|
||||
f" break;\n"
|
||||
)
|
||||
|
||||
|
||||
@@ -274,125 +274,3 @@ def _timing_entry(
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def old():
|
||||
# 1. Define event lists for substitutions
|
||||
base_types = ["click", "double_click", "triple_click", "hold"]
|
||||
display_types = [] + (base_types[1:] if INCLUDE_DISPLAY_EXTRA else [])
|
||||
|
||||
display_events = []
|
||||
button_events = []
|
||||
for p in range(NR_OF_PAGES):
|
||||
display_events.extend([f"{t} page {p}" for t in display_types])
|
||||
button_events.extend([f"{t} page {p}" for t in base_types])
|
||||
|
||||
# 2. Event Entities
|
||||
buttons = [
|
||||
("DISPLAY Button Left", "display_btn_left_event", "${display_events}"),
|
||||
("DISPLAY Button Right", "display_btn_right_event", "${display_events}"),
|
||||
("BAR1 Button Left", "bar1_btn_left_event", "${button_events}"),
|
||||
("BAR1 Button Right", "bar1_btn_right_event", "${button_events}"),
|
||||
("BAR2 Button Left", "bar2_btn_left_event", "${button_events}"),
|
||||
("BAR2 Button Right", "bar2_btn_right_event", "${button_events}"),
|
||||
("BAR3 Button Left", "bar3_btn_left_event", "${button_events}"),
|
||||
("BAR3 Button Right", "bar3_btn_right_event", "${button_events}"),
|
||||
]
|
||||
for name, eid, types in buttons:
|
||||
data["event"].append(
|
||||
{
|
||||
"platform": "template",
|
||||
"name": name,
|
||||
"id": eid,
|
||||
"device_class": "button",
|
||||
"event_types": types,
|
||||
}
|
||||
)
|
||||
|
||||
# 3. Binary Sensor Extensions
|
||||
data["binary_sensor"] = []
|
||||
triggers = {
|
||||
"click": (0.0, 1.0, 0.0), # Green
|
||||
"double_click": (0.0, 0.0, 1.0), # Blue
|
||||
"triple_click": (1.0, 0.0, 1.0), # Purple
|
||||
"hold": (1.0, 0.5, 0.0), # Orange
|
||||
}
|
||||
|
||||
btn_configs = [
|
||||
("main_display_btn_left", "display_btn_left_event", None, "prev_page"),
|
||||
("main_display_btn_right", "display_btn_right_event", None, "next_page"),
|
||||
("bar1_btn_left", "bar1_btn_left_event", "bar1_left_rgb_front", None),
|
||||
("bar1_btn_right", "bar1_btn_right_event", "bar1_right_rgb_front", None),
|
||||
("bar2_btn_left", "bar2_btn_left_event", "bar2_left_rgb_front", None),
|
||||
("bar2_btn_right", "bar2_btn_right_event", "bar2_right_rgb_front", None),
|
||||
("bar3_btn_left", "bar3_btn_left_event", "bar3_left_rgb_front", None),
|
||||
("bar3_btn_right", "bar3_btn_right_event", "bar3_right_rgb_front", None),
|
||||
]
|
||||
|
||||
for btn_id, event_id, led_id, page_script in btn_configs:
|
||||
sensor = {
|
||||
"id": ESPHomeExtend(btn_id),
|
||||
"filters": [{"delayed_off": "10ms"}],
|
||||
"internal": True,
|
||||
"on_multi_click": [],
|
||||
}
|
||||
|
||||
is_display = "main_display" in btn_id
|
||||
for t_name, color in triggers.items():
|
||||
if is_display and t_name != "click" and not INCLUDE_DISPLAY_EXTRA:
|
||||
continue
|
||||
|
||||
multi_click = {"timing": []}
|
||||
if t_name == "click":
|
||||
multi_click["timing"] = [
|
||||
"ON for at most 300ms",
|
||||
"OFF for at least 0.2s",
|
||||
]
|
||||
elif t_name == "double_click":
|
||||
multi_click["timing"] = [
|
||||
"ON for at most 300ms",
|
||||
"OFF for at most 0.2s",
|
||||
"ON for at most 300ms",
|
||||
"OFF for at least 0.2s",
|
||||
]
|
||||
elif t_name == "triple_click":
|
||||
multi_click["timing"] = [
|
||||
"ON for at most 300ms",
|
||||
"OFF for at most 0.2s",
|
||||
"ON for at most 300ms",
|
||||
"OFF for at most 0.2s",
|
||||
"ON for at most 300ms",
|
||||
"OFF for at least 0.2s",
|
||||
]
|
||||
elif t_name == "hold":
|
||||
multi_click["timing"] = ["ON for at least 1s", "OFF for at least 0.2s"]
|
||||
|
||||
actions = []
|
||||
if t_name == "click" and page_script:
|
||||
actions.append({"script.execute": page_script})
|
||||
else:
|
||||
# The Feedback call with proper ESPHome formatting
|
||||
actions.append(
|
||||
{
|
||||
"script.execute": {
|
||||
"id": "trigger_with_feedback",
|
||||
"event_id": event_id,
|
||||
"event_msg": ESPHomeLambda(
|
||||
f'return "{t_name} page " + std::to_string(id(active_page_nr));'
|
||||
),
|
||||
"led_id": (
|
||||
led_id
|
||||
if (led_id and LED_CONFIRMATION)
|
||||
else ESPHomeLambda("return nullptr;")
|
||||
),
|
||||
"r": color[0],
|
||||
"g": color[1],
|
||||
"b": color[2],
|
||||
}
|
||||
}
|
||||
)
|
||||
multi_click["then"] = actions
|
||||
sensor["on_multi_click"].append(multi_click)
|
||||
data["binary_sensor"].append(sensor)
|
||||
|
||||
return data
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
from typing import Dict, Any, List
|
||||
from buttonplus_generator.merger import deep_merge
|
||||
from buttonplus_generator.constants import *
|
||||
|
||||
|
||||
def config_services(version: int) -> Dict[str, Any]:
|
||||
# Toast needs to be after notification, so that the toast is temporarily shown over a notification if needed
|
||||
config = notification_service(version=version)
|
||||
config = deep_merge(config, toast_service())
|
||||
# config = deep_merge(config, image_service())
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def toast_service() -> Dict[str, Any]:
|
||||
generated_cpp = (
|
||||
"if(id(internal_toast).size() > 0){\n"
|
||||
" // Draw white box over existing text\n"
|
||||
" id(main_display).filled_rectangle(0, 200, 320, 40, white);\n"
|
||||
" // Show toast message\n"
|
||||
" id(main_display).print(160, 235, id(font_arial20), id(black), TextAlign::BOTTOM_CENTER, id(internal_toast).c_str());\n"
|
||||
"}\n"
|
||||
)
|
||||
|
||||
return {
|
||||
"globals": [
|
||||
{"id": "internal_toast", "type": "std::string", "initial_value": ""}
|
||||
],
|
||||
"script": [{"id": "main_draw", "then": [{"lambda": generated_cpp}]}],
|
||||
"api": {
|
||||
"actions": [
|
||||
{
|
||||
"action": "toast",
|
||||
"variables": {"timeout": "int", "message": "string"},
|
||||
"then": [
|
||||
{
|
||||
"globals.set": {
|
||||
"id": "internal_toast",
|
||||
"value": YAMLLambda("return message;"),
|
||||
}
|
||||
},
|
||||
{"script.execute": "update_main"},
|
||||
{"delay": YAMLLambda("return timeout * 1000;")},
|
||||
{
|
||||
"globals.set": {
|
||||
"id": "internal_toast",
|
||||
"value": YAMLLambda("return std::string();"),
|
||||
}
|
||||
},
|
||||
{"script.execute": "update_main"},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def notification_service(version: int) -> Dict[str, Any]:
|
||||
generated_cpp = (
|
||||
"if(id(internal_notify).size() > 0){\n"
|
||||
" // Draw white box over existing text\n"
|
||||
" id(main_display).filled_rectangle(0, 200, 320, 40, white);\n"
|
||||
" id(main_display).rectangle(0, 200, 320, 40, accent);\n"
|
||||
" // Show notification message\n"
|
||||
" id(main_display).print(160, 235, id(font_arial20), id(black), TextAlign::BOTTOM_CENTER, id(internal_notify).c_str());\n"
|
||||
"}\n"
|
||||
)
|
||||
|
||||
led_left = "main_display_left_rgb_front"
|
||||
led_right = "main_display_right_rgb_front"
|
||||
|
||||
# No display leds, switch to BAR 1 wall leds
|
||||
if version == 1:
|
||||
led_left = "bar1_left_rgb_back"
|
||||
led_right = "bar1_right_rgb_back"
|
||||
|
||||
return {
|
||||
"globals": [
|
||||
{"id": "internal_notify", "type": "std::string", "initial_value": ""}
|
||||
],
|
||||
"script": [{"id": "main_draw", "then": [{"lambda": generated_cpp}]}],
|
||||
"api": {
|
||||
"actions": [
|
||||
{
|
||||
"action": "notification",
|
||||
"variables": {"flash_leds": "bool", "message": "string"},
|
||||
"then": [
|
||||
{
|
||||
"globals.set": {
|
||||
"id": "internal_notify",
|
||||
"value": YAMLLambda("return message;"),
|
||||
}
|
||||
},
|
||||
{"script.execute": "update_main"},
|
||||
{
|
||||
"while": {
|
||||
"condition": {
|
||||
"lambda": "return (flash_leds && id(internal_notify).size() > 0);"
|
||||
},
|
||||
"then": [
|
||||
{
|
||||
"light.turn_on": {
|
||||
"id": led_left,
|
||||
"brightness": "100%",
|
||||
"red": "100%",
|
||||
"green": "100%",
|
||||
"blue": "100%",
|
||||
"flash_length": "1s",
|
||||
}
|
||||
},
|
||||
{
|
||||
"light.turn_on": {
|
||||
"id": led_right,
|
||||
"brightness": "100%",
|
||||
"red": "100%",
|
||||
"green": "100%",
|
||||
"blue": "100%",
|
||||
"flash_length": "1s",
|
||||
}
|
||||
},
|
||||
{"delay": "2s"},
|
||||
],
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"action": "notification_clear",
|
||||
"then": [
|
||||
{
|
||||
"globals.set": {
|
||||
"id": "internal_notify",
|
||||
"value": YAMLLambda("return std::string();"),
|
||||
}
|
||||
},
|
||||
{"script.execute": "update_main"},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import re
|
||||
from typing import List
|
||||
from typing import List, Dict, Any
|
||||
|
||||
from buttonplus_generator.constants import *
|
||||
|
||||
@@ -32,6 +32,27 @@ def get_display_address(num_bars : int) -> str:
|
||||
|
||||
return 0x23 - num_bars
|
||||
|
||||
def get_light_effects() -> List[Dict[str, Any]]:
|
||||
return [
|
||||
{
|
||||
"pulse": {
|
||||
"name": "Breathe",
|
||||
"transition_length": "500ms",
|
||||
"update_interval": "2s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pulse": {
|
||||
"name": "Blink",
|
||||
"transition_length": {
|
||||
"on_length": "500ms",
|
||||
"off_length": "1s"
|
||||
},
|
||||
"update_interval": "100ms"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
def string_to_click_type(ct:str) -> ClickType:
|
||||
if ct == "single":
|
||||
return ClickType.SINGLE
|
||||
|
||||
Reference in New Issue
Block a user