From e86d5955a3fa290548e1b6ed02091c1c3785a68e Mon Sep 17 00:00:00 2001 From: Filip Wieland Date: Sat, 15 Sep 2018 22:20:20 +0100 Subject: [PATCH] Settings screen, spit and polish --- trains/api.py | 23 ++++++ trains/departure_screen.py | 110 +++++++++++++++++++++++++++ trains/main.py | 150 ++++++++++++++++--------------------- trains/screen.py | 28 +++++++ trains/settings_screen.py | 19 +++++ trains/splash.gif | Bin 0 -> 25760 bytes trains/utils.py | 19 +++++ 7 files changed, 264 insertions(+), 85 deletions(-) create mode 100644 trains/api.py create mode 100644 trains/departure_screen.py create mode 100644 trains/screen.py create mode 100644 trains/settings_screen.py create mode 100644 trains/splash.gif create mode 100644 trains/utils.py diff --git a/trains/api.py b/trains/api.py new file mode 100644 index 0000000..4da4eaf --- /dev/null +++ b/trains/api.py @@ -0,0 +1,23 @@ +import http +import ujson +from tilda import LED + +API_URL = "https://huxley.apphb.com/all/{}?expand=true&accessToken=D102521A-06C6-44C9-8693-7A0394C757EF" + +def get_trains(station_code='LBG'): + print('trains/api: Getting trains for {}'.format(station_code)) + station_data = None + + LED(LED.RED).on() # Red for total get_trains + try: + station_json = http.get(API_URL.format( + station_code)).raise_for_status().content + LED(LED.GREEN).on() # Green for parsing + station_data = ujson.loads(station_json) + except Exception as e: + print('Error:') + print(e) + + LED(LED.RED).off() + LED(LED.GREEN).off() + return station_data diff --git a/trains/departure_screen.py b/trains/departure_screen.py new file mode 100644 index 0000000..2d50dde --- /dev/null +++ b/trains/departure_screen.py @@ -0,0 +1,110 @@ +import sleep +import ugfx +import database +from time import time +from homescreen import time_as_string +from tilda import Buttons +from trains.screen import Screen, S_CONTINUE, S_TO_SETTINGS +from trains.api import get_trains +from trains.utils import get_departure, get_title, is_red + +UPDATE_INTERVAL_SECS = 30 + +class DepartureScreen(Screen): + def __init__(self): + self.station_data = None + self.has_error = False + self.last_update = 0 + self.should_redraw = True + + self._names = None + self._old_names = None + + def enter(self): + self.next_state = S_CONTINUE + self.station_code = database.get('trains.station_code', 'LBG') + self.last_update = 0 + Buttons.enable_interrupt( + Buttons.BTN_A, + lambda t: self.set_next_state(S_TO_SETTINGS), + on_press=True, + on_release=False + ) + + def set_next_state(self, s): + self.next_state = s + + def update(self): + now = time() + if self.last_update < (now - UPDATE_INTERVAL_SECS): + print('trains/departure_screen: Updating data') + new_station_data = get_trains(self.station_code) + if new_station_data == None: + self.has_error = True + self.should_redraw = True + else: + self.station_data = new_station_data + self.has_error = False + self.should_redraw = True + self.last_update = now + + def tick(self): + self.update() + + if self.should_redraw: + if self.station_data == None: + self.show_error() + else: + self.show_trains() + else: + self._destroy_old_names() + + sleep.sleep_ms(500) + + return self.next_state + + def _get_names_container(self): + if self._names != None: + self._names.hide() + self._old_names = self._names + names = ugfx.Container(0, 25, 190, 295) + self._names = names + return names + + def _destroy_old_names(self): + if self._old_names != None: + self._old_names.destroy() + self._old_names = None + def _destroy_names(self): + if self._names != None: + self._names.destroy() + self._names = None + + def show_trains(self): + ugfx.clear() + ugfx.area(0, 0, 240, 25, ugfx.RED if self.has_error else ugfx.GRAY) + title = get_title(self.station_data['locationName'], self.has_error) + ugfx.text(5, 5, title, ugfx.WHITE if self.has_error else ugfx.BLACK) + ugfx.text(195, 5, time_as_string(), ugfx.BLUE) + + names = self._get_names_container() + names.show() + row_num = 0 + for service in self.station_data['trainServices']: + departure = get_departure(service) + if departure: + names.text(5, 15 * row_num, service['destination'][0]['locationName'], ugfx.BLACK) + ugfx.text(195, 25 + (15 * row_num), departure,ugfx.RED if is_red(service) else ugfx.BLUE) + row_num += 1 + + self.should_redraw = False + + def show_error(self): + ugfx.clear() + ugfx.text(5, 5, 'Error :(', ugfx.RED) + self.should_redraw = False + + def exit(self): + self._destroy_old_names() + self._destroy_names() + Buttons.disable_all_interrupt() diff --git a/trains/main.py b/trains/main.py index d5a0ffc..0d681e6 100644 --- a/trains/main.py +++ b/trains/main.py @@ -6,107 +6,87 @@ ___title___ = "trains" ___license___ = "MIT" ___dependencies___ = ["app", "sleep", "wifi", "http", "ugfx_helper"] ___categories___ = ["Homescreens", "Other"] +___bootstrapped___ = False -# Config - -STATION_CODE = "DEP" -API_URL = "https://huxley.apphb.com/all/{}?expand=true&accessToken=D102521A-06C6-44C9-8693-7A0394C757EF" +import database import wifi import ugfx -import http -import ujson import app import sleep +import ntp from tilda import Buttons, LED +from trains import api +from trains import screen +from trains.departure_screen import DepartureScreen +from trains.settings_screen import SettingsScreen + +def init_screen(orientation): + # initialize screen + ugfx.clear() + ugfx.orientation(orientation) + ugfx.backlight(50) + # show initial screen + # photo credit: https://www.flickr.com/photos/remedy451/8061918891 + ugfx.display_image(0, 0, 'trains/splash.gif', 90) + def init(): - # initialize screen + print('trains/main: Init') ugfx.init() - ugfx.clear() - ugfx.orientation(90) - ugfx.backlight(50) - + ntp.set_NTP_time() # ensure wifi connection if not wifi.is_connected(): - wifi.connect(show_wait_message=True) - - # show initial screen - ugfx.text(5, 5, "Will monitor station:", ugfx.BLACK) - ugfx.text(200, 5, STATION_CODE, ugfx.BLUE) - -def get_trains(): - station_data = None + wifi.connect(show_wait_message=False) - LED(LED.RED).on() # Red for total get_trains - try: - station_json = http.get(API_URL.format(STATION_CODE)).raise_for_status().content - LED(LED.GREEN).on() # Green for parsing - station_data = ujson.loads(station_json) - except: - print('Fuck') - LED(LED.RED).off() - LED(LED.GREEN).off() - return station_data - -def get_time(station_data): - return ':'.join(station_data['generatedAt'].split('T')[1].split(':')[0:2]) - -def is_red(service): - return service['isCancelled'] or service['etd'] != 'On time' - -def get_arrival(service): - if service['isCancelled']: - return 'CANX' - - if service['eta'] == 'On time': - return service['sta'] - - return service['eta'] - -def get_title(name, has_error): - if has_error: - return 'ERR ' + name - - return name - -def show_trains(station_data, has_error): +def exit(): + print('trains/main: Exit') ugfx.clear() - ugfx.area(0, 0, 240, 25, - ugfx.RED if has_error else ugfx.GRAY) - title = get_title(station_data['locationName'], has_error) - ugfx.text(5, 5, title, - ugfx.WHITE if has_error else ugfx.BLACK) - ugfx.text(195, 5, get_time(station_data), ugfx.BLUE) - names = ugfx.Container(0, 25, 190, 295) - names.show() - for idx, service in enumerate(station_data['trainServices']): - names.text(5, 15 * idx, service['destination'][0]['locationName'], ugfx.BLACK) - ugfx.text(195, 25 + (15 * idx), get_arrival(service), ugfx.RED if is_red(service) else ugfx.BLUE) + app.restart_to_default() -def show_error(): - ugfx.clear() - ugfx.text(5, 5, 'Error :(', ugfx.RED) + +app_screens = { + screen.SETTINGS: SettingsScreen(), + screen.DEPARTURES: DepartureScreen() +} + + +def get_initial_screen(): + station_code = database.get('trains.station_code', None) + if station_code == None: + return app_screens[screen.SETTINGS] + return app_screens[screen.DEPARTURES] + + +def run_screen(instance): + print('trains/main: Starting screen {}'.format(instance)) + instance.enter() + + is_running = True + next_screen_name = None + while is_running: + status, value = instance.tick() + + if status == screen.SWITCH_SCREEN: + is_running = False + next_screen_name = value + elif status == screen.EXIT_APP: + is_running = False + + print('trains/main: Stopping screen {} (next = {})'.format(instance, next_screen_name)) + instance.exit() + return next_screen_name init() -station_data = None -has_error = False -while (not Buttons.is_pressed(Buttons.BTN_A)) and (not Buttons.is_pressed(Buttons.BTN_B)) and (not Buttons.is_pressed(Buttons.BTN_Menu)): - new_station_data = get_trains() - if new_station_data == None: - has_error = True - else: - station_data = new_station_data - has_error = False - - if station_data == None: - show_error() - else: - show_trains(station_data, has_error) - sleep.sleep_ms(30 * 1000) +current_screen = get_initial_screen() +is_app_running = True +while is_app_running: + init_screen(current_screen.orientation()) + next_screen_name = run_screen(current_screen) - -# closing -ugfx.clear() -app.restart_to_default() + if next_screen_name != None: + current_screen = app_screens[next_screen_name] + else: + is_app_running = False + exit() diff --git a/trains/screen.py b/trains/screen.py new file mode 100644 index 0000000..54752c5 --- /dev/null +++ b/trains/screen.py @@ -0,0 +1,28 @@ +CONTINUE = 1 +SWITCH_SCREEN = 2 +EXIT_APP = 3 + +DEPARTURES = 10 +SETTINGS = 11 + +S_CONTINUE = (CONTINUE, None) +S_TO_SETTINGS = (SWITCH_SCREEN, SETTINGS) +S_TO_TRAINS = (SWITCH_SCREEN, DEPARTURES) +S_EXIT = (EXIT_APP, None) + + +class Screen(): + def __init__(self): + pass + + def orientation(self): + return 90 + + def enter(self): + pass + + def tick(self): + return S_CONTINUE + + def exit(self): + pass diff --git a/trains/settings_screen.py b/trains/settings_screen.py new file mode 100644 index 0000000..42f6421 --- /dev/null +++ b/trains/settings_screen.py @@ -0,0 +1,19 @@ +import database +import ugfx +from dialogs import prompt_text +from trains.screen import Screen, S_CONTINUE, S_TO_TRAINS + +class SettingsScreen(Screen): + def __init__(self): + self.next_state = S_TO_TRAINS + + def orientation(self): + return 270 + + def tick(self): + with database.Database() as db: + crs = prompt_text('Enter your station\'s CRS code', db.get('trains.station_code', '')) + db.set('trains.station_code', crs) + + return self.next_state + \ No newline at end of file diff --git a/trains/splash.gif b/trains/splash.gif new file mode 100644 index 0000000000000000000000000000000000000000..d3d64b0d5d8c678a94f9de796ba642bee8cbe973 GIT binary patch literal 25760 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