""" tested on uiflow for stickc v1.8.1 """ # pylint: disable=import-error import json import m5ui import machine import micropython import utils from m5stack import axp, btnA, btnB, lcd _FONT = lcd.FONT_DejaVu40 _DEFAULT_FONT_COLOR = lcd.WHITE _ALARM_TIME_PATH = "alarm.json" _SCREEN_WIDTH, _SCREEN_HEIGHT = lcd.winsize() class App: # pylint: disable=too-few-public-methods,too-many-instance-attributes def __init__(self) -> None: self._rtc = None self._clock_text_box = None self._menu_position = 0 self._alarm_hour = None self._alarm_minute = None self._alarm_hour_text_box = None self._alarm_minute_text_box = None self._alarm_timer = None def _load_alarm_time(self) -> None: with open(_ALARM_TIME_PATH, "r") as alarm_time_file: alarm_time = json.load(alarm_time_file) self._alarm_hour = alarm_time["hour"] self._alarm_minute = alarm_time["minute"] def _save_alarm_time(self) -> None: with open(_ALARM_TIME_PATH, "w") as alarm_time_file: json.dump( {"hour": self._alarm_hour, "minute": self._alarm_minute}, alarm_time_file, ) def _update_menu(self, event_arg: None) -> None: # pylint: disable=unused-argument; callback self._alarm_hour_text_box.setColor( # type: ignore lcd.GREEN if self._menu_position == 1 else (lcd.RED if self._menu_position == 2 else _DEFAULT_FONT_COLOR) ) self._alarm_minute_text_box.setColor( # type: ignore lcd.GREEN if self._menu_position == 3 else (lcd.RED if self._menu_position == 4 else _DEFAULT_FONT_COLOR) ) def _button_a_pressed(self) -> None: self._menu_position = (self._menu_position + 1) % 5 # https://docs.micropython.org/en/latest/library/micropython.html#micropython.schedule micropython.schedule(self._update_menu, None) @staticmethod def _alert() -> None: print("ALARM") def _alarm(self, timer: machine.Timer) -> None: # pylint: disable=unused-argument; callback micropython.schedule(lambda n: self._alert(), None) micropython.schedule(lambda n: self._configure_alarm_timer(), None) def _configure_alarm_timer(self) -> None: now_hour, now_minute = self._rtc.datetime()[4:6] # type: ignore minutes_until_alarm = ( (self._alarm_hour - now_hour) * 60 + self._alarm_minute - now_minute - 1 ) % (24 * 60) + 1 print("alarm in ", minutes_until_alarm, " min") self._alarm_timer.init( # type: ignore period=minutes_until_alarm * 60 * 1000, # ms mode=machine.Timer.ONE_SHOT, callback=self._alarm, ) def _update_alarm_time(self, event_arg: None) -> None: # pylint: disable=unused-argument; callback self._alarm_hour_text_box.setText( # type: ignore "{:02d}".format(self._alarm_hour) # type: ignore ) self._alarm_minute_text_box.setText( # type: ignore "{:02d}".format(self._alarm_minute) # type: ignore ) self._save_alarm_time() self._configure_alarm_timer() def _button_b_pressed(self) -> None: if self._menu_position == 1: self._alarm_hour += 1 # type: ignore elif self._menu_position == 2: self._alarm_hour -= 1 # type: ignore elif self._menu_position == 3: self._alarm_minute += 1 # type: ignore elif self._menu_position == 4: self._alarm_minute -= 1 # type: ignore self._alarm_hour %= 24 # type: ignore self._alarm_minute %= 60 # type: ignore micropython.schedule(self._update_alarm_time, None) def _format_time(self) -> str: return "{:02d}:{:02d}".format(*self._rtc.datetime()[4:6]) # type: ignore def _setup_clock(self) -> None: # > [contradictory to] official micropython documentation, to set RTC, # > use particular tuple (year , month, day, week=0, hour, minute, second, timestamp=0) # https://community.m5stack.com/topic/3108/m5stack-core2-micropython-rtc-example self._rtc = machine.RTC() # https://github.com/m5stack/UIFlow-Code/wiki/M5UI#textbox self._clock_text_box = m5ui.M5TextBox( _SCREEN_WIDTH - 1, 0, self._format_time(), _FONT, _DEFAULT_FONT_COLOR, rotate=90, ) machine.Timer(0).init( period=10000, # ms mode=machine.Timer.PERIODIC, callback=lambda t: self._clock_text_box.setText(self._format_time()), # type: ignore ) def _setup_alarm(self) -> None: if not utils.exists(_ALARM_TIME_PATH): self._alarm_hour = self._alarm_minute = 0 # type: ignore self._save_alarm_time() else: self._load_alarm_time() self._alarm_hour_text_box = m5ui.M5TextBox( _SCREEN_WIDTH // 2, 0, "{:02d}".format(self._alarm_hour), # type: ignore _FONT, _DEFAULT_FONT_COLOR, rotate=90, ) m5ui.M5TextBox( _SCREEN_WIDTH // 2, 53, ":", _FONT, _DEFAULT_FONT_COLOR, rotate=90 ) self._alarm_minute_text_box = m5ui.M5TextBox( _SCREEN_WIDTH // 2, 66, "{:02d}".format(self._alarm_minute), # type: ignore _FONT, _DEFAULT_FONT_COLOR, rotate=90, ) # machine.Timer(1).init(...) breaks button handling self._alarm_timer = machine.Timer(2) self._configure_alarm_timer() def run(self) -> None: m5ui.setScreenColor(0x000000) # clear screen print("battery:", axp.getBatVoltage(), "V") self._setup_clock() self._setup_alarm() btnA.wasPressed(self._button_a_pressed) btnB.wasPressed(self._button_b_pressed) App().run() # TODO https://docs.micropython.org/en/latest/library/machine.WDT.html#machine-wdt # TODO https://docs.micropython.org/en/latest/esp8266/tutorial/powerctrl.html#deep-sleep-mode