From 314499109ccab19b81b8b950669b780f299fc50a Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 22 Oct 2023 01:12:55 +0200 Subject: [PATCH] Added telemetry system, location sharing and map view for shared locations --- sbapp/main.py | 276 ++++++++++++++++++++++++++--------------- sbapp/mapview/view.py | 6 +- sbapp/sideband/core.py | 169 ++++++++++++++++++------- sbapp/ui/layouts.py | 19 ++- 4 files changed, 322 insertions(+), 148 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 3629a8a..ab44dc4 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -44,8 +44,9 @@ from kivy.uix.screenmanager import FadeTransition, NoTransition from kivymd.uix.list import OneLineIconListItem from kivy.properties import StringProperty from kivymd.uix.pickers import MDColorPicker +from kivymd.uix.button import BaseButton, MDIconButton from sideband.sense import Telemeter -from mapview import MapMarker +from mapview import CustomMapMarker if RNS.vendor.platformutils.get_platform() == "android": from sideband.core import SidebandCore @@ -922,6 +923,11 @@ class SidebandApp(MDApp): self.root.ids.message_send_button.disabled = False Clock.schedule_once(cb, 0.5) + def peer_show_location_action(self, sender): + if self.root.ids.screen_manager.current == "messages_screen": + context_dest = self.root.ids.messages_scrollview.active_conversation + self.map_show_peer_location(context_dest) + def message_propagation_action(self, sender): if self.outbound_mode_paper: self.outbound_mode_paper = False @@ -1289,6 +1295,10 @@ class SidebandApp(MDApp): self.sideband.save_configuration() self.update_ui_theme() + def save_advanced_stats(sender=None, event=None): + self.sideband.config["advanced_stats"] = self.root.ids.settings_advanced_statistics.active + self.sideband.save_configuration() + def save_notifications_on(sender=None, event=None): self.sideband.config["notifications_on"] = self.root.ids.settings_notifications_on.active self.sideband.save_configuration() @@ -1377,6 +1387,9 @@ class SidebandApp(MDApp): self.root.ids.settings_eink_mode.active = self.sideband.config["eink_mode"] self.root.ids.settings_eink_mode.bind(active=save_eink_mode) + self.root.ids.settings_advanced_statistics.active = self.sideband.config["advanced_stats"] + self.root.ids.settings_advanced_statistics.bind(active=save_advanced_stats) + self.root.ids.settings_start_announce.active = self.sideband.config["start_announce"] self.root.ids.settings_start_announce.bind(active=save_start_announce) @@ -2898,6 +2911,7 @@ class SidebandApp(MDApp): self.sideband.config["telemetry_icon"] = self.root.ids.telemetry_icon_preview.icon self.sideband.save_configuration() + self.own_appearance_changed = True def telemetry_enabled_toggle(self, sender=None, event=None): @@ -2909,9 +2923,10 @@ class SidebandApp(MDApp): def telemetry_location_toggle(self, sender=None, event=None): if self.root.ids.telemetry_s_location.active: - if not check_permission("android.permission.ACCESS_COARSE_LOCATION") or not check_permission("android.permission.ACCESS_FINE_LOCATION"): - RNS.log("Requesting location permission", RNS.LOG_DEBUG) - request_permissions(["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"]) + if RNS.vendor.platformutils.is_android(): + if not check_permission("android.permission.ACCESS_COARSE_LOCATION") or not check_permission("android.permission.ACCESS_FINE_LOCATION"): + RNS.log("Requesting location permission", RNS.LOG_DEBUG) + request_permissions(["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"]) self.telemetry_save() @@ -2984,6 +2999,7 @@ class SidebandApp(MDApp): self.root.ids.telemetry_icon_preview.icon_color = color self.sideband.config["telemetry_fg"] = color self.sideband.save_configuration() + self.own_appearance_changed = True if hasattr(self, "color_picker") and self.color_picker != None: self.color_picker.dismiss() self.color_picker = None @@ -3000,6 +3016,8 @@ class SidebandApp(MDApp): color = selected_color[:-1] + [1] self.root.ids.telemetry_icon_preview.md_bg_color = color self.sideband.config["telemetry_bg"] = color + self.sideband.save_configuration() + self.own_appearance_changed = True if hasattr(self, "color_picker") and self.color_picker != None: self.color_picker.dismiss() self.color_picker = None @@ -3057,7 +3075,7 @@ class SidebandApp(MDApp): from mapview import MapView mapview = MapView(zoom=self.sideband.config["map_zoom"], lat=self.sideband.config["map_lat"], lon=self.sideband.config["map_lon"]) mapview.snap_to_zoom = False - mapview.double_tap_zoom = False + mapview.double_tap_zoom = True self.root.ids.map_layout.map = mapview self.root.ids.map_layout.add_widget(self.root.ids.map_layout.map) @@ -3073,11 +3091,71 @@ class SidebandApp(MDApp): self.map_update_markers() Clock.schedule_once(am_job, 0.6) + def close_location_error_dialog(self, sender=None): + if hasattr(self, "location_error_dialog") and self.location_error_dialog != None: + self.location_error_dialog.dismiss() + + def map_show(self, location): + RNS.log(str(location), RNS.LOG_WARNING) + if hasattr(self.root.ids.map_layout, "map") and self.root.ids.map_layout.map: + self.root.ids.map_layout.map.lat = location["latitude"] + self.root.ids.map_layout.map.lon = location["longtitude"] + self.root.ids.map_layout.map.zoom = 16 + + def map_show_peer_location(self, context_dest): + location = self.sideband.peer_location(context_dest) + if not location: + self.location_error_dialog = MDDialog( + title="No Location", + text="During the last 24 hours, no location updates have been received from this peer. You can use the the [b]Situation Map[/b] to manually search for earlier telemetry.", + buttons=[ + MDRectangleFlatButton( + text="OK", + font_size=dp(18), + on_release=self.close_location_error_dialog + ) + ], + ) + self.location_error_dialog.open() + else: + self.map_action() + self.map_show(location) + + def map_create_marker(self, source, telemetry, appearance): + try: + l = telemetry["location"] + a_icon = appearance[0] + a_fg = appearance[1]; a_bg = appearance[2] + marker = CustomMapMarker(lat=l["latitude"], lon=l["longtitude"], icon_bg=a_bg) + marker.source_dest = source + marker.location_time = l["last_update"] + marker.icon = MDMapIconButton( + icon=a_icon, icon_color=a_fg, + md_bg_color=a_bg, theme_icon_color="Custom", + icon_size=dp(32), + ) + marker.icon._default_icon_pad = dp(16) + marker.add_widget(marker.icon) + + ######## + # marker.badge = MDMapIconButton( + # icon="network-strength-2", icon_color=[0,0,0,1], + # md_bg_color=[1,1,1,1], theme_icon_color="Custom", + # icon_size=dp(18), + # ) + # marker.badge._default_icon_pad = dp(5) + # marker.icon.add_widget(marker.badge) + ######## + + return marker + + except Exception as e: + RNS.log("Could not create map marker for "+RNS.prettyhexrep(source)+": "+str(e), RNS.LOG_ERROR) + return None + + def map_update_markers(self, sender=None): - # TODO: Remove - # time_s = time.time() - # RNS.log("Update map markers", RNS.LOG_WARNING) earliest = time.time() - self.sideband.config["map_history_limit"] telemetry_entries = self.sideband.list_telemetry(after=earliest) own_address = self.sideband.lxmf_destination.hash @@ -3086,113 +3164,106 @@ class SidebandApp(MDApp): # Add own marker if available retain_own = False own_telemetry = self.sideband.get_telemetry() - if own_telemetry != None and "location" in own_telemetry and own_telemetry["location"]["latitude"] != None and own_telemetry["location"]["longtitude"] != None: - retain_own = True - o = own_telemetry["location"] - if not own_address in self.map_markers: - # TODO: Remove - RNS.log("Adding own marker", RNS.LOG_WARNING) - marker = MapMarker(lat=o["latitude"], lon=o["longtitude"]) - marker.source_dest = own_address - marker.latest_timestamp = o["last_update"] - self.map_markers[own_address] = marker - self.root.ids.map_layout.map.add_widget(marker) - changes = True - else: - marker = self.map_markers[own_address] - if o["last_update"] > marker.latest_timestamp: - # TODO: Remove - RNS.log("Updating own marker", RNS.LOG_WARNING) - marker.latest_timestamp = o["last_update"] - marker.lat = o["latitude"] - marker.lon = o["longtitude"] - changes = True - else: - # TODO: Remove - RNS.log("Skipped updating own marker, no new location", RNS.LOG_WARNING) - else: - # TODO: Remove - RNS.log("Not adding own marker, no data", RNS.LOG_WARNING) + own_appearance = [ + self.sideband.config["telemetry_icon"], + self.sideband.config["telemetry_fg"], + self.sideband.config["telemetry_bg"] + ] - stale_markers = [] - for marker in self.map_markers: - if not marker in telemetry_entries: - if marker == own_address: - if not retain_own: - # TODO: Remove - RNS.log("Setting own marker for removal: "+str(marker), RNS.LOG_WARNING) + try: + if own_telemetry != None and "location" in own_telemetry and own_telemetry["location"] != None and own_telemetry["location"]["latitude"] != None and own_telemetry["location"]["longtitude"] != None: + retain_own = True + + if not own_address in self.map_markers: + RNS.log("Adding own marker", RNS.LOG_WARNING) + marker = self.map_create_marker(own_address, own_telemetry, own_appearance) + if marker != None: + self.map_markers[own_address] = marker + self.root.ids.map_layout.map.add_marker(marker) + changes = True + + else: + marker = self.map_markers[own_address] + o = own_telemetry["location"] + if o["last_update"] > marker.location_time or (hasattr(self, "own_appearance_changed") and self.own_appearance_changed): + marker.location_time = o["last_update"] + marker.lat = o["latitude"] + marker.lon = o["longtitude"] + marker.icon.icon = own_appearance[0] + marker.icon.icon_color = own_appearance[1] + marker.icon.md_bg_color = own_appearance[2] + self.own_appearance_changed = False + changes = True + + stale_markers = [] + for marker in self.map_markers: + if not marker in telemetry_entries: + if marker == own_address: + if not retain_own: + stale_markers.append(marker) + else: stale_markers.append(marker) - else: - # TODO: Remove - RNS.log("Setting marker for removal: "+str(marker), RNS.LOG_WARNING) - stale_markers.append(marker) - for marker in stale_markers: - try: - self.root.ids.map_layout.map.remove_widget(self.map_markers[marker]) - self.map_markers.pop(marker) - except Exception as e: - RNS.log("Error while removing map marker: "+str(e), RNS.LOG_ERROR) + for marker in stale_markers: + try: + self.root.ids.map_layout.map.remove_widget(self.map_markers[marker]) + self.map_markers.pop(marker) + except Exception as e: + RNS.log("Error while removing map marker: "+str(e), RNS.LOG_ERROR) + + except Exception as e: + RNS.log("Error while updating own map marker: "+str(e), RNS.LOG_ERROR) for telemetry_source in telemetry_entries: - skip = False - - # TODO: Remove - RNS.log("Processing telemetry for "+RNS.prettyhexrep(telemetry_source)+"/"+RNS.prettyhexrep(self.sideband.lxmf_destination.hash), RNS.LOG_WARNING) - - if telemetry_source == own_address: - # TODO: Remove - RNS.log("Skipping own telemetry", RNS.LOG_WARNING) - skip = True - elif telemetry_source in self.map_markers: - marker = self.map_markers[telemetry_source] - newest_timestamp = telemetry_entries[telemetry_source][0][0] - if newest_timestamp <= marker.latest_timestamp: + try: + skip = False + if telemetry_source == own_address: skip = True + elif telemetry_source in self.map_markers: + marker = self.map_markers[telemetry_source] + newest_timestamp = telemetry_entries[telemetry_source][0][0] + if newest_timestamp <= marker.location_time: + skip = True - latest_viewable = None - if not skip: - for telemetry_entry in telemetry_entries[telemetry_source]: - telemetry_timestamp = telemetry_entry[0] - telemetry_data = telemetry_entry[1] - t = Telemeter.from_packed(telemetry_data) - if t != None: - telemetry = t.read_all() - # TODO: Remove - # RNS.log(str(telemetry)+" "+str(t), RNS.LOG_WARNING) - if "location" in telemetry and telemetry["location"]["latitude"] != None and telemetry["location"]["longtitude"] != None: - latest_viewable = telemetry - break + latest_viewable = None + if not skip: + for telemetry_entry in telemetry_entries[telemetry_source]: + telemetry_timestamp = telemetry_entry[0] + telemetry_data = telemetry_entry[1] + t = Telemeter.from_packed(telemetry_data) + if t != None: + telemetry = t.read_all() + if "location" in telemetry and telemetry["location"]["latitude"] != None and telemetry["location"]["longtitude"] != None: + latest_viewable = telemetry + break - if latest_viewable != None: - l = latest_viewable["location"] - if not telemetry_source in self.map_markers: - marker = MapMarker(lat=l["latitude"], lon=l["longtitude"]) - marker.source_dest = telemetry_source - marker.latest_timestamp = latest_viewable["time"]["utc"] - self.map_markers[telemetry_source] = marker - self.root.ids.map_layout.map.add_widget(marker) - changes = True - else: - marker = self.map_markers[telemetry_source] - marker.latest_timestamp = latest_viewable["time"]["utc"] - marker.lat = l["latitude"] - marker.lon = l["longtitude"] - changes = True + if latest_viewable != None: + l = latest_viewable["location"] + if not telemetry_source in self.map_markers: + marker = self.map_create_marker(telemetry_source, latest_viewable, self.sideband.peer_appearance(telemetry_source)) + if marker != None: + self.map_markers[telemetry_source] = marker + self.root.ids.map_layout.map.add_marker(marker) + changes = True + else: + marker = self.map_markers[telemetry_source] + marker.location_time = latest_viewable["time"]["utc"] + marker.lat = l["latitude"] + marker.lon = l["longtitude"] + appearance = self.sideband.peer_appearance(telemetry_source) + marker.icon.icon = appearance[0] + marker.icon.icon_color = appearance[1] + marker.icon.md_bg_color = appearance[2] + changes = True + + except Exception as e: + RNS.log("Error while updating map entry for "+RNS.prettyhexrep(telemetry_source)+": "+str(e), RNS.LOG_ERROR) self.last_map_update = time.time() if changes: mv = self.root.ids.map_layout.map mv.trigger_update(True) - # TODO: Remove - # RNS.log("Updated map markers in "+RNS.prettytime(time.time()-time_s), RNS.LOG_WARNING) - - - def map_add_marker(self, marker): - marker = MapMarker(lat=0.0, lon=0.0) - self.root.ids.map_layout.map.add_widget(marker) - ### Guide screen ###################################### def close_guide_action(self, sender=None): @@ -3307,6 +3378,9 @@ Thank you very much for using Free Communications Systems. class CustomOneLineIconListItem(OneLineIconListItem): icon = StringProperty() +class MDMapIconButton(MDIconButton): + pass + def run(): SidebandApp().run() diff --git a/sbapp/mapview/view.py b/sbapp/mapview/view.py index de074b6..678c338 100644 --- a/sbapp/mapview/view.py +++ b/sbapp/mapview/view.py @@ -327,9 +327,9 @@ class MarkerMapLayer(MapLayer): marker.y = int(y - marker.height * marker.anchor_y) if hasattr(marker, "children"): if marker.children != None and len(marker.children) > 0: - c = marker.children[0] - c.x = marker.x - c.y = marker.y+dp(16) + for c in marker.children: + c.x = marker.x + c.y = marker.y+dp(16) def unload(self): self.clear_widgets() diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index df46627..b760ad0 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -73,14 +73,14 @@ class SidebandCore(): SERVICE_JOB_INTERVAL = 1 PERIODIC_JOBS_INTERVAL = 60 PERIODIC_SYNC_RETRY = 360 - # TODO: Reset - # TELEMETRY_INTERVAL = 60 - TELEMETRY_INTERVAL = 10 + TELEMETRY_INTERVAL = 60 IF_CHANGE_ANNOUNCE_MIN_INTERVAL = 6 # In seconds AUTO_ANNOUNCE_RANDOM_MIN = 90 # In minutes AUTO_ANNOUNCE_RANDOM_MAX = 480 # In minutes + DEFAULT_APPEARANCE = ["alpha-p-circle-outline", [0,0,0,1], [1,1,1,1]] + aspect_filter = "lxmf.delivery" def received_announce(self, destination_hash, announced_identity, app_data): # Add the announce to the directory announce @@ -305,7 +305,7 @@ class SidebandCore(): # Telemetry self.config["telemetry_enabled"] = False - self.config["telemetry_icon"] = "alpha-p-circle-outline" + self.config["telemetry_icon"] = SidebandCore.DEFAULT_APPEARANCE[0] self.config["telemetry_send_to_trusted"] = False self.config["telemetry_send_to_collector"] = False @@ -315,6 +315,7 @@ class SidebandCore(): self._db_initstate() self._db_initpersistent() self._db_inittelemetry() + self._db_upgradetables() self.__save_config() @@ -338,6 +339,8 @@ class SidebandCore(): self.config["debug"] = False if not "dark_ui" in self.config: self.config["dark_ui"] = True + if not "advanced_stats" in self.config: + self.config["advanced_stats"] = False if not "lxmf_periodic_sync" in self.config: self.config["lxmf_periodic_sync"] = False if not "lxmf_ignore_unknown" in self.config: @@ -454,11 +457,11 @@ class SidebandCore(): self.config["telemetry_send_to_collector"] = False if not "telemetry_icon" in self.config: - self.config["telemetry_icon"] = "alpha-p-circle-outline" + self.config["telemetry_icon"] = SidebandCore.DEFAULT_APPEARANCE[0] if not "telemetry_fg" in self.config: - self.config["telemetry_fg"] = [0,0,0,1] + self.config["telemetry_fg"] = SidebandCore.DEFAULT_APPEARANCE[1] if not "telemetry_bg" in self.config: - self.config["telemetry_bg"] = [1,1,1,1] + self.config["telemetry_bg"] = SidebandCore.DEFAULT_APPEARANCE[2] if not "telemetry_send_appearance" in self.config: self.config["telemetry_send_appearance"] = False @@ -503,6 +506,7 @@ class SidebandCore(): self._db_initstate() self._db_initpersistent() self._db_inittelemetry() + self._db_upgradetables() self.__db_indices() def __reload_config(self): @@ -648,6 +652,9 @@ class SidebandCore(): RNS.log("Error while getting peer name: "+str(e), RNS.LOG_ERROR) return "" + def peer_appearance(self, context_dest): + return self._db_get_appearance(context_dest) or SidebandCore.DEFAULT_APPEARANCE + def peer_display_name(self, context_dest): try: existing_conv = self._db_conversation(context_dest) @@ -730,6 +737,31 @@ class SidebandCore(): def list_telemetry(self, context_dest = None, after = None, before = None, limit = None): return self._db_telemetry(context_dest = context_dest, after = after, before = before, limit = limit) or [] + def peer_telemetry(self, context_dest, after = None, before = None, limit = None): + pts = self._db_telemetry(context_dest, after = after, before = before, limit = limit) + if pts != None: + if context_dest in pts: + return pts[context_dest] + + return [] + + def peer_location(self, context_dest): + after_time = time.time()-24*60*60 + pts = self.peer_telemetry(context_dest, after=after_time) + for pt in pts: + try: + t = Telemeter.from_packed(pt[1]).read_all() + RNS.log(str(t), RNS.LOG_WARNING) + if "location" in t: + l = t["location"] + if "latitude" in l and "longtitude" in l: + if l["latitude"] != None and l["longtitude"] != None: + return l + except: + pass + + return None + def list_messages(self, context_dest, after = None, before = None, limit = None): result = self._db_messages(context_dest, after, before, limit) if result != None: @@ -829,7 +861,7 @@ class SidebandCore(): dbc = db.cursor() dbc.execute("DROP TABLE IF EXISTS lxm") - dbc.execute("CREATE TABLE lxm (lxm_hash BLOB PRIMARY KEY, dest BLOB, source BLOB, title BLOB, tx_ts INTEGER, rx_ts INTEGER, state INTEGER, method INTEGER, t_encrypted INTEGER, t_encryption INTEGER, data BLOB)") + dbc.execute("CREATE TABLE lxm (lxm_hash BLOB PRIMARY KEY, dest BLOB, source BLOB, title BLOB, tx_ts INTEGER, rx_ts INTEGER, state INTEGER, method INTEGER, t_encrypted INTEGER, t_encryption INTEGER, data BLOB, extra BLOB)") dbc.execute("DROP TABLE IF EXISTS conv") dbc.execute("CREATE TABLE conv (dest_context BLOB PRIMARY KEY, last_tx INTEGER, last_rx INTEGER, unread INTEGER, type INTEGER, trust INTEGER, name BLOB, data BLOB)") @@ -837,6 +869,9 @@ class SidebandCore(): dbc.execute("DROP TABLE IF EXISTS announce") dbc.execute("CREATE TABLE announce (id PRIMARY KEY, received INTEGER, source BLOB, data BLOB, dest_type BLOB)") + dbc.execute("DROP TABLE IF EXISTS telemetry") + dbc.execute("CREATE TABLE IF NOT EXISTS telemetry (id INTEGER PRIMARY KEY, dest_context BLOB, ts INTEGER, data BLOB)") + dbc.execute("DROP TABLE IF EXISTS state") dbc.execute("CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB)") @@ -853,6 +888,23 @@ class SidebandCore(): dbc.execute("CREATE UNIQUE INDEX IF NOT EXISTS idx_conv_dest_context ON conv(dest_context)") db.commit() + def _db_inittelemetry(self): + db = self.__db_connect() + dbc = db.cursor() + + dbc.execute("CREATE TABLE IF NOT EXISTS telemetry (id INTEGER PRIMARY KEY, dest_context BLOB, ts INTEGER, data BLOB)") + db.commit() + + def _db_upgradetables(self): + # TODO: Remove this again at some point in the future + db = self.__db_connect() + dbc = db.cursor() + dbc.execute("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'lxm' AND sql LIKE '%extra%'") + result = dbc.fetchall() + if len(result) == 0: + dbc.execute("ALTER TABLE lxm ADD COLUMN extra BLOB") + db.commit() + def _db_initstate(self): db = self.__db_connect() dbc = db.cursor() @@ -930,13 +982,6 @@ class SidebandCore(): dbc.execute("CREATE TABLE IF NOT EXISTS persistent (property BLOB PRIMARY KEY, value BLOB)") db.commit() - def _db_inittelemetry(self): - db = self.__db_connect() - dbc = db.cursor() - - dbc.execute("CREATE TABLE IF NOT EXISTS telemetry (id INTEGER PRIMARY KEY, dest_context BLOB, ts INTEGER, data BLOB)") - db.commit() - def _db_getpersistent(self, prop): try: db = self.__db_connect() @@ -1079,10 +1124,7 @@ class SidebandCore(): return results - def _db_save_telemetry(self, context_dest, telemetry): - # TODO: Remove - # RNS.log("Saving telemetry for "+RNS.prettyhexrep(context_dest), RNS.LOG_WARNING) - + def _db_save_telemetry(self, context_dest, telemetry, physical_link = None): try: remote_telemeter = Telemeter.from_packed(telemetry) telemetry_timestamp = remote_telemeter.read_all()["time"]["utc"] @@ -1095,9 +1137,15 @@ class SidebandCore(): result = dbc.fetchall() if len(result) != 0: - # TODO: Remove - # RNS.log("Telemetry entry already exists, ignoring", RNS.LOG_WARNING) return + + if physical_link != None and len(physical_link) != 0: + remote_telemeter.synthesize("physical_link") + if "rssi" in physical_link: remote_telemeter.sensors["physical_link"].rssi = physical_link["rssi"] + if "snr" in physical_link: remote_telemeter.sensors["physical_link"].snr = physical_link["snr"] + if "q" in physical_link: remote_telemeter.sensors["physical_link"].q = physical_link["q"] + remote_telemeter.sensors["physical_link"].update_data() + RNS.log("PACKED: "+str(remote_telemeter.read_all()), RNS.LOG_WARNING) query = "INSERT INTO telemetry (dest_context, ts, data) values (?, ?, ?)" data = (context_dest, telemetry_timestamp, telemetry) @@ -1110,25 +1158,44 @@ class SidebandCore(): self.db = None def _db_update_appearance(self, context_dest, timestamp, appearance): - # TODO: Remove - # RNS.log("Updating appearance for "+RNS.prettyhexrep(context_dest), RNS.LOG_WARNING) - conv = self._db_conversation(context_dest) data_dict = conv["data"] if data_dict == None: data_dict = {} - data_dict["appearance"] = appearance - packed_dict = msgpack.packb(data_dict) + if data_dict["appearance"] != appearance: + data_dict["appearance"] = appearance + packed_dict = msgpack.packb(data_dict) - db = self.__db_connect() - dbc = db.cursor() + db = self.__db_connect() + dbc = db.cursor() - query = "UPDATE conv set data = ? where dest_context = ?" - data = (packed_dict, context_dest) - dbc.execute(query, data) - result = dbc.fetchall() - db.commit() + query = "UPDATE conv set data = ? where dest_context = ?" + data = (packed_dict, context_dest) + dbc.execute(query, data) + result = dbc.fetchall() + db.commit() + + def _db_get_appearance(self, context_dest): + conv = self._db_conversation(context_dest) + data_dict = conv["data"] + try: + if data_dict != None and "appearance" in data_dict: + def htf(cbytes): + d = 1.0/255.0 + r = round(struct.unpack("!B", bytes([cbytes[0]]))[0]*d, 4) + g = round(struct.unpack("!B", bytes([cbytes[1]]))[0]*d, 4) + b = round(struct.unpack("!B", bytes([cbytes[2]]))[0]*d, 4) + return [r,g,b] + + appearance = [data_dict["appearance"][0], htf(data_dict["appearance"][1]), htf(data_dict["appearance"][2])] + + return appearance + except Exception as e: + RNS.log("Could not retrieve appearance for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR) + + return None + def _db_conversation_set_telemetry(self, context_dest, send_telemetry=False): conv = self._db_conversation(context_dest) @@ -1437,6 +1504,12 @@ class SidebandCore(): if lxm.desired_method == LXMF.LXMessage.PAPER: lxm.paper_packed = paper_packed_lxm + + extras = None + try: + extras = msgpack.unpackb(entry[11]) + except: + pass message = { "hash": lxm.hash, @@ -1448,7 +1521,8 @@ class SidebandCore(): "sent": lxm.timestamp, "state": entry[6], "method": entry[7], - "lxm": lxm + "lxm": lxm, + "extras": extras, } messages.append(message) @@ -1470,7 +1544,14 @@ class SidebandCore(): else: packed_lxm = lxm.packed - query = "INSERT INTO lxm (lxm_hash, dest, source, title, tx_ts, rx_ts, state, method, t_encrypted, t_encryption, data) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + extras = {} + if lxm.rssi or lxm.snr or lxm.q: + extras["rssi"] = lxm.rssi + extras["snr"] = lxm.snr + extras["q"] = lxm.q + extras = msgpack.packb(extras) + + query = "INSERT INTO lxm (lxm_hash, dest, source, title, tx_ts, rx_ts, state, method, t_encrypted, t_encryption, data, extra) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" data = ( lxm.hash, lxm.destination_hash, @@ -1483,10 +1564,10 @@ class SidebandCore(): lxm.transport_encrypted, lxm.transport_encryption, packed_lxm, + extras ) dbc.execute(query, data) - db.commit() if not originator and lxm.fields != None: @@ -1494,7 +1575,12 @@ class SidebandCore(): self._db_update_appearance(context_dest, lxm.timestamp, lxm.fields[LXMF.FIELD_ICON_APPEARANCE]) if LXMF.FIELD_TELEMETRY in lxm.fields: - self._db_save_telemetry(context_dest, lxm.fields[LXMF.FIELD_TELEMETRY]) + physical_link = {} + if lxm.rssi or lxm.snr or lxm.q: + physical_link["rssi"] = lxm.rssi + physical_link["snr"] = lxm.snr + physical_link["q"] = lxm.q + self._db_save_telemetry(context_dest, lxm.fields[LXMF.FIELD_TELEMETRY], physical_link=physical_link) self.__event_conversation_changed(context_dest) @@ -2258,7 +2344,7 @@ class SidebandCore(): fields = {} if send_appearance: # TODO: REMOVE - # RNS.log("Sending appearance", RNS.LOG_WARNING) + RNS.log("Sending appearance", RNS.LOG_WARNING) def fth(c): r = c[0]; g = c[1]; b = c[2] r = min(max(0, r), 1); g = min(max(0, g), 1); b = min(max(0, b), 1) @@ -2273,7 +2359,7 @@ class SidebandCore(): if send_telemetry: # TODO: REMOVE - # RNS.log("Sending telemetry", RNS.LOG_WARNING) + RNS.log("Sending telemetry", RNS.LOG_WARNING) fields[LXMF.FIELD_TELEMETRY] = self.latest_packed_telemetry return fields @@ -2377,9 +2463,6 @@ class SidebandCore(): self.setstate("lxm_uri_ingest.result", response) def lxm_ingest(self, message, originator = False): - # TODO: Remove - RNS.log("MESSAGE FIELDS: "+str(message.fields), RNS.LOG_WARNING) - should_notify = False is_trusted = False unread_reason_tx = False @@ -2535,7 +2618,7 @@ class SidebandCore(): if message.unverified_reason == LXMF.LXMessage.SOURCE_UNKNOWN: signature_string = "Cannot verify, source is unknown" - RNS.log("LXMF delivery "+str(time_string)+". "+str(signature_string)+".") + RNS.log("LXMF delivery "+str(time_string)+". "+str(signature_string)+".", RNS.LOG_DEBUG) try: if self.config["lxmf_ignore_unknown"] == True: diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index f5cbab5..62d1e7a 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -115,6 +115,7 @@ MDNavigationLayout: [['menu', lambda x: nav_drawer.set_state("open")]] right_action_items: [ + ['map-search', lambda x: root.ids.screen_manager.app.peer_show_location_action(self)], ['lan-connect', lambda x: root.ids.screen_manager.app.message_propagation_action(self)], ['close', lambda x: root.ids.screen_manager.app.close_messages_action(self)], ] @@ -1009,7 +1010,7 @@ MDNavigationLayout: MDIconButton: pos_hint: {"center_x": .5} id: telemetry_icon_preview - icon: "alpha-p-circle-outline" + icon: "account" type: "large" theme_icon_color: "Custom" icon_color: [0, 0, 0, 1] @@ -1018,6 +1019,7 @@ MDNavigationLayout: size_hint_y: None # width: dp(64) height: dp(80) + on_release: root.ids.screen_manager.app.icons_action(self) MDRectangleFlatIconButton: @@ -1589,6 +1591,21 @@ MDNavigationLayout: pos_hint: {"center_y": 0.3} active: False + MDBoxLayout: + orientation: "horizontal" + size_hint_y: None + padding: [0,0,dp(24),dp(0)] + height: dp(48) + + MDLabel: + text: "Advanced Statistics" + font_style: "H6" + + MDSwitch: + id: settings_advanced_statistics + pos_hint: {"center_y": 0.3} + active: False + MDBoxLayout: orientation: "horizontal" size_hint_y: None