178 lines
4.2 KiB
Go
178 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"image/color"
|
|
"log"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.dvdt.dev/david/bitcoin-ticker-pi/display"
|
|
"git.dvdt.dev/david/bitcoin-ticker-pi/fonts"
|
|
rgbmatrix "git.dvdt.dev/david/bitcoin-ticker-pi/go-rpi-rgb-led-matrix"
|
|
"git.dvdt.dev/david/bitcoin-ticker-pi/mempool"
|
|
)
|
|
|
|
const (
|
|
RefreshRateHz = 120
|
|
MempoolURL = "mempool.space"
|
|
)
|
|
|
|
func main() {
|
|
cfg := rgbmatrix.DefaultConfig
|
|
cfg.Rows = 32
|
|
cfg.Cols = 64
|
|
|
|
m, _ := rgbmatrix.NewRGBLedMatrix(&cfg)
|
|
c := rgbmatrix.NewCanvas(m)
|
|
|
|
d := display.New(c,
|
|
display.NewSlot(display.Left, 9, 5, 4, 0, 0),
|
|
display.NewSlot(display.Right, 5, 5, 4, 44, 0),
|
|
display.NewSlot(display.Center, 8, 13, 8, 0, 9),
|
|
display.NewSlot(display.Center, 16, 5, 4, 0, 27),
|
|
)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
ticker := time.NewTicker(time.Second / RefreshRateHz)
|
|
tdone := make(chan struct{})
|
|
mdone := make(chan struct{})
|
|
msgs := make(chan mempool.Message)
|
|
errs := make(chan error)
|
|
|
|
mp := mempool.New(MempoolURL, &http.Client{
|
|
Timeout: 5 * time.Second,
|
|
})
|
|
err := mp.Init()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
currentBlockHeight, err := mp.CurrentBlockHeight()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
highfee, medfee, lowfee, err := mp.CurrentFees()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
currentPrice, err := mp.CurrentPrice()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var blockHeight int
|
|
blockHeightMutex := &sync.Mutex{}
|
|
var fees mempool.Fees
|
|
feesMutex := &sync.Mutex{}
|
|
var price int
|
|
priceMutex := &sync.Mutex{}
|
|
|
|
blockHeight = currentBlockHeight
|
|
fees.FastestFee = highfee
|
|
fees.HalfHourFee = medfee
|
|
fees.HourFee = lowfee
|
|
price = currentPrice
|
|
|
|
go mp.Listen(ctx, msgs, errs, mdone)
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case m := <-msgs:
|
|
if m.Fees != nil {
|
|
feesMutex.Lock()
|
|
fees = *m.Fees
|
|
feesMutex.Unlock()
|
|
}
|
|
if m.Block != nil {
|
|
blockHeightMutex.Lock()
|
|
blockHeight = int(m.Block.Height)
|
|
blockHeightMutex.Unlock()
|
|
}
|
|
if m.Conversions != nil {
|
|
priceMutex.Lock()
|
|
price = m.Conversions.USD
|
|
priceMutex.Unlock()
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-tdone:
|
|
return
|
|
case <-ticker.C:
|
|
d.Clear()
|
|
err = updateSlots(d, price, blockHeight, fees.HourFee, fees.HalfHourFee, fees.FastestFee)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateSlots(d *display.Display, price, height, lfee, mfee, hfee int) error {
|
|
d.Clear()
|
|
|
|
var priceSegments []display.Segment
|
|
priceSegments = append(priceSegments, display.Segment{Text: "dollar", Color: fonts.Green})
|
|
priceSegments = append(priceSegments, strToDisplaySegments(fmt.Sprintf("%d", price), fonts.White)...)
|
|
|
|
err := d.UpdateTopLeft(priceSegments)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var moscowTimeSegments []display.Segment
|
|
moscowTime := 0
|
|
if price > 0 {
|
|
moscowTime = 100000000 / price
|
|
}
|
|
moscowTimeSegments = append(moscowTimeSegments, display.Segment{Text: "satoshi", Color: fonts.Orange})
|
|
moscowTimeSegments = append(moscowTimeSegments, strToDisplaySegments(fmt.Sprintf("%d", moscowTime), fonts.White)...)
|
|
|
|
err = d.UpdateTopRight(moscowTimeSegments)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var heightSegments []display.Segment
|
|
heightSegments = append(heightSegments, display.Segment{Text: "height", Color: fonts.Blue})
|
|
heightSegments = append(heightSegments, strToDisplaySegments(fmt.Sprintf("%d", height), fonts.White)...)
|
|
|
|
err = d.UpdateMain(heightSegments)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var feeSegments []display.Segment
|
|
feeSegments = append(feeSegments, display.Segment{Text: "low", Color: fonts.Green})
|
|
feeSegments = append(feeSegments, strToDisplaySegments(fmt.Sprintf("%d", lfee), fonts.White)...)
|
|
feeSegments = append(feeSegments, display.Segment{Text: "medium", Color: fonts.Orange})
|
|
feeSegments = append(feeSegments, strToDisplaySegments(fmt.Sprintf("%d", mfee), fonts.White)...)
|
|
feeSegments = append(feeSegments, display.Segment{Text: "high", Color: fonts.Red})
|
|
feeSegments = append(feeSegments, strToDisplaySegments(fmt.Sprintf("%d", hfee), fonts.White)...)
|
|
|
|
err = d.UpdateBottom(feeSegments)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return d.Render()
|
|
}
|
|
|
|
func strToDisplaySegments(s string, c color.RGBA) []display.Segment {
|
|
var segs []display.Segment
|
|
for _, r := range s {
|
|
segs = append(segs, display.Segment{Text: string(r), Color: c})
|
|
}
|
|
return segs
|
|
}
|