Skip to content

Commit 2f63837

Browse files
reorder wallet
1 parent 25f60d9 commit 2f63837

File tree

2 files changed

+99
-80
lines changed

2 files changed

+99
-80
lines changed

internal_filesystem/apps/com.lightningpiggy.displaywallet/assets/displaywallet.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
main_screen = None
1010
settings_screen = None
1111

12+
# widgets
1213
balance_label = None
1314

15+
wallet = None
16+
1417
# Settings screen implementation
1518
class SettingsScreen():
1619
def __init__(self):
@@ -181,9 +184,10 @@ def close_popup(self, event):
181184

182185

183186
def settings_button_tap(event):
184-
global settings_screen
187+
global settings_screen, wallet
185188
if not settings_screen:
186189
settings_screen = SettingsScreen().screen
190+
wallet.stop()
187191
mpos.ui.load_screen(settings_screen)
188192

189193
def build_main_ui():
@@ -217,34 +221,36 @@ def redraw_balance_cb(timer):
217221
balance_label.set_text(str(wallet.last_known_balance))
218222

219223
def janitor_cb(timer):
220-
global wallet
221-
if lv.screen_active() != main_screen and lv.screen_active() != settings_screen:
224+
global wallet, config
225+
if lv.screen_active() == main_screen and (not wallet or not wallet.is_running()):
226+
# just started the app or just returned from settings_screen
227+
config = mpos.config.SharedPreferences("com.lightningpiggy.displaywallet")
228+
wallet_type = config.get_string("wallet_type")
229+
if wallet_type == "lnbits":
230+
try:
231+
wallet = LNBitsWallet(config.get_string("lnbits_url"), config.get_string("lnbits_readkey"))
232+
except Exception as e:
233+
print(f"Couldn't initialize LNBitsWallet because: {e}")
234+
elif wallet_type == "nwc":
235+
try:
236+
wallet = NWCWallet(config.get_string("nwc_url"))
237+
except Exception as e:
238+
print(f"Couldn't initialize NWCWallet because: {e}")
239+
else:
240+
print(f"No or unsupported wallet type configured: '{wallet_type}'")
241+
if wallet:
242+
wallet.start(lambda : balance_label.set_text(str(wallet.last_known_balance)))
243+
else:
244+
print("ERROR: could not start any wallet!") # maybe call the error callback to show the error to the user
245+
elif lv.screen_active() != main_screen and lv.screen_active() != settings_screen:
222246
print("app backgrounded, cleaning up...")
223247
janitor.delete()
224-
wallet.destroy()
248+
wallet.stop()
225249
if settings_screen:
226250
settings_screen.delete()
227251
if main_screen:
228252
main_screen.delete()
229253

230254
build_main_ui()
231255

232-
config = mpos.config.SharedPreferences("com.lightningpiggy.displaywallet")
233-
234-
wallet_type = config.get_string("wallet_type")
235-
if wallet_type == "lnbits":
236-
try:
237-
wallet = LNBitsWallet(config.get_string("lnbits_url"), config.get_string("lnbits_readkey"))
238-
except Exception as e:
239-
print(f"Couldn't initialize LNBitsWallet because: {e}")
240-
elif wallet_type == "nwc":
241-
try:
242-
wallet = NWCWallet(config.get_string("nwc_url"))
243-
except Exception as e:
244-
print(f"Couldn't initialize NWCWallet because: {e}")
245-
else:
246-
print(f"No or unsupported wallet type configured: '{wallet_type}'")
247-
248-
wallet.start_refresh_balance(lambda : balance_label.set_text(str(wallet.last_known_balance)))
249-
250-
janitor = lv.timer_create(janitor_cb, 1000, None)
256+
janitor = lv.timer_create(janitor_cb, 500, None)

internal_filesystem/apps/com.lightningpiggy.displaywallet/assets/wallet.py

Lines changed: 70 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,30 @@ class Wallet:
2020
#last_known_balance_timestamp = 0
2121

2222
def __init__(self):
23-
pass
23+
self.keep_running = True
2424

2525
def __str__(self):
2626
if isinstance(self, LNBitsWallet):
2727
return "LNBitsWallet"
2828
elif isinstance(self, NWCWallet):
2929
return "NWCWallet"
3030

31-
def start_refresh_balance(self, balance_updated_cb):
31+
# Need callbacks for:
32+
# - started (so the user can show the UI)
33+
# - stopped (so the user can delete/free it)
34+
# - error (so the user can show the error)
35+
# - balance
36+
# - transactions
37+
def start(self, balance_updated_cb):
38+
self.keep_running = True
3239
_thread.stack_size(mpos.apps.good_stack_size())
33-
_thread.start_new_thread(self.fetch_balance_thread, (balance_updated_cb,))
40+
_thread.start_new_thread(self.manage_wallet_thread, (balance_updated_cb,))
3441

