diff --git a/requirements.txt b/requirements.txt index 94c698d..cd3f086 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,9 +4,9 @@ docutils==0.19 idna==3.4 Kivy==2.1.0 Kivy-Garden==0.1.5 -kivymd==1.1.1 +kivymd==1.0.2 Pillow==9.4.0 Pygments==2.14.0 requests==2.28.2 urllib3==1.26.15 -paho-mqtt==1.6.1 \ No newline at end of file +paho-mqtt==1.6.1 diff --git a/src/buildozer.spec b/src/buildozer.spec index c1efba2..8629046 100644 --- a/src/buildozer.spec +++ b/src/buildozer.spec @@ -1,6 +1,6 @@ [app] -title = Shooping List App +title = Shopping List App package.name = shoppingapp package.domain = gsog.shopping diff --git a/src/main.py b/src/main.py index ad1d6fd..313d738 100644 --- a/src/main.py +++ b/src/main.py @@ -8,7 +8,12 @@ from language import TranslationProvider, LANGUAGES from mqtt import MqttClient import kivy.utils -from kivy.properties import StringProperty, ObjectProperty, BooleanProperty, ListProperty +from kivy.properties import ( + StringProperty, + ObjectProperty, + BooleanProperty, + ListProperty, +) from kivy.metrics import dp from kivy.uix.screenmanager import Screen, ScreenManager from kivy.core.window import Window @@ -44,15 +49,13 @@ class ShoppingEntry(OneLineAvatarIconListItem): if self.edit_dialog: return - # SPRACHE buttons = [ MDFlatButton( - text=self.get_translated("cancel"), - on_release=self.close_edit_popup + text=self.get_translated("cancel"), on_release=self.close_edit_popup ), MDFlatButton( text=self.get_translated("confirm"), - on_release=lambda args: self.save_changes(args) + on_release=lambda args: self.save_changes(args), ), ] @@ -109,22 +112,21 @@ class ShoppingEntryScreen(Screen): def __init__(self, **kwargs): super().__init__(**kwargs) - + self.from_mqtt = False self.update_from_file() - + def update_from_file(self): try: entries = read_entries_from_files() except OSError: return - - self.set_entries(entries['entries']) + + self.set_entries(entries["entries"]) def update_from_mqtt(self, msg_dict): self.from_mqtt = True - self.entries = msg_dict['entries'] - + self.entries = msg_dict["entries"] def on_bestaetigen(self, *_): if self.add_dialog is None: @@ -180,36 +182,35 @@ class ShoppingEntryScreen(Screen): write_entries_to_files(entries_dict) except OSError: return - + # dont push to mqtt if coming from mqtt if not self.from_mqtt: app.mqtt.publish(entries_dict) else: self.from_mqtt = False - + @mainthread def on_entries(self, *_): print("on_entries called") self.ids["shopping_list"].clear_widgets() for entry in self.entries: - shopping_entry = ShoppingEntry(text=entry['text']) - shopping_entry.is_checked = entry['is_checked'] # type: ignore + shopping_entry = ShoppingEntry(text=entry["text"]) + shopping_entry.is_checked = entry["is_checked"] # type: ignore self.ids["shopping_list"].add_widget(shopping_entry) - self.export_entries() + self.export_entries() def get_entires(self): entries = [] for entry in self.ids.shopping_list.children: entry_dict = {"is_checked": entry.is_checked, "text": entry.text} entries.append(entry_dict) - - entries.sort(key=lambda entry: (entry['is_checked'], entry['text'])) + + entries.sort(key=lambda entry: (entry["is_checked"], entry["text"])) return entries - - def set_entries(self, entries: List[Dict[str, Union[str,bool]]]): + + def set_entries(self, entries: List[Dict[str, Union[str, bool]]]): self.entries = entries - def navigate_to_settings(self): self.export_entries() @@ -227,8 +228,8 @@ class SettingsScreen(Screen): self.initialized = False # remember previous mqtt-settings to check if they changed - self.previous_mqtt_server = app.settings.mqtt_server - self.previous_mqtt_topic = app.settings.mqtt_topic + self.previous_mqtt_server = app.settings.mqtt_server + self.previous_mqtt_topic = app.settings.mqtt_topic self.previous_mqtt_password = app.settings.mqtt_password self.previous_mqtt_username = app.settings.mqtt_username @@ -251,21 +252,25 @@ class SettingsScreen(Screen): self.menu.bind() # update the current selected from settings - self.set_item(app.settings.language) + self.set_item(app.settings.language) self.initialized = True def apply_mqtt_settings(self): - if self.previous_mqtt_server != app.settings.mqtt_server or \ - self.previous_mqtt_topic != app.settings.mqtt_topic or \ - self.previous_mqtt_password != app.settings.mqtt_password or \ - self.previous_mqtt_username != app.settings.mqtt_username: + if ( + self.previous_mqtt_server != app.settings.mqtt_server + or self.previous_mqtt_topic != app.settings.mqtt_topic + or self.previous_mqtt_password != app.settings.mqtt_password + or self.previous_mqtt_username != app.settings.mqtt_username + ): app.mqtt.disconnect() - app.mqtt.set_target(app.settings.mqtt_server, - app.settings.mqtt_topic, - 1883, - app.settings.mqtt_username, - app.settings.mqtt_password) + app.mqtt.set_target( + app.settings.mqtt_server, + app.settings.mqtt_topic, + 1883, + app.settings.mqtt_username, + app.settings.mqtt_password, + ) app.mqtt.connect() def navigate_to_shopping_list(self): @@ -285,7 +290,7 @@ class SettingsScreen(Screen): def get_translated(self, key) -> str: language = app.settings.language return TranslationProvider.get_translated(key, language) - + def update_language(self): if not self.initialized: return @@ -298,8 +303,7 @@ class SettingsScreen(Screen): if not self.initialized: return self.update_settings() - toast(self.get_translated("change_setting_restart_alert")) - # app.update_theme() # TODO (1): eventuell ohne restart + app.update_theme() def update_settings(self): if not self.initialized: @@ -316,9 +320,10 @@ class SettingsScreen(Screen): class ShoppingListApp(MDApp): settings = ObjectProperty(AppSettings(), rebind=True) + def __init__(self, **kwargs): super().__init__(**kwargs) - self.mqtt: MqttClient = None # type: ignore + self.mqtt: MqttClient = None # type: ignore def build(self): self.title = "Shopping List App" @@ -326,19 +331,18 @@ class ShoppingListApp(MDApp): print(self.settings) print(self.user_data_dir) print(self.directory) - + if kivy.utils.platform in ["android", "ios"]: - print('android or ios') - src_path = Path(self.user_data_dir, 'app') + print("android or ios") + src_path = Path(self.user_data_dir, "app") else: - print('not android or ios') + print("not android or ios") src_path = Path(self.directory) - + print(src_path) TranslationProvider.src_dir = src_path - + # TODO @Tom Theme anpassen - self.theme_cls.primary_palette = "Gray" self.update_theme() sm = ScreenManager() @@ -346,21 +350,25 @@ class ShoppingListApp(MDApp): sm.add_widget(shoppingEntryScreen) sm.add_widget(SettingsScreen(name="settings")) - self.mqtt = MqttClient(broker=self.settings.mqtt_server, - port=1883, - topic=self.settings.mqtt_topic, - client_id=None, - subscribe_callback=lambda msg_dict, _:shoppingEntryScreen.update_from_mqtt(msg_dict), - username=self.settings.mqtt_username, - password=self.settings.mqtt_password) + self.mqtt = MqttClient( + broker=self.settings.mqtt_server, + port=1883, + topic=self.settings.mqtt_topic, + client_id=None, + subscribe_callback=lambda msg_dict, _: shoppingEntryScreen.update_from_mqtt( + msg_dict + ), + username=self.settings.mqtt_username, + password=self.settings.mqtt_password, + ) try: - print('getaddrinfo google.com') - print(socket.getaddrinfo('google.com', 80)) + print("getaddrinfo google.com") + print(socket.getaddrinfo("google.com", 80)) except Exception as e: print(e) try: - print(f'getaddrinfo {self.settings.mqtt_server}') + print(f"getaddrinfo {self.settings.mqtt_server}") print(socket.getaddrinfo(self.settings.mqtt_server, 1883)) except Exception as e: print(e) @@ -374,6 +382,9 @@ class ShoppingListApp(MDApp): return sm def update_theme(self): + self.theme_cls.primary_palette = "Gray" + self.theme_cls.accent_palette = "BlueGray" + if self.settings.dark_theme: self.theme_cls.theme_style = "Dark" else: diff --git a/src/mqtt.py b/src/mqtt.py index c2bd3ac..8f0616a 100644 --- a/src/mqtt.py +++ b/src/mqtt.py @@ -13,14 +13,16 @@ class MqttSendError(ConnectionError): def __init__(self, *args: object) -> None: super().__init__("Error sending message to MQTT broker", *args) -def _get_broker_and_port(broker: str, port: int): - print('getting broker and port', broker, port) - - if ':' in broker: - broker, port = broker.split(':') # type: ignore - port = int(port) - return broker, port +def _get_broker_and_port(broker: str, port: int): + print("getting broker and port", broker, port) + + if ":" in broker: + broker, port = broker.split(":") # type: ignore + port = int(port) + + return broker, port + class MqttClient: def __init__( @@ -29,7 +31,7 @@ class MqttClient: port: int, topic: str, subscribe_callback: Callable[[dict, str], None], - username: Optional[str] = None, + username: Optional[str] = None, password: Optional[str] = None, client_id: Optional[str] = None, ) -> None: @@ -39,7 +41,14 @@ class MqttClient: self.__client: Optional[mqtt_client.Client] = None self.__subscribe_callback = subscribe_callback - def set_target(self, broker: str, topic:str, port: int, username: Optional[str] = None, password: Optional[str] = None): + def set_target( + self, + broker: str, + topic: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + ): broker, port = _get_broker_and_port(broker, port) self.__broker = broker self.__port = port @@ -48,7 +57,6 @@ class MqttClient: self.__password = password def parse_callback(self, msg, callback: Optional[Callable]): - if callback is None: callback = self.__subscribe_callback x = json.loads(msg.payload.decode()) @@ -61,7 +69,9 @@ class MqttClient: else: print("Failed to connect, return code", rc) - def publish(self, msg: dict, topic: Optional[str] = None, retain: bool = True) -> None: + def publish( + self, msg: dict, topic: Optional[str] = None, retain: bool = True + ) -> None: if self.__client is None: self.connect() if self.__client is None: @@ -84,7 +94,9 @@ class MqttClient: raise MqttConnectionError() self.__client.subscribe(self.__topic) - self.__client.on_message = lambda _client, _userdata, msg: self.parse_callback(msg, callback) + self.__client.on_message = lambda _client, _userdata, msg: self.parse_callback( + msg, callback + ) def connect(self) -> None: self.__client = mqtt_client.Client(self.__client_id) @@ -94,8 +106,8 @@ class MqttClient: self.__client.on_connect = self.on_connect print("Connecting to MQTT broker...") - print('broker:',self.__broker, 'port:', self.__port) - self.__client.connect(host=self.__broker, port= self.__port) + print("broker:", self.__broker, "port:", self.__port) + self.__client.connect(host=self.__broker, port=self.__port) self.__client.loop_start() def disconnect(self) -> None: @@ -109,7 +121,6 @@ class MqttClient: def sample2(): - broker = "broker.hivemq.com" topic = "/gsog/shopping" username = None @@ -118,7 +129,14 @@ def sample2(): def on_message(msg: dict, topic: str) -> None: print(f"Received `{msg}` from `{topic}` topic") - client = MqttClient(broker=broker, port=1883,topic=topic,subscribe_callback= on_message,username= username,password= password) + client = MqttClient( + broker=broker, + port=1883, + topic=topic, + subscribe_callback=on_message, + username=username, + password=password, + ) client.connect() count = 0 while True: @@ -138,7 +156,7 @@ def sample2(): print("Now subscribing...") client.subscribe() - + input("Press enter to quit...\n") diff --git a/src/shoppinglist.kv b/src/shoppinglist.kv index 9462d2c..2672dd4 100644 --- a/src/shoppinglist.kv +++ b/src/shoppinglist.kv @@ -6,7 +6,6 @@ ImageLeftWidget: MDCheckbox: id: shopping_entry_check - # bind to property active: root.is_checked on_active: root.is_checked = self.active @@ -23,11 +22,8 @@ MDTextField: id: shopping_entry_text pos_hint: { 'center_y': 0.4 } - # SPRACHE hint_text: root.get_translated('new_entry') text: root.text - text_color_normal: "#000000" - text_color_focus: "#000000" mode: "round" : @@ -38,7 +34,6 @@ MDTopAppBar: title: root.get_translated('app_title') md_bg_color: app.theme_cls.primary_color - specific_text_color: '#1C2120' right_action_items: [['res/settings.png', lambda x: root.navigate_to_settings()]] ScrollView: @@ -94,11 +89,8 @@ text: root.get_translated('mqtt-server') MDTextField: id: mqtt_server_text_field - # SPRACHE? hint_text: 'mqtt.server.de' mode: "round" - text_color_normal: "#000000" - text_color_focus: "#000000" text: app.settings.mqtt_server on_text: root.update_settings() @@ -106,11 +98,8 @@ text: root.get_translated('mqtt-topic') MDTextField: id: mqtt_topic_text_field - # SPRACHE? hint_text: 'topic' mode: "round" - text_color_normal: "#000000" - text_color_focus: "#000000" text: app.settings.mqtt_topic on_text: root.update_settings() @@ -118,11 +107,8 @@ text: root.get_translated('mqtt-username') MDTextField: id: mqtt_username_text_field - # SPRACHE? hint_text: 'username' mode: "round" - text_color_normal: "#000000" - text_color_focus: "#000000" text: app.settings.mqtt_username on_text: root.update_settings() @@ -132,7 +118,5 @@ id: mqtt_password_text_field password: True mode: "round" - text_color_normal: "#000000" - text_color_focus: "#000000" text: app.settings.mqtt_password on_text: root.update_settings()