From: joeycastillo Date: Fri, 11 Oct 2024 00:57:57 +0000 (-0400) Subject: update 'new watch face' script X-Git-Url: https://git.earman.xyz/?a=commitdiff_plain;h=06aed9749f649d41bf0830c5a45c6d504b47ab4e;p=sensor-watch.git update 'new watch face' script --- diff --git a/movement/template/template.c b/movement/template/template.c deleted file mode 100644 index 4f2e7e1..0000000 --- a/movement/template/template.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * MIT License - * - * Copyright (c) <#year#> <#author_name#> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include "<#watch_face_name#>_face.h" - -void <#watch_face_name#>_face_setup(uint8_t watch_face_index, void ** context_ptr) { - (void) watch_face_index; - if (*context_ptr == NULL) { - *context_ptr = malloc(sizeof(<#watch_face_name#>_state_t)); - memset(*context_ptr, 0, sizeof(<#watch_face_name#>_state_t)); - // Do any one-time tasks in here; the inside of this conditional happens only at boot. - } - // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. -} - -void <#watch_face_name#>_face_activate(void *context) { - <#watch_face_name#>_state_t *state = (<#watch_face_name#>_state_t *)context; - - // Handle any tasks related to your watch face coming on screen. -} - -bool <#watch_face_name#>_face_loop(movement_event_t event, void *context) { - <#watch_face_name#>_state_t *state = (<#watch_face_name#>_state_t *)context; - - switch (event.event_type) { - case EVENT_ACTIVATE: - // Show your initial UI here. - break; - case EVENT_TICK: - // If needed, update your display here. - break; - case EVENT_LIGHT_BUTTON_UP: - // You can use the Light button for your own purposes. Note that by default, Movement will also - // illuminate the LED in response to EVENT_LIGHT_BUTTON_DOWN; to suppress that behavior, add an - // empty case for EVENT_LIGHT_BUTTON_DOWN. - break; - case EVENT_ALARM_BUTTON_UP: - // Just in case you have need for another button. - break; - case EVENT_TIMEOUT: - // Your watch face will receive this event after a period of inactivity. If it makes sense to resign, - // you may uncomment this line to move back to the first watch face in the list: - // movement_move_to_face(0); - break; - case EVENT_LOW_ENERGY_UPDATE: - // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute. - // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds. - // You should also consider starting the tick animation, to show the wearer that this is sleep mode: - // watch_start_sleep_animation(500); - break; - default: - // Movement's default loop handler will step in for any cases you don't handle above: - // * EVENT_LIGHT_BUTTON_DOWN lights the LED - // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list - // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured) - // You can override any of these behaviors by adding a case for these events to this switch statement. - return movement_default_loop_handler(event); - } - - // return true if the watch can enter standby mode. Generally speaking, you should always return true. - // Exceptions: - // * If you are displaying a color using the low-level watch_set_led_color function, you should return false. - // * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false. - // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or - // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions. - return true; -} - -void <#watch_face_name#>_face_resign(void *context) { - (void) context; - - // handle any cleanup before your watch face goes off-screen. -} - diff --git a/movement/template/template.h b/movement/template/template.h deleted file mode 100644 index 7f2612c..0000000 --- a/movement/template/template.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * MIT License - * - * Copyright (c) <#year#> <#author_name#> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef <#WATCH_FACE_NAME#>_FACE_H_ -#define <#WATCH_FACE_NAME#>_FACE_H_ - -#include "movement.h" - -/* - * A DESCRIPTION OF YOUR WATCH FACE - * - * and a description of how use it - * - */ - -typedef struct { - // Anything you need to keep track of, put it here! - uint8_t unused; -} <#watch_face_name#>_state_t; - -void <#watch_face_name#>_face_setup(uint8_t watch_face_index, void ** context_ptr); -void <#watch_face_name#>_face_activate(void *context); -bool <#watch_face_name#>_face_loop(movement_event_t event, void *context); -void <#watch_face_name#>_face_resign(void *context); - -#define <#watch_face_name#>_face ((const watch_face_t){ \ - <#watch_face_name#>_face_setup, \ - <#watch_face_name#>_face_activate, \ - <#watch_face_name#>_face_loop, \ - <#watch_face_name#>_face_resign, \ - NULL, \ -}) - -#endif // <#WATCH_FACE_NAME#>_FACE_H_ - diff --git a/movement/template/watch_face.py b/movement/template/watch_face.py deleted file mode 100644 index 4143b01..0000000 --- a/movement/template/watch_face.py +++ /dev/null @@ -1,102 +0,0 @@ -# MIT License -# -# Copyright (c) 2022 David Keck -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import os -import re -import sys -import datetime -import argparse - - -MAKEFILE_INDICATOR = "# New watch faces go above this line.\n" -INCLUDE_INDICATOR = "// New includes go above this line.\n" - - -def replace_placeholders(contents, args): - modified_contents = contents.replace("<#WATCH_FACE_NAME#>", args.watch_face_name.upper()) - modified_contents = modified_contents.replace("<#watch_face_name#>", args.watch_face_name) - modified_contents = modified_contents.replace("<#year#>", datetime.datetime.now().strftime("%Y")) - if args.author_name: - modified_contents = modified_contents.replace("<#author_name#>", " ".join(args.author_name)) - - return modified_contents - - -def write_modified_template(args, file_type, output_dir): - with open(f"template.{file_type}", 'r') as file_template: - file_contents = file_template.read() - modified_template = replace_placeholders(file_contents, args) - file_path = f"{output_dir}{args.watch_face_name}_face.{file_type}" - if not os.path.exists(file_path): - with open(file_path, 'w') as output_file: - output_file.write(modified_template) - print(f"Created {file_path}.") - else: - print(f"Generation failed: the watch face \"{args.watch_face_name}\" already exists at {file_path}. Unable to generate new files. Exiting...") - sys.exit(0) - - -def update_include_file(file_path, indicator_line, line_to_insert): - with open(file_path, 'r+') as include_file: - include_contents = include_file.readlines() - if line_to_insert not in include_contents: - new_face_index = include_contents.index(indicator_line) - include_contents.insert(new_face_index, line_to_insert) - include_file.seek(0) - include_file.writelines(include_contents) - print(f"Updated {file_path}.") - else: - print(f"Generation failed: {file_path} already has an entry for {line_to_insert.strip()}. Unable to generate new files. Exiting...") - sys.exit(0) - - -def main(): - parser = argparse.ArgumentParser(description="Create a new watch face.") - parser.add_argument("watch_face_type", metavar="face_type", type=str, choices=["clock", "complication", "demo", "sensor", "settings"], help="The type of watch face to create ('clock', 'complication', 'demo', 'sensor', 'settings')") - parser.add_argument("watch_face_name", metavar="face_name", type=str, help="The name of the watch face. Use underscores between words if you have more than one.") - parser.add_argument("--author-name", metavar="author_name", type=str, nargs='*', help="The name of the author") - - args = parser.parse_args() - - name_valid = re.fullmatch(r'[a-zA-Z_][a-zA-Z0-9_]*', args.watch_face_name) - if name_valid is None: - print(f"Generation failed: {args.watch_face_name} is not a valid C variable name. Exiting...") - sys.exit(0) - - if not args.author_name: - print("Note: the \"--author-name\" argument was not supplied via command line. \"<#author_name#>\" will not be replaced in generated template.") - - line_to_insert = f"#include \"{args.watch_face_name}_face.h\"\n" - update_include_file(f"..{os.sep}movement_faces.h", INCLUDE_INDICATOR, line_to_insert) - - line_to_insert = f" ..{os.sep}watch_faces{os.sep}{args.watch_face_type}{os.sep}{args.watch_face_name}_face.c \\\n" - update_include_file(f"..{os.sep}make{os.sep}Makefile", MAKEFILE_INDICATOR, line_to_insert) - - output_dir = f"..{os.sep}watch_faces{os.sep}{args.watch_face_type}{os.sep}" - write_modified_template(args, "h", output_dir) - write_modified_template(args, "c", output_dir) - - print(f"Successfully generated the {args.watch_face_name} {args.watch_face_type} and updated include files.") - - -if __name__ == "__main__": - main() diff --git a/template/template.c b/template/template.c new file mode 100644 index 0000000..4f2e7e1 --- /dev/null +++ b/template/template.c @@ -0,0 +1,97 @@ +/* + * MIT License + * + * Copyright (c) <#year#> <#author_name#> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "<#watch_face_name#>_face.h" + +void <#watch_face_name#>_face_setup(uint8_t watch_face_index, void ** context_ptr) { + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(<#watch_face_name#>_state_t)); + memset(*context_ptr, 0, sizeof(<#watch_face_name#>_state_t)); + // Do any one-time tasks in here; the inside of this conditional happens only at boot. + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void <#watch_face_name#>_face_activate(void *context) { + <#watch_face_name#>_state_t *state = (<#watch_face_name#>_state_t *)context; + + // Handle any tasks related to your watch face coming on screen. +} + +bool <#watch_face_name#>_face_loop(movement_event_t event, void *context) { + <#watch_face_name#>_state_t *state = (<#watch_face_name#>_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + // Show your initial UI here. + break; + case EVENT_TICK: + // If needed, update your display here. + break; + case EVENT_LIGHT_BUTTON_UP: + // You can use the Light button for your own purposes. Note that by default, Movement will also + // illuminate the LED in response to EVENT_LIGHT_BUTTON_DOWN; to suppress that behavior, add an + // empty case for EVENT_LIGHT_BUTTON_DOWN. + break; + case EVENT_ALARM_BUTTON_UP: + // Just in case you have need for another button. + break; + case EVENT_TIMEOUT: + // Your watch face will receive this event after a period of inactivity. If it makes sense to resign, + // you may uncomment this line to move back to the first watch face in the list: + // movement_move_to_face(0); + break; + case EVENT_LOW_ENERGY_UPDATE: + // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute. + // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds. + // You should also consider starting the tick animation, to show the wearer that this is sleep mode: + // watch_start_sleep_animation(500); + break; + default: + // Movement's default loop handler will step in for any cases you don't handle above: + // * EVENT_LIGHT_BUTTON_DOWN lights the LED + // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list + // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured) + // You can override any of these behaviors by adding a case for these events to this switch statement. + return movement_default_loop_handler(event); + } + + // return true if the watch can enter standby mode. Generally speaking, you should always return true. + // Exceptions: + // * If you are displaying a color using the low-level watch_set_led_color function, you should return false. + // * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false. + // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or + // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions. + return true; +} + +void <#watch_face_name#>_face_resign(void *context) { + (void) context; + + // handle any cleanup before your watch face goes off-screen. +} + diff --git a/template/template.h b/template/template.h new file mode 100644 index 0000000..3efb201 --- /dev/null +++ b/template/template.h @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) <#year#> <#author_name#> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "movement.h" + +/* + * A DESCRIPTION OF YOUR WATCH FACE + * + * and a description of how use it + * + */ + +typedef struct { + // Anything you need to keep track of, put it here! + uint8_t unused; +} <#watch_face_name#>_state_t; + +void <#watch_face_name#>_face_setup(uint8_t watch_face_index, void ** context_ptr); +void <#watch_face_name#>_face_activate(void *context); +bool <#watch_face_name#>_face_loop(movement_event_t event, void *context); +void <#watch_face_name#>_face_resign(void *context); + +#define <#watch_face_name#>_face ((const watch_face_t){ \ + <#watch_face_name#>_face_setup, \ + <#watch_face_name#>_face_activate, \ + <#watch_face_name#>_face_loop, \ + <#watch_face_name#>_face_resign, \ + NULL, \ +}) diff --git a/template/watch_face.py b/template/watch_face.py new file mode 100644 index 0000000..26d78cd --- /dev/null +++ b/template/watch_face.py @@ -0,0 +1,102 @@ +# MIT License +# +# Copyright (c) 2022 David Keck +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import os +import re +import sys +import datetime +import argparse + + +MAKEFILE_INDICATOR = "# New watch faces go above this line.\n" +INCLUDE_INDICATOR = "// New includes go above this line.\n" + + +def replace_placeholders(contents, args): + modified_contents = contents.replace("<#WATCH_FACE_NAME#>", args.watch_face_name.upper()) + modified_contents = modified_contents.replace("<#watch_face_name#>", args.watch_face_name) + modified_contents = modified_contents.replace("<#year#>", datetime.datetime.now().strftime("%Y")) + if args.author_name: + modified_contents = modified_contents.replace("<#author_name#>", " ".join(args.author_name)) + + return modified_contents + + +def write_modified_template(args, file_type, output_dir): + with open(f"template.{file_type}", 'r') as file_template: + file_contents = file_template.read() + modified_template = replace_placeholders(file_contents, args) + file_path = f"{output_dir}{args.watch_face_name}_face.{file_type}" + if not os.path.exists(file_path): + with open(file_path, 'w') as output_file: + output_file.write(modified_template) + print(f"Created {file_path}.") + else: + print(f"Generation failed: the watch face \"{args.watch_face_name}\" already exists at {file_path}. Unable to generate new files. Exiting...") + sys.exit(0) + + +def update_include_file(file_path, indicator_line, line_to_insert): + with open(file_path, 'r+') as include_file: + include_contents = include_file.readlines() + if line_to_insert not in include_contents: + new_face_index = include_contents.index(indicator_line) + include_contents.insert(new_face_index, line_to_insert) + include_file.seek(0) + include_file.writelines(include_contents) + print(f"Updated {file_path}.") + else: + print(f"Generation failed: {file_path} already has an entry for {line_to_insert.strip()}. Unable to generate new files. Exiting...") + sys.exit(0) + + +def main(): + parser = argparse.ArgumentParser(description="Create a new watch face.") + parser.add_argument("watch_face_type", metavar="face_type", type=str, choices=["clock", "complication", "demo", "sensor", "settings"], help="The type of watch face to create ('clock', 'complication', 'demo', 'sensor', 'settings')") + parser.add_argument("watch_face_name", metavar="face_name", type=str, help="The name of the watch face. Use underscores between words if you have more than one.") + parser.add_argument("--author-name", metavar="author_name", type=str, nargs='*', help="The name of the author") + + args = parser.parse_args() + + name_valid = re.fullmatch(r'[a-zA-Z_][a-zA-Z0-9_]*', args.watch_face_name) + if name_valid is None: + print(f"Generation failed: {args.watch_face_name} is not a valid C variable name. Exiting...") + sys.exit(0) + + if not args.author_name: + print("Note: the \"--author-name\" argument was not supplied via command line. \"<#author_name#>\" will not be replaced in generated template.") + + line_to_insert = f"#include \"{args.watch_face_name}_face.h\"\n" + update_include_file(f"..{os.sep}movement_faces.h", INCLUDE_INDICATOR, line_to_insert) + + line_to_insert = f" .{os.sep}watch-faces{os.sep}{args.watch_face_type}{os.sep}{args.watch_face_name}_face.c \\\n" + update_include_file(f"..{os.sep}watch-faces.mk", MAKEFILE_INDICATOR, line_to_insert) + + output_dir = f"..{os.sep}watch-faces{os.sep}{args.watch_face_type}{os.sep}" + write_modified_template(args, "h", output_dir) + write_modified_template(args, "c", output_dir) + + print(f"Successfully generated the {args.watch_face_name} {args.watch_face_type} and updated include files.") + + +if __name__ == "__main__": + main() diff --git a/watch-faces.mk b/watch-faces.mk index 98c7ef8..4d2e0fe 100644 --- a/watch-faces.mk +++ b/watch-faces.mk @@ -15,3 +15,4 @@ SRCS += \ ./watch-faces/sensor/voltage_face.c \ ./watch-faces/settings/set_time_face.c \ ./watch-faces/settings/preferences_face.c \ +# New watch faces go above this line.