35-
def destroy(self):
36-
# optional to inherit
37-
pass
42+
def stop(self):
43+
self.keep_running = False
3844

45+
def is_running(self):
46+
return self.keep_running
3947

4048
class LNBitsWallet(Wallet):
4149

@@ -44,17 +52,28 @@ def __init__(self, lnbits_url, lnbits_readkey):
4452
self.lnbits_url = lnbits_url
4553
self.lnbits_readkey = lnbits_readkey
4654

47-
def fetch_balance_thread(self, balance_updated_cb):
48-
print("fetch_balance_thread")
55+
def manage_wallet_thread(self, balance_updated_cb):
56+
print("manage_wallet_thread")
57+
while self.keep_running:
58+
try:
59+
self.last_known_balance = fetch_balance()
60+
balance_updated_cb()
61+
# TODO: if the balance changed, then re-list transactions
62+
except Exception as e:
63+
print(f"WARNING: fetch_balance got exception {e}, ignorning.")
64+
print("Sleeping a while before re-fetching balance...")
65+
time.sleep(60)
66+
print("manage_wallet_thread stopping")
67+
68+
def fetch_balance():
4969
walleturl = self.lnbits_url + "/api/v1/wallet"
5070
headers = {
5171
"X-Api-Key": self.lnbits_readkey,
5272
}
5373
try:
5474
response = requests.get(walleturl, timeout=10, headers=headers)
5575
except Exception as e:
56-
print("GET request failed:", e)
57-
#lv.async_call(lambda l: please_wait_label.set_text(f"Error downloading app index: {e}"), None)
76+
print("fetch_balance: get request failed:", e)
5877
if response and response.status_code == 200:
5978
response_text = response.text
6079
print(f"Got response text: {response_text}")
@@ -63,42 +82,39 @@ def fetch_balance_thread(self, balance_updated_cb):
6382
balance_reply = json.loads(response_text)
6483
print(f"Got balance: {balance_reply['balance']}")
6584
balance_msat = balance_reply['balance']
66-
self.last_known_balance = round(balance_msat / 1000)
67-
#self.last_known_balance_timestamp = mpos.time.epoch_seconds()
68-
balance_updated_cb()
85+
return round(balance_msat / 1000)
6986
except Exception as e:
7087
print(f"Could not parse reponse text '{response_text}' as JSON: {e}")
88+
raise e
7189

7290

7391
class NWCWallet(Wallet):
7492

7593
def __init__(self, nwc_url):
7694
super().__init__()
7795
self.nwc_url = nwc_url
78-
self.relay, self.wallet_pubkey, self.secret, self.lud16 = self.parse_nwc_url(nwc_url)
96+
self.connected = False
97+
98+
def manage_wallet_thread(self, balance_updated_cb):
99+
self.relay, self.wallet_pubkey, self.secret, self.lud16 = self.parse_nwc_url(self.nwc_url)
79100
self.private_key = PrivateKey(bytes.fromhex(self.secret))
80101
self.relay_manager = RelayManager()
81102
self.relay_manager.add_relay(self.relay)
82-
self.connected = False
83-
84-
def destroy(self):
85-
self.relay_manager.close_connections()
86103

