Added telemetry system, location sharing and map view for shared locations

This commit is contained in:
Mark Qvist 2023-10-22 01:12:55 +02:00
parent a9160b558b
commit 314499109c
4 changed files with 322 additions and 148 deletions

View File

@ -44,8 +44,9 @@ from kivy.uix.screenmanager import FadeTransition, NoTransition
from kivymd.uix.list import OneLineIconListItem from kivymd.uix.list import OneLineIconListItem
from kivy.properties import StringProperty from kivy.properties import StringProperty
from kivymd.uix.pickers import MDColorPicker from kivymd.uix.pickers import MDColorPicker
from kivymd.uix.button import BaseButton, MDIconButton
from sideband.sense import Telemeter from sideband.sense import Telemeter
from mapview import MapMarker from mapview import CustomMapMarker
if RNS.vendor.platformutils.get_platform() == "android": if RNS.vendor.platformutils.get_platform() == "android":
from sideband.core import SidebandCore from sideband.core import SidebandCore
@ -922,6 +923,11 @@ class SidebandApp(MDApp):
self.root.ids.message_send_button.disabled = False self.root.ids.message_send_button.disabled = False
Clock.schedule_once(cb, 0.5) 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): def message_propagation_action(self, sender):
if self.outbound_mode_paper: if self.outbound_mode_paper:
self.outbound_mode_paper = False self.outbound_mode_paper = False
@ -1289,6 +1295,10 @@ class SidebandApp(MDApp):
self.sideband.save_configuration() self.sideband.save_configuration()
self.update_ui_theme() 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): def save_notifications_on(sender=None, event=None):
self.sideband.config["notifications_on"] = self.root.ids.settings_notifications_on.active self.sideband.config["notifications_on"] = self.root.ids.settings_notifications_on.active
self.sideband.save_configuration() 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.active = self.sideband.config["eink_mode"]
self.root.ids.settings_eink_mode.bind(active=save_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.active = self.sideband.config["start_announce"]
self.root.ids.settings_start_announce.bind(active=save_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.config["telemetry_icon"] = self.root.ids.telemetry_icon_preview.icon
self.sideband.save_configuration() self.sideband.save_configuration()
self.own_appearance_changed = True
def telemetry_enabled_toggle(self, sender=None, event=None): def telemetry_enabled_toggle(self, sender=None, event=None):
@ -2909,6 +2923,7 @@ class SidebandApp(MDApp):
def telemetry_location_toggle(self, sender=None, event=None): def telemetry_location_toggle(self, sender=None, event=None):
if self.root.ids.telemetry_s_location.active: if self.root.ids.telemetry_s_location.active:
if RNS.vendor.platformutils.is_android():
if not check_permission("android.permission.ACCESS_COARSE_LOCATION") or not check_permission("android.permission.ACCESS_FINE_LOCATION"): 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) RNS.log("Requesting location permission", RNS.LOG_DEBUG)
request_permissions(["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"]) request_permissions(["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"])
@ -2984,6 +2999,7 @@ class SidebandApp(MDApp):
self.root.ids.telemetry_icon_preview.icon_color = color self.root.ids.telemetry_icon_preview.icon_color = color
self.sideband.config["telemetry_fg"] = color self.sideband.config["telemetry_fg"] = color
self.sideband.save_configuration() self.sideband.save_configuration()
self.own_appearance_changed = True
if hasattr(self, "color_picker") and self.color_picker != None: if hasattr(self, "color_picker") and self.color_picker != None:
self.color_picker.dismiss() self.color_picker.dismiss()
self.color_picker = None self.color_picker = None
@ -3000,6 +3016,8 @@ class SidebandApp(MDApp):
color = selected_color[:-1] + [1] color = selected_color[:-1] + [1]
self.root.ids.telemetry_icon_preview.md_bg_color = color self.root.ids.telemetry_icon_preview.md_bg_color = color
self.sideband.config["telemetry_bg"] = 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: if hasattr(self, "color_picker") and self.color_picker != None:
self.color_picker.dismiss() self.color_picker.dismiss()
self.color_picker = None self.color_picker = None
@ -3057,7 +3075,7 @@ class SidebandApp(MDApp):
from mapview import MapView 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 = 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.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.map = mapview
self.root.ids.map_layout.add_widget(self.root.ids.map_layout.map) self.root.ids.map_layout.add_widget(self.root.ids.map_layout.map)
@ -3073,11 +3091,71 @@ class SidebandApp(MDApp):
self.map_update_markers() self.map_update_markers()
Clock.schedule_once(am_job, 0.6) 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): 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"] earliest = time.time() - self.sideband.config["map_history_limit"]
telemetry_entries = self.sideband.list_telemetry(after=earliest) telemetry_entries = self.sideband.list_telemetry(after=earliest)
own_address = self.sideband.lxmf_destination.hash own_address = self.sideband.lxmf_destination.hash
@ -3086,45 +3164,44 @@ class SidebandApp(MDApp):
# Add own marker if available # Add own marker if available
retain_own = False retain_own = False
own_telemetry = self.sideband.get_telemetry() 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: own_appearance = [
self.sideband.config["telemetry_icon"],
self.sideband.config["telemetry_fg"],
self.sideband.config["telemetry_bg"]
]
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 retain_own = True
o = own_telemetry["location"]
if not own_address in self.map_markers: if not own_address in self.map_markers:
# TODO: Remove
RNS.log("Adding own marker", RNS.LOG_WARNING) RNS.log("Adding own marker", RNS.LOG_WARNING)
marker = MapMarker(lat=o["latitude"], lon=o["longtitude"]) marker = self.map_create_marker(own_address, own_telemetry, own_appearance)
marker.source_dest = own_address if marker != None:
marker.latest_timestamp = o["last_update"]
self.map_markers[own_address] = marker self.map_markers[own_address] = marker
self.root.ids.map_layout.map.add_widget(marker) self.root.ids.map_layout.map.add_marker(marker)
changes = True changes = True
else: else:
marker = self.map_markers[own_address] marker = self.map_markers[own_address]
if o["last_update"] > marker.latest_timestamp: o = own_telemetry["location"]
# TODO: Remove if o["last_update"] > marker.location_time or (hasattr(self, "own_appearance_changed") and self.own_appearance_changed):
RNS.log("Updating own marker", RNS.LOG_WARNING) marker.location_time = o["last_update"]
marker.latest_timestamp = o["last_update"]
marker.lat = o["latitude"] marker.lat = o["latitude"]
marker.lon = o["longtitude"] 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 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)
stale_markers = [] stale_markers = []
for marker in self.map_markers: for marker in self.map_markers:
if not marker in telemetry_entries: if not marker in telemetry_entries:
if marker == own_address: if marker == own_address:
if not retain_own: if not retain_own:
# TODO: Remove
RNS.log("Setting own marker for removal: "+str(marker), RNS.LOG_WARNING)
stale_markers.append(marker) stale_markers.append(marker)
else: else:
# TODO: Remove
RNS.log("Setting marker for removal: "+str(marker), RNS.LOG_WARNING)
stale_markers.append(marker) stale_markers.append(marker)
for marker in stale_markers: for marker in stale_markers:
@ -3134,20 +3211,18 @@ class SidebandApp(MDApp):
except Exception as e: except Exception as e:
RNS.log("Error while removing map marker: "+str(e), RNS.LOG_ERROR) 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: for telemetry_source in telemetry_entries:
try:
skip = False 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: if telemetry_source == own_address:
# TODO: Remove
RNS.log("Skipping own telemetry", RNS.LOG_WARNING)
skip = True skip = True
elif telemetry_source in self.map_markers: elif telemetry_source in self.map_markers:
marker = self.map_markers[telemetry_source] marker = self.map_markers[telemetry_source]
newest_timestamp = telemetry_entries[telemetry_source][0][0] newest_timestamp = telemetry_entries[telemetry_source][0][0]
if newest_timestamp <= marker.latest_timestamp: if newest_timestamp <= marker.location_time:
skip = True skip = True
latest_viewable = None latest_viewable = None
@ -3158,8 +3233,6 @@ class SidebandApp(MDApp):
t = Telemeter.from_packed(telemetry_data) t = Telemeter.from_packed(telemetry_data)
if t != None: if t != None:
telemetry = t.read_all() 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: if "location" in telemetry and telemetry["location"]["latitude"] != None and telemetry["location"]["longtitude"] != None:
latest_viewable = telemetry latest_viewable = telemetry
break break
@ -3167,32 +3240,30 @@ class SidebandApp(MDApp):
if latest_viewable != None: if latest_viewable != None:
l = latest_viewable["location"] l = latest_viewable["location"]
if not telemetry_source in self.map_markers: if not telemetry_source in self.map_markers:
marker = MapMarker(lat=l["latitude"], lon=l["longtitude"]) marker = self.map_create_marker(telemetry_source, latest_viewable, self.sideband.peer_appearance(telemetry_source))
marker.source_dest = telemetry_source if marker != None:
marker.latest_timestamp = latest_viewable["time"]["utc"]
self.map_markers[telemetry_source] = marker self.map_markers[telemetry_source] = marker
self.root.ids.map_layout.map.add_widget(marker) self.root.ids.map_layout.map.add_marker(marker)
changes = True changes = True
else: else:
marker = self.map_markers[telemetry_source] marker = self.map_markers[telemetry_source]
marker.latest_timestamp = latest_viewable["time"]["utc"] marker.location_time = latest_viewable["time"]["utc"]
marker.lat = l["latitude"] marker.lat = l["latitude"]
marker.lon = l["longtitude"] 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 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() self.last_map_update = time.time()
if changes: if changes:
mv = self.root.ids.map_layout.map mv = self.root.ids.map_layout.map
mv.trigger_update(True) 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 ### Guide screen
###################################### ######################################
def close_guide_action(self, sender=None): def close_guide_action(self, sender=None):
@ -3307,6 +3378,9 @@ Thank you very much for using Free Communications Systems.
class CustomOneLineIconListItem(OneLineIconListItem): class CustomOneLineIconListItem(OneLineIconListItem):
icon = StringProperty() icon = StringProperty()
class MDMapIconButton(MDIconButton):
pass
def run(): def run():
SidebandApp().run() SidebandApp().run()

