diff --git a/lnbits/extensions/gerty/helpers.py b/lnbits/extensions/gerty/helpers.py
index ccb1bb55..4852fb58 100644
--- a/lnbits/extensions/gerty/helpers.py
+++ b/lnbits/extensions/gerty/helpers.py
@@ -239,3 +239,42 @@ def get_time_remaining(seconds, granularity=2):
name = name.rstrip("s")
result.append("{} {}".format(round(value), name))
return ", ".join(result[:granularity])
+
+
+async def get_mining_stat(stat_slug: str, gerty):
+ text = []
+ if stat_slug == "mining_current_hash_rate":
+ stat = await api_get_mining_stat(stat_slug, gerty)
+ logger.debug(stat)
+ current = "{0}hash".format(si_format(stat['current'], 6, True, " "))
+ text.append(get_text_item_dict("Current Mining Hashrate", 20))
+ text.append(get_text_item_dict(current, 40))
+ # compare vs previous time period
+ difference = get_percent_difference(current=stat['current'], previous=stat['1w'])
+ text.append(get_text_item_dict("{0} in last 7 days".format(difference), 12))
+ elif stat_slug == "mining_current_difficulty":
+ stat = await api_get_mining_stat(stat_slug, gerty)
+ text.append(get_text_item_dict("Current Mining Difficulty", 20))
+ text.append(get_text_item_dict(format_number(stat['current']), 40))
+ difference = get_percent_difference(current=stat['current'], previous=stat['previous'])
+ text.append(get_text_item_dict("{0} since last adjustment".format(difference), 12))
+ # text.append(get_text_item_dict("Required threshold for mining proof-of-work", 12))
+ return text
+
+async def api_get_mining_stat(stat_slug: str, gerty):
+ stat = ""
+ if stat_slug == "mining_current_hash_rate":
+ async with httpx.AsyncClient() as client:
+ r = await client.get(gerty.mempool_endpoint + "/api/v1/mining/hashrate/1m")
+ data = r.json()
+ stat = {}
+ stat['current'] = data['currentHashrate']
+ stat['1w'] = data['hashrates'][len(data['hashrates']) - 7]['avgHashrate']
+ elif stat_slug == "mining_current_difficulty":
+ async with httpx.AsyncClient() as client:
+ r = await client.get(gerty.mempool_endpoint + "/api/v1/mining/hashrate/1m")
+ data = r.json()
+ stat = {}
+ stat['current'] = data['currentDifficulty']
+ stat['previous'] = data['difficulty'][len(data['difficulty']) - 2]['difficulty']
+ return stat
\ No newline at end of file
diff --git a/lnbits/extensions/gerty/templates/gerty/index.html b/lnbits/extensions/gerty/templates/gerty/index.html
index 5d67f46e..55e67a2d 100644
--- a/lnbits/extensions/gerty/templates/gerty/index.html
+++ b/lnbits/extensions/gerty/templates/gerty/index.html
@@ -1,639 +1,781 @@
-{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
-%} {% block page %}
-
-
-
-
- New Gerty
-
-
-
-
-
-
-
-
Gerty
-
-
- Export to CSV
-
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %}
+
+
+
+
+ New Gerty
+
+
+
+
+
+
+
+
Gerty
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+
+
+
+ Launch software Gerty
+
+
+ View Gerty API
+
+
+
+ {{ (col.name == 'tip_options' && col.value ?
+ JSON.parse(col.value).join(", ") : col.value) }}
+
+
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
-
- {% raw %}
-
-
-
-
- {{ col.label }}
-
-
-
-
-
-
-
-
-
- Launch software Gerty
-
-
- View Gerty API
-
-
-
- {{ (col.name == 'tip_options' && col.value ?
- JSON.parse(col.value).join(", ") : col.value) }}
-
-
-
-
-
-
-
-
-
- {% endraw %}
-
-
-
-
-
-
-
-
-
- {{ SITE_TITLE }} Gerty extension
-
-
-
-
- {% include "gerty/_api_docs.html" %}
-
-
-
-
-
-
-
-
-
-
- Hit enter to add values
-
-
-
- Used for getting onchain/ln stats
-
-
-
- The amount of time in seconds between screen updates
-
-
-
- Enter a UTC time offset value (e.g. -1)
-
-
- Use the toggles below to control what your Gerty will display
-
-
-
-
- Displays random quotes from Satoshi
-
-
-
-
-
-
-
-
-
-
-
-
-
Create Gerty
-
-
Update Gerty
-
-
Cancel
-
+
+
+
+
+ {{ SITE_TITLE }} Gerty extension
+
+
+
+
+ {% include "gerty/_api_docs.html" %}
+
+
-
-
-
-
+
+
+
+
+
+
+
+ Hit enter to add values
+
+
+
+ Used for getting onchain/ln stats
+
+
+
+ The amount of time in seconds between screen updates
+
+
+
+
+ Enter a UTC time offset value (e.g. -1)
+
+
+ Use the toggles below to control what your Gerty will display
+
+
+
+
+
+ Displays random quotes from Satoshi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Toggle all
+
+
+
+
+
+
+
+
+
+
+
+
+ Toggle all
+
+
+
+
+
+
+
+
+
+
+ Toggle all
+
+
+
+
+
+
+
+
+
+
+
+ Create Gerty
+
+ Update Gerty
+
+ Cancel
+
+
+
+
+
+
{% endblock %} {% block scripts %} {{ window_vars(user) }}
-
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this Gerty?')
+ .onOk(function () {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/gerty/api/v1/gerty/' + gertyId,
+ _.findWhere(self.g.user.wallets, {id: gerty.wallet}).adminkey
+ )
+ .then(function (response) {
+ self.gertys = _.reject(self.gertys, function (obj) {
+ return obj.id == gertyId
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ })
+ },
+ exportCSV: function () {
+ LNbits.utils.exportCSV(this.gertysTable.columns, this.gertys)
+ }
+ },
+ created: function () {
+ if (this.g.user.wallets.length) {
+ this.getGertys()
+ }
+ },
+ watch: {
+ toggleStates: {
+ handler(toggleStatesValue) {
+ // Switch all the toggles in each section to the relevant state
+ for (const [toggleKey, toggleValue] of Object.entries(toggleStatesValue)) {
+ if (this.oldToggleStates[toggleKey] !== toggleValue) {
+ for (const [dpKey, dpValue] of Object.entries(this.formDialog.data.display_preferences)) {
+ if (dpKey.indexOf(toggleKey) === 0) {
+ this.formDialog.data.display_preferences[dpKey] = toggleValue
+ }
+ }
+ }
+ }
+ // This is a weird hack we have to use to get VueJS to persist the previous toggle state between
+ // watches. VueJS passes the old and new values by reference so when comparing objects they
+ // will have the same values unless we do this
+ this.oldToggleStates = JSON.parse(JSON.stringify(toggleStatesValue))
+ },
+ deep: true
+ }
+ }
+ })
+
{% endblock %} {% block styles %}
-
+
{% endblock %}
diff --git a/lnbits/extensions/gerty/views_api.py b/lnbits/extensions/gerty/views_api.py
index 84a9b967..b400f3ec 100644
--- a/lnbits/extensions/gerty/views_api.py
+++ b/lnbits/extensions/gerty/views_api.py
@@ -173,16 +173,37 @@ async def get_screen_data(screen_num: int, screens_list: dict, gerty):
if screen_slug == "dashboard":
title = gerty.name
areas = await get_dashboard(gerty)
+ if screen_slug == "lnbits_wallets_balance":
+ wallets = await get_lnbits_wallet_balances(gerty)
+ text = []
+ for wallet in wallets:
+ text.append(get_text_item_dict("{0}'s Wallet".format(wallet['name']), 20))
+ text.append(get_text_item_dict("{0} sats".format(format_number(wallet['balance'])), 40))
+ areas.append(text)
elif screen_slug == "fun_satoshi_quotes":
areas.append(await get_satoshi_quotes())
elif screen_slug == "fun_exchange_market_rate":
areas.append(await get_exchange_rate(gerty))
- elif screen_slug == "onchain_dashboard":
+ elif screen_slug == "onchain_difficulty_epoch_progress":
+ areas.append(await get_onchain_stat(screen_slug, gerty))
+ elif screen_slug == "onchain_difficulty_retarget_date":
+ areas.append(await get_onchain_stat(screen_slug, gerty))
+ elif screen_slug == "onchain_difficulty_blocks_remaining":
+ areas.append(await get_onchain_stat(screen_slug, gerty))
+ elif screen_slug == "onchain_difficulty_epoch_time_remaining":
+ areas.append(await get_onchain_stat(screen_slug, gerty))
+ elif screen_slug == "dashboard_onchain":
title = "Onchain Data"
areas = await get_onchain_dashboard(gerty)
elif screen_slug == "mempool_recommended_fees":
areas.append(await get_mempool_stat(screen_slug, gerty))
- elif screen_slug == "mining_dashboard":
+ elif screen_slug == "mempool_tx_count":
+ areas.append(await get_mempool_stat(screen_slug, gerty))
+ elif screen_slug == "mining_current_hash_rate":
+ areas.append(await get_mining_stat(screen_slug, gerty))
+ elif screen_slug == "mining_current_difficulty":
+ areas.append(await get_mining_stat(screen_slug, gerty))
+ elif screen_slug == "dashboard_mining":
title = "Mining Data"
areas = await get_mining_dashboard(gerty)
elif screen_slug == "lightning_dashboard":
@@ -292,6 +313,34 @@ async def get_exchange_rate(gerty):
pass
return text
+async def get_onchain_stat(stat_slug: str, gerty):
+ text = []
+ if (
+ stat_slug == "onchain_difficulty_epoch_progress" or
+ stat_slug == "onchain_difficulty_retarget_date" or
+ stat_slug == "onchain_difficulty_blocks_remaining" or
+ stat_slug == "onchain_difficulty_epoch_time_remaining"
+ ):
+ async with httpx.AsyncClient() as client:
+ r = await client.get(gerty.mempool_endpoint + "/api/v1/difficulty-adjustment")
+ if stat_slug == "onchain_difficulty_epoch_progress":
+ stat = round(r.json()['progressPercent'])
+ text.append(get_text_item_dict("Progress through current difficulty epoch", 15))
+ text.append(get_text_item_dict("{0}%".format(stat), 80))
+ elif stat_slug == "onchain_difficulty_retarget_date":
+ stat = r.json()['estimatedRetargetDate']
+ dt = datetime.fromtimestamp(stat / 1000).strftime("%e %b %Y at %H:%M")
+ text.append(get_text_item_dict("Estimated date of next difficulty adjustment", 15))
+ text.append(get_text_item_dict(dt, 40))
+ elif stat_slug == "onchain_difficulty_blocks_remaining":
+ stat = r.json()['remainingBlocks']
+ text.append(get_text_item_dict("Blocks remaining until next difficulty adjustment", 15))
+ text.append(get_text_item_dict("{0}".format(format_number(stat)), 80))
+ elif stat_slug == "onchain_difficulty_epoch_time_remaining":
+ stat = r.json()['remainingTime']
+ text.append(get_text_item_dict("Blocks remaining until next difficulty adjustment", 15))
+ text.append(get_text_item_dict(get_time_remaining(stat / 1000, 4), 20))
+ return text
async def get_onchain_dashboard(gerty):
areas = []
@@ -440,3 +489,30 @@ async def get_mempool_stat(stat_slug: str, gerty):
)
)
return text
+
+
+def get_date_suffix(dayNumber):
+ if 4 <= dayNumber <= 20 or 24 <= dayNumber <= 30:
+ return "th"
+ else:
+ return ["st", "nd", "rd"][dayNumber % 10 - 1]
+
+def get_time_remaining(seconds, granularity=2):
+ intervals = (
+ # ('weeks', 604800), # 60 * 60 * 24 * 7
+ ('days', 86400), # 60 * 60 * 24
+ ('hours', 3600), # 60 * 60
+ ('minutes', 60),
+ ('seconds', 1),
+ )
+
+ result = []
+
+ for name, count in intervals:
+ value = seconds // count
+ if value:
+ seconds -= value * count
+ if value == 1:
+ name = name.rstrip('s')
+ result.append("{} {}".format(round(value), name))
+ return ', '.join(result[:granularity])