87-
def fetch_balance_thread(self, balance_updated_cb) :
88-
# make sure connected to relay (otherwise connect)
89-
if self.relay_manager.relays[self.relay].connected != True:
90-
print(f"DEBUG: Opening relay connections")
91-
self.relay_manager.open_connections({"cert_reqs": ssl.CERT_NONE})
92-
self.connected = False
93-
for _ in range(20):
94-
time.sleep(0.5)
95-
if self.relay_manager.relays[self.relay].connected is True:
96-
self.connected = True
97-
break
98-
print("Waiting for relay connection...")
104+
print(f"DEBUG: Opening relay connections")
105+
self.relay_manager.open_connections({"cert_reqs": ssl.CERT_NONE})
106+
self.connected = False
107+
for _ in range(20):
108+
time.sleep(0.5)
109+
if self.relay_manager.relays[self.relay].connected is True:
110+
self.connected = True
111+
break
112+
print("Waiting for relay connection...")
99113
if not self.connected:
100-
print("Failed to connect, aborting...")
114+
print("ERROR: could not connect to NWC relay {self.relay}, aborting...")
115+
# TODO: call an error callback to notify the user
101116
return
117+
102118
# Set up subscription to receive response
103119
self.subscription_id = "nwc_balance_" + str(round(time.time()))
104120
print(f"DEBUG: Setting up subscription with ID: {self.subscription_id}")
@@ -110,6 +126,7 @@ def fetch_balance_thread(self, balance_updated_cb) :
110126
print(f"DEBUG: Subscription filters: {self.filters.to_json_array()}")
111127
self.relay_manager.add_subscription(self.subscription_id, self.filters)
112128
time.sleep(1)
129+
113130
# Create get_balance request
114131
balance_request = {
115132
"method": "get_balance",
@@ -123,53 +140,48 @@ def fetch_balance_thread(self, balance_updated_cb) :
123140
)
124141
print(f"DEBUG: Signing DM {json.dumps(dm)} with private key")
125142
self.private_key.sign_event(dm) # sign also does encryption if it's a encrypted dm
126-
print(f"DEBUG: DM created with ID: {dm.id}")
127-
# Publish request
128143
print(f"DEBUG: Publishing subscription request")
129144
request_message = [ClientMessageType.REQUEST, self.subscription_id]
130145
request_message.extend(self.filters.to_json_array())
131146
self.relay_manager.publish_message(json.dumps(request_message))
132147
print(f"DEBUG: Publishing encrypted DM")
133148
self.relay_manager.publish_event(dm)
134-
# only accept events after the time it was published
135-
after_time = mpos.time.epoch_seconds()
136-
after_time -= 60 # go back a bit because server clocks might be drifting
137-
print(f"will only consider events after {after_time}")
138-
139-
# Wait for response
140-
print(f"DEBUG: Waiting for response...")
141-
print(f"starting at {time.localtime()}")
142-
start_time = mpos.time.epoch_seconds()
143-
balance = None
144-
while mpos.time.epoch_seconds() - start_time < 60 * 2:
145-
while self.relay_manager.message_pool.has_events():
149+
150+
print(f"DEBUG: Waiting for incoming NWC events...")
151+
while self.keep_running:
152+
if self.relay_manager.message_pool.has_events():
146153
print(f"DEBUG: Event received from message pool")
147154
event_msg = self.relay_manager.message_pool.get_event()
148155
event_created_at = event_msg.event.created_at
149156
print(f"Received at {time.localtime()} a message with timestamp {event_created_at}")
150-
#if event_created_at < after_time:
151-
# print("Skipping event because it's too old!")
152-
# continue
153-
#print(f"event_msg content {event_msg.event.content}")
154157
try:
155-
#print(f"DEBUG: Decrypting event from public_key: {event_msg.event.public_key}")
156158
decrypted_content = self.private_key.decrypt_message(
157159
event_msg.event.content,
158160
event_msg.event.public_key
159161
)
160162
print(f"DEBUG: Decrypted content: {decrypted_content}")
161163
response = json.loads(decrypted_content)
162164
print(f"DEBUG: Parsed response: {response}")
163-
self.last_known_balance = round(int(response["result"]["balance"]) / 1000)
164-
print(f"Got balance: {self.last_known_balance}")
165-
balance_updated_cb()
166-
break
165+
if response["result"]:
166+
if response["result"]["balance"]:
167+
self.last_known_balance = round(int(response["result"]["balance"]) / 1000)
168+
print(f"Got balance: {self.last_known_balance}")
169+
# TODO: if balance changed, then update list of transactions
170+
balance_updated_cb()
171+
elif response["result"]["transactions"]:
172+
print("TODO: Response contains transactions!")
173+
else:
174+
print("Unsupported response, ignoring.")
175+
else:
176+
print("Event doesn't contain result, ignoring.")
167177
except Exception as e:
168178
print(f"DEBUG: Error processing response: {e}")
169-
if balance is not None:
170-
break
171179
time.sleep(1)
172180

181+
print("NWCWallet: manage_wallet_thread stopping, closing connections...")
182+
self.relay_manager.close_connections()
183+
184+
173185
def parse_nwc_url(self, nwc_url):
174186
"""Parse Nostr Wallet Connect URL to extract pubkey, relay, secret, and lud16."""
175187
print(f"DEBUG: Starting to parse NWC URL: {nwc_url}")
@@ -185,6 +197,7 @@ def parse_nwc_url(self, nwc_url):
185197
print(f"DEBUG: No recognized prefix found in URL")
186198
raise ValueError("Invalid NWC URL: missing 'nostr+walletconnect://' or 'nwc:' prefix")
187199
print(f"DEBUG: URL after prefix removal: {nwc_url}")
200+
# TODO: urldecode because the relay might have %3A%2F%2F etc
188201
# Split into pubkey and query params
189202
parts = nwc_url.split('?')
190203
pubkey = parts[0]
@@ -201,7 +214,7 @@ def parse_nwc_url(self, nwc_url):
201214
params = parts[1].split('&')
202215
for param in params:
203216
if param.startswith('relay='):
204-
relay = param[6:] # TODO: urldecode because the relay might have %3A%2F%2F etc
217+
relay = param[6:]
205218
print(f"DEBUG: Extracted relay: {relay}")
206219
elif param.startswith('secret='):
207220
secret = param[7:]

0 commit comments

Comments
 (0)