View File

@ -327,7 +327,7 @@ class MarkerMapLayer(MapLayer):
marker.y = int(y - marker.height * marker.anchor_y) marker.y = int(y - marker.height * marker.anchor_y)
if hasattr(marker, "children"): if hasattr(marker, "children"):
if marker.children != None and len(marker.children) > 0: if marker.children != None and len(marker.children) > 0:
c = marker.children[0] for c in marker.children:
c.x = marker.x c.x = marker.x
c.y = marker.y+dp(16) c.y = marker.y+dp(16)

View File

@ -73,14 +73,14 @@ class SidebandCore():
SERVICE_JOB_INTERVAL = 1 SERVICE_JOB_INTERVAL = 1
PERIODIC_JOBS_INTERVAL = 60 PERIODIC_JOBS_INTERVAL = 60
PERIODIC_SYNC_RETRY = 360 PERIODIC_SYNC_RETRY = 360
# TODO: Reset TELEMETRY_INTERVAL = 60
# TELEMETRY_INTERVAL = 60
TELEMETRY_INTERVAL = 10
IF_CHANGE_ANNOUNCE_MIN_INTERVAL = 6 # In seconds IF_CHANGE_ANNOUNCE_MIN_INTERVAL = 6 # In seconds
AUTO_ANNOUNCE_RANDOM_MIN = 90 # In minutes AUTO_ANNOUNCE_RANDOM_MIN = 90 # In minutes
AUTO_ANNOUNCE_RANDOM_MAX = 480 # 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" aspect_filter = "lxmf.delivery"
def received_announce(self, destination_hash, announced_identity, app_data): def received_announce(self, destination_hash, announced_identity, app_data):
# Add the announce to the directory announce # Add the announce to the directory announce
@ -305,7 +305,7 @@ class SidebandCore():
# Telemetry # Telemetry
self.config["telemetry_enabled"] = False 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_trusted"] = False
self.config["telemetry_send_to_collector"] = False self.config["telemetry_send_to_collector"] = False
@ -315,6 +315,7 @@ class SidebandCore():
self._db_initstate() self._db_initstate()
self._db_initpersistent() self._db_initpersistent()
self._db_inittelemetry() self._db_inittelemetry()
self._db_upgradetables()
self.__save_config() self.__save_config()
@ -338,6 +339,8 @@ class SidebandCore():
self.config["debug"] = False self.config["debug"] = False
if not "dark_ui" in self.config: if not "dark_ui" in self.config:
self.config["dark_ui"] = True 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: if not "lxmf_periodic_sync" in self.config:
self.config["lxmf_periodic_sync"] = False self.config["lxmf_periodic_sync"] = False
if not "lxmf_ignore_unknown" in self.config: if not "lxmf_ignore_unknown" in self.config:
@ -454,11 +457,11 @@ class SidebandCore():
self.config["telemetry_send_to_collector"] = False self.config["telemetry_send_to_collector"] = False
if not "telemetry_icon" in self.config: 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: 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: 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: if not "telemetry_send_appearance" in self.config:
self.config["telemetry_send_appearance"] = False self.config["telemetry_send_appearance"] = False
@ -503,6 +506,7 @@ class SidebandCore():
self._db_initstate() self._db_initstate()
self._db_initpersistent() self._db_initpersistent()
self._db_inittelemetry() self._db_inittelemetry()
self._db_upgradetables()
self.__db_indices() self.__db_indices()
def __reload_config(self): def __reload_config(self):
@ -648,6 +652,9 @@ class SidebandCore():
RNS.log("Error while getting peer name: "+str(e), RNS.LOG_ERROR) RNS.log("Error while getting peer name: "+str(e), RNS.LOG_ERROR)
return "" 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): def peer_display_name(self, context_dest):
try: try:
existing_conv = self._db_conversation(context_dest) 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): 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 [] 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): def list_messages(self, context_dest, after = None, before = None, limit = None):
result = self._db_messages(context_dest, after, before, limit) result = self._db_messages(context_dest, after, before, limit)
if result != None: if result != None:
@ -829,7 +861,7 @@ class SidebandCore():
dbc = db.cursor() dbc = db.cursor()
dbc.execute("DROP TABLE IF EXISTS lxm") 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("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)") 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("DROP TABLE IF EXISTS announce")
dbc.execute("CREATE TABLE announce (id PRIMARY KEY, received INTEGER, source BLOB, data BLOB, dest_type BLOB)") 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("DROP TABLE IF EXISTS state")
dbc.execute("CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB)") 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)") dbc.execute("CREATE UNIQUE INDEX IF NOT EXISTS idx_conv_dest_context ON conv(dest_context)")
db.commit() 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): def _db_initstate(self):
db = self.__db_connect() db = self.__db_connect()
dbc = db.cursor() dbc = db.cursor()
@ -930,13 +982,6 @@ class SidebandCore():
dbc.execute("CREATE TABLE IF NOT EXISTS persistent (property BLOB PRIMARY KEY, value BLOB)") dbc.execute("CREATE TABLE IF NOT EXISTS persistent (property BLOB PRIMARY KEY, value BLOB)")
db.commit() 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): def _db_getpersistent(self, prop):
try: try:
db = self.__db_connect() db = self.__db_connect()
@ -1079,10 +1124,7 @@ class SidebandCore():
return results return results
def _db_save_telemetry(self, context_dest, telemetry): def _db_save_telemetry(self, context_dest, telemetry, physical_link = None):
# TODO: Remove
# RNS.log("Saving telemetry for "+RNS.prettyhexrep(context_dest), RNS.LOG_WARNING)
try: try:
remote_telemeter = Telemeter.from_packed(telemetry) remote_telemeter = Telemeter.from_packed(telemetry)
telemetry_timestamp = remote_telemeter.read_all()["time"]["utc"] telemetry_timestamp = remote_telemeter.read_all()["time"]["utc"]
@ -1095,10 +1137,16 @@ class SidebandCore():
result = dbc.fetchall() result = dbc.fetchall()
if len(result) != 0: if len(result) != 0:
# TODO: Remove
# RNS.log("Telemetry entry already exists, ignoring", RNS.LOG_WARNING)
return 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 (?, ?, ?)" query = "INSERT INTO telemetry (dest_context, ts, data) values (?, ?, ?)"
data = (context_dest, telemetry_timestamp, telemetry) data = (context_dest, telemetry_timestamp, telemetry)
dbc.execute(query, data) dbc.execute(query, data)
@ -1110,14 +1158,12 @@ class SidebandCore():
self.db = None self.db = None
def _db_update_appearance(self, context_dest, timestamp, appearance): 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) conv = self._db_conversation(context_dest)
data_dict = conv["data"] data_dict = conv["data"]
if data_dict == None: if data_dict == None:
data_dict = {} data_dict = {}
if data_dict["appearance"] != appearance:
data_dict["appearance"] = appearance data_dict["appearance"] = appearance
packed_dict = msgpack.packb(data_dict) packed_dict = msgpack.packb(data_dict)
@ -1130,6 +1176,27 @@ class SidebandCore():
result = dbc.fetchall() result = dbc.fetchall()
db.commit() 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): def _db_conversation_set_telemetry(self, context_dest, send_telemetry=False):
conv = self._db_conversation(context_dest) conv = self._db_conversation(context_dest)
data_dict = conv["data"] data_dict = conv["data"]
@ -1438,6 +1505,12 @@ class SidebandCore():
if lxm.desired_method == LXMF.LXMessage.PAPER: if lxm.desired_method == LXMF.LXMessage.PAPER:
lxm.paper_packed = paper_packed_lxm lxm.paper_packed = paper_packed_lxm
extras = None
try:
extras = msgpack.unpackb(entry[11])
except:
pass
message = { message = {
"hash": lxm.hash, "hash": lxm.hash,
"dest": lxm.destination_hash, "dest": lxm.destination_hash,
@ -1448,7 +1521,8 @@ class SidebandCore():
"sent": lxm.timestamp, "sent": lxm.timestamp,
"state": entry[6], "state": entry[6],
"method": entry[7], "method": entry[7],
"lxm": lxm "lxm": lxm,
"extras": extras,
} }
messages.append(message) messages.append(message)
@ -1470,7 +1544,14 @@ class SidebandCore():
else: else:
packed_lxm = lxm.packed 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 = ( data = (
lxm.hash, lxm.hash,
lxm.destination_hash, lxm.destination_hash,
@ -1483,10 +1564,10 @@ class SidebandCore():
lxm.transport_encrypted, lxm.transport_encrypted,
lxm.transport_encryption, lxm.transport_encryption,
packed_lxm, packed_lxm,
extras
) )
dbc.execute(query, data) dbc.execute(query, data)
db.commit() db.commit()
if not originator and lxm.fields != None: 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]) self._db_update_appearance(context_dest, lxm.timestamp, lxm.fields[LXMF.FIELD_ICON_APPEARANCE])
if LXMF.FIELD_TELEMETRY in lxm.fields: 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) self.__event_conversation_changed(context_dest)
@ -2258,7 +2344,7 @@ class SidebandCore():
fields = {} fields = {}
if send_appearance: if send_appearance:
# TODO: REMOVE # TODO: REMOVE
# RNS.log("Sending appearance", RNS.LOG_WARNING) RNS.log("Sending appearance", RNS.LOG_WARNING)
def fth(c): def fth(c):
r = c[0]; g = c[1]; b = c[2] 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) 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: if send_telemetry:
# TODO: REMOVE # TODO: REMOVE
# RNS.log("Sending telemetry", RNS.LOG_WARNING) RNS.log("Sending telemetry", RNS.LOG_WARNING)
fields[LXMF.FIELD_TELEMETRY] = self.latest_packed_telemetry fields[LXMF.FIELD_TELEMETRY] = self.latest_packed_telemetry
return fields return fields
@ -2377,9 +2463,6 @@ class SidebandCore():
self.setstate("lxm_uri_ingest.result", response) self.setstate("lxm_uri_ingest.result", response)
def lxm_ingest(self, message, originator = False): def lxm_ingest(self, message, originator = False):
# TODO: Remove
RNS.log("MESSAGE FIELDS: "+str(message.fields), RNS.LOG_WARNING)
should_notify = False should_notify = False
is_trusted = False is_trusted = False
unread_reason_tx = False unread_reason_tx = False
@ -2535,7 +2618,7 @@ class SidebandCore():
if message.unverified_reason == LXMF.LXMessage.SOURCE_UNKNOWN: if message.unverified_reason == LXMF.LXMessage.SOURCE_UNKNOWN:
signature_string = "Cannot verify, source is 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: try:
if self.config["lxmf_ignore_unknown"] == True: if self.config["lxmf_ignore_unknown"] == True:

View File

@ -115,6 +115,7 @@ MDNavigationLayout:
[['menu', lambda x: nav_drawer.set_state("open")]] [['menu', lambda x: nav_drawer.set_state("open")]]
right_action_items: 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)], ['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)], ['close', lambda x: root.ids.screen_manager.app.close_messages_action(self)],
] ]
@ -1009,7 +1010,7 @@ MDNavigationLayout:
MDIconButton: MDIconButton:
pos_hint: {"center_x": .5} pos_hint: {"center_x": .5}
id: telemetry_icon_preview id: telemetry_icon_preview
icon: "alpha-p-circle-outline" icon: "account"
type: "large" type: "large"
theme_icon_color: "Custom" theme_icon_color: "Custom"
icon_color: [0, 0, 0, 1] icon_color: [0, 0, 0, 1]
@ -1018,6 +1019,7 @@ MDNavigationLayout:
size_hint_y: None size_hint_y: None
# width: dp(64) # width: dp(64)
height: dp(80) height: dp(80)
on_release: root.ids.screen_manager.app.icons_action(self)
MDRectangleFlatIconButton: MDRectangleFlatIconButton:
@ -1589,6 +1591,21 @@ MDNavigationLayout:
pos_hint: {"center_y": 0.3} pos_hint: {"center_y": 0.3}
active: False 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: MDBoxLayout:
orientation: "horizontal" orientation: "horizontal"
size_hint_y: None size_hint_y: None