Compare commits
80 Commits
Author | SHA1 | Date | |
---|---|---|---|
d80230b9d4 | |||
c81ac19dda | |||
4a397d4c1e | |||
c5fb3e1a33 | |||
908bd7d5f3 | |||
c97fcaeefb | |||
992eb2e085 | |||
f33b3d2b56 | |||
03f3968fe1 | |||
dea92c6404 | |||
006f245c10 | |||
d232340edd | |||
c6458720e9 | |||
1e072a3540 | |||
f639fef992 | |||
2d0db1dcf1 | |||
4efa4380c4 | |||
fca64afbf3 | |||
cf24c5ace8 | |||
a25b2e3e62 | |||
2d0b64d92f | |||
5efafe9be7 | |||
271510d528 | |||
4d72a063b2 | |||
643245f16c | |||
6f87980d4b | |||
851f1975d6 | |||
5e66fe82ac | |||
645541e079 | |||
1012be6e5b | |||
5973290d6c | |||
19bacf29b2 | |||
a78650e526 | |||
71e9caf0bc | |||
1f5a6365bc | |||
958f2af516 | |||
60f1eedc9a | |||
c05147518d | |||
422f844943 | |||
66618e5bf0 | |||
0c2e57ced0 | |||
6d9f6fc6e6 | |||
0cbd6a48ae | |||
b614138f6b | |||
3a0491f069 | |||
093a5632c7 | |||
91662e6f38 | |||
931966bf1e | |||
ed01700e26 | |||
e9269e8eb8 | |||
52b85ab361 | |||
bc45943bdc | |||
6933f45683 | |||
0f22d67395 | |||
6da03181a9 | |||
44a25625da | |||
14a38351e4 | |||
4c27f424b2 | |||
86fbef2e8a | |||
7b8658e072 | |||
73c46cfa66 | |||
1e0f1c5b76 | |||
78b5ca1de8 | |||
b0c4574481 | |||
01975f207c | |||
428e7967c1 | |||
56dbf0540e | |||
240e7a5ee4 | |||
625805fe96 | |||
4b6f7d408e | |||
9034ef7c6b | |||
9939f724c4 | |||
8dce33f7b1 | |||
563009c44d | |||
d4a8a9f8c9 | |||
7fd9af3288 | |||
4508559bfd | |||
0cdf8a4bed | |||
2af6c1887f | |||
3a3f95acdf |
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
/itd
|
/itd
|
||||||
/itgui
|
/itgui
|
||||||
/version.txt
|
/version.txt
|
||||||
|
dist/
|
||||||
|
109
.goreleaser.yaml
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- go generate
|
||||||
|
- go mod tidy
|
||||||
|
builds:
|
||||||
|
- id: itd
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
binary: itd
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
goarch:
|
||||||
|
- 386
|
||||||
|
- amd64
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
- id: itctl
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
main: ./cmd/itctl
|
||||||
|
binary: itctl
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
goarch:
|
||||||
|
- 386
|
||||||
|
- amd64
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
archives:
|
||||||
|
- replacements:
|
||||||
|
386: i386
|
||||||
|
amd64: x86_64
|
||||||
|
arm64: aarch64
|
||||||
|
files:
|
||||||
|
- LICENSE
|
||||||
|
- README.md
|
||||||
|
- itd.toml
|
||||||
|
- itd.service
|
||||||
|
nfpms:
|
||||||
|
- id: itd
|
||||||
|
file_name_template: '{{.PackageName}}-{{.Version}}-{{.Os}}-{{.Arch}}'
|
||||||
|
description: "Companion daemon for the InfiniTime firmware on the PineTime smartwatch"
|
||||||
|
replacements:
|
||||||
|
386: i386
|
||||||
|
amd64: x86_64
|
||||||
|
arm64: aarch64
|
||||||
|
homepage: 'https://gitea.arsenm.dev/Arsen6331/itd'
|
||||||
|
maintainer: 'Arsen Musyaelyan <arsen@arsenm.dev>'
|
||||||
|
license: GPLv3
|
||||||
|
formats:
|
||||||
|
- apk
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
dependencies:
|
||||||
|
- dbus
|
||||||
|
- bluez
|
||||||
|
- pulseaudio-utils
|
||||||
|
contents:
|
||||||
|
- src: itd.toml
|
||||||
|
dst: /etc/itd.toml
|
||||||
|
type: "config|noreplace"
|
||||||
|
- src: itd.service
|
||||||
|
dst: /usr/lib/systemd/user/itd.service
|
||||||
|
aurs:
|
||||||
|
- name: itd-bin
|
||||||
|
homepage: 'https://gitea.arsenm.dev/Arsen6331/itd'
|
||||||
|
description: "Companion daemon for the InfiniTime firmware on the PineTime smartwatch"
|
||||||
|
maintainers:
|
||||||
|
- 'Arsen Musyaelyan <arsen@arsenm.dev>'
|
||||||
|
license: GPLv3
|
||||||
|
private_key: '{{ .Env.AUR_KEY }}'
|
||||||
|
git_url: 'ssh://aur@aur.archlinux.org/itd-bin.git'
|
||||||
|
provides:
|
||||||
|
- itd
|
||||||
|
- itctl
|
||||||
|
conflicts:
|
||||||
|
- itd
|
||||||
|
- itctl
|
||||||
|
depends:
|
||||||
|
- dbus
|
||||||
|
- bluez
|
||||||
|
- libpulse
|
||||||
|
package: |-
|
||||||
|
# binaries
|
||||||
|
install -Dm755 "./itd" "${pkgdir}/usr/bin/itd"
|
||||||
|
install -Dm755 "./itctl" "${pkgdir}/usr/bin/itctl"
|
||||||
|
|
||||||
|
# service
|
||||||
|
install -Dm644 "./itd.service" ${pkgdir}/usr/lib/systemd/user/itd.service
|
||||||
|
|
||||||
|
# config
|
||||||
|
install -Dm644 "./itd.toml" ${pkgdir}/etc/itd.toml
|
||||||
|
|
||||||
|
# license
|
||||||
|
install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/itd/LICENSE"
|
||||||
|
release:
|
||||||
|
gitea:
|
||||||
|
owner: Arsen6331
|
||||||
|
name: itd
|
||||||
|
gitea_urls:
|
||||||
|
api: 'https://gitea.arsenm.dev/api/v1/'
|
||||||
|
download: 'https://gitea.arsenm.dev'
|
||||||
|
skip_tls_verify: false
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ incpatch .Version }}-next"
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
8
.woodpecker.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
pipeline:
|
||||||
|
release:
|
||||||
|
image: goreleaser/goreleaser
|
||||||
|
commands:
|
||||||
|
- goreleaser release
|
||||||
|
secrets: [ gitea_token, aur_key ]
|
||||||
|
when:
|
||||||
|
event: tag
|
12
Makefile
@ -3,14 +3,14 @@ BIN_PREFIX = $(DESTDIR)$(PREFIX)/bin
|
|||||||
SERVICE_PREFIX = $(DESTDIR)$(PREFIX)/lib/systemd/user
|
SERVICE_PREFIX = $(DESTDIR)$(PREFIX)/lib/systemd/user
|
||||||
CFG_PREFIX = $(DESTDIR)/etc
|
CFG_PREFIX = $(DESTDIR)/etc
|
||||||
|
|
||||||
all: version
|
all: version.txt
|
||||||
go build $(GOFLAGS)
|
go build
|
||||||
go build ./cmd/itctl $(GOFLAGS)
|
go build ./cmd/itctl
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f itctl
|
rm -f itctl
|
||||||
rm -f itd
|
rm -f itd
|
||||||
printf "unknown" > version.txt
|
rm -f version.txt
|
||||||
|
|
||||||
install:
|
install:
|
||||||
install -Dm755 ./itd $(BIN_PREFIX)/itd
|
install -Dm755 ./itd $(BIN_PREFIX)/itd
|
||||||
@ -24,7 +24,7 @@ uninstall:
|
|||||||
rm $(SERVICE_PREFIX)/itd.service
|
rm $(SERVICE_PREFIX)/itd.service
|
||||||
rm $(CFG_PREFIX)/itd.toml
|
rm $(CFG_PREFIX)/itd.toml
|
||||||
|
|
||||||
version:
|
version.txt:
|
||||||
printf "r%s.%s" "$(shell git rev-list --count HEAD)" "$(shell git rev-parse --short HEAD)" > version.txt
|
printf "r%s.%s" "$(shell git rev-list --count HEAD)" "$(shell git rev-parse --short HEAD)" > version.txt
|
||||||
|
|
||||||
.PHONY: all clean install uninstall version
|
.PHONY: all clean install uninstall
|
125
README.md
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
`itd` is a daemon that uses my infinitime [library](https://go.arsenm.dev/infinitime) to interact with the [PineTime](https://www.pine64.org/pinetime/) running [InfiniTime](https://infinitime.io).
|
`itd` is a daemon that uses my infinitime [library](https://go.arsenm.dev/infinitime) to interact with the [PineTime](https://www.pine64.org/pinetime/) running [InfiniTime](https://infinitime.io).
|
||||||
|
|
||||||
[![Build status](https://ci.appveyor.com/api/projects/status/xgj5sobw76ndqaod?svg=true)](https://ci.appveyor.com/project/moussaelianarsen/itd)
|
[![status-badge](https://ci.arsenm.dev/api/badges/Arsen6331/itd/status.svg)](https://ci.arsenm.dev/Arsen6331/itd)
|
||||||
[![Binary downloads](https://img.shields.io/badge/download-binary-orange)](https://minio.arsenm.dev/minio/itd/)
|
[![itd-git AUR package](https://img.shields.io/aur/version/itd-git?label=itd-git&logo=archlinux)](https://aur.archlinux.org/packages/itd-git/)
|
||||||
[![AUR package](https://img.shields.io/aur/version/itd-git?label=itd-git&logo=archlinux)](https://aur.archlinux.org/packages/itd-git/)
|
[![itd-bin AUR package](https://img.shields.io/aur/version/itd-bin?label=itd-bin&logo=archlinux)](https://aur.archlinux.org/packages/itd-bin/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -19,6 +19,39 @@
|
|||||||
- Set current time
|
- Set current time
|
||||||
- Control socket
|
- Control socket
|
||||||
- Firmware upgrades
|
- Firmware upgrades
|
||||||
|
- Weather
|
||||||
|
- BLE Filesystem
|
||||||
|
- Navigation (PureMaps)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Since ITD 0.0.7, packages are built and uploaded whenever a new release is created.
|
||||||
|
|
||||||
|
#### Arch Linux
|
||||||
|
|
||||||
|
Use the `itd-bin` or `itd-git` AUR packages.
|
||||||
|
|
||||||
|
#### Debian/Ubuntu
|
||||||
|
|
||||||
|
- Go to the [latest release](https://gitea.arsenm.dev/Arsen6331/itd/releases/latest) and download the `.deb` package for your CPU architecture. You can find your architecture by running `uname -m` in the terminal.
|
||||||
|
- Run `sudo apt install <package>`, replacing `<package>` with the path to the downloaded file. Note: relative paths must begin with `./`.
|
||||||
|
- Example: `sudo apt install ~/Downloads/itd-0.0.7-linux-aarch64.deb`
|
||||||
|
|
||||||
|
#### Fedora
|
||||||
|
|
||||||
|
- Go to the [latest release](https://gitea.arsenm.dev/Arsen6331/itd/releases/latest) and download the `.rpm` package for your CPU architecture. You can find your architecture by running `uname -m` in the terminal.
|
||||||
|
- Run `sudo dnf install <package>`, replacing `<package>` with the path to the downloaded file.
|
||||||
|
- Example: `sudo dnf install ~/Downloads/itd-0.0.7-linux-aarch64.rpm`
|
||||||
|
|
||||||
|
#### Alpine (and postmarketOS)
|
||||||
|
|
||||||
|
- Go to the [latest release](https://gitea.arsenm.dev/Arsen6331/itd/releases/latest) and download the `.apk` package for your CPU architecture. You can find your architecture by running `uname -m` in the terminal.
|
||||||
|
- Run `sudo apk add --allow-untrusted <package>`, replacing `<package>` with the path to the downloaded file.
|
||||||
|
- Example: `sudo apk add --allow-untrusted ~/Downloads/itd-0.0.7-linux-aarch64.apk`
|
||||||
|
|
||||||
|
Note: `--allow-untrusted` is required because ITD isn't part of a repository, and therefore is not signed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -26,15 +59,13 @@
|
|||||||
|
|
||||||
This daemon creates a UNIX socket at `/tmp/itd/socket`. It allows you to directly control the daemon and, by extension, the connected watch.
|
This daemon creates a UNIX socket at `/tmp/itd/socket`. It allows you to directly control the daemon and, by extension, the connected watch.
|
||||||
|
|
||||||
The socket accepts JSON requests. For example, sending a notification looks like this:
|
The socket uses my [lrpc](https://gitea.arsenm.dev/Arsen6331/lrpc) library for requests. This library accepts requests in msgpack, with the following format:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"type": 5, "data": {"title": "title1", "body": "body1"}}
|
{"Receiver": "ITD", "Method": "Notify", "Arg": {"title": "title1", "body": "body1"}, "ID": "some-id-here"}
|
||||||
```
|
```
|
||||||
|
|
||||||
It will return a JSON response. A response can have 3 fields: `error`, `msg`, and `value`. Error is a boolean that signals whether an error was returned. If error is true, the msg field will contain the error. Value can contain any data and depends on what the request was.
|
It will return a msgpack response, the format of which can be found [here](https://gitea.arsenm.dev/Arsen6331/lrpc/src/branch/master/internal/types/types.go#L30). The response will have the same ID as was sent in the request in order to allow the client to keep track of which request the response belongs to.
|
||||||
|
|
||||||
The various request types and their data requirements can be seen in `internal/types`. I can make separate docs for it if I get enough requests.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -55,7 +86,7 @@ Since the PineTime does not have enough space to store all unicode glyphs, it on
|
|||||||
- Lithuanian
|
- Lithuanian
|
||||||
- Estonian
|
- Estonian
|
||||||
- Icelandic
|
- Icelandic
|
||||||
- Czeck
|
- Czech
|
||||||
- French
|
- French
|
||||||
- Armenian
|
- Armenian
|
||||||
- Korean
|
- Korean
|
||||||
@ -81,72 +112,74 @@ This daemon comes with a binary called `itctl` which uses the socket to control
|
|||||||
|
|
||||||
This is the `itctl` usage screen:
|
This is the `itctl` usage screen:
|
||||||
```
|
```
|
||||||
Control the itd daemon for InfiniTime smartwatches
|
NAME:
|
||||||
|
itctl - A new cli application
|
||||||
|
|
||||||
Usage:
|
USAGE:
|
||||||
itctl [flags]
|
itctl [global options] command [command options] [arguments...]
|
||||||
itctl [command]
|
|
||||||
|
|
||||||
Available Commands:
|
COMMANDS:
|
||||||
firmware Manage InfiniTime firmware
|
help Display help screen for a command
|
||||||
get Get information from InfiniTime
|
resources, res Handle InfiniTime resource loading
|
||||||
help Help about any command
|
filesystem, fs Perform filesystem operations on the PineTime
|
||||||
notify Send notification to InfiniTime
|
firmware, fw Manage InfiniTime firmware
|
||||||
set Set information on InfiniTime
|
get Get information from InfiniTime
|
||||||
|
notify Send notification to InfiniTime
|
||||||
|
set Set information on InfiniTime
|
||||||
|
update, upd Update information on InfiniTime
|
||||||
|
watch Watch a value for changes
|
||||||
|
|
||||||
Flags:
|
GLOBAL OPTIONS:
|
||||||
-h, --help help for itctl
|
--socket-path value, -s value Path to itd socket (default: "/tmp/itd/socket")
|
||||||
-s, --socket-path string Path to itd socket
|
|
||||||
|
|
||||||
Use "itctl [command] --help" for more information about a command.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `itgui`
|
### `itgui`
|
||||||
|
|
||||||
In `cmd/itgui`, there is a gui frontend to the socket of `itd`. It uses the [fyne library](https://fyne.io/) for Go. It can be compiled by running:
|
In `cmd/itgui`, there is a gui frontend to the socket of `itd`. It uses the [Fyne library](https://fyne.io/) for Go.
|
||||||
|
|
||||||
|
#### Compilation
|
||||||
|
|
||||||
|
Before compiling, certain prerequisites must be installed. These are listed on the following page: https://developer.fyne.io/started/#prerequisites
|
||||||
|
|
||||||
|
It can be compiled by running:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
go build ./cmd/itgui
|
go build ./cmd/itgui
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Cross-compilation
|
||||||
|
|
||||||
|
Due to the use of OpenGL, cross-compilation of `itgui` isn't as simple as that of `itd` and `itctl`. The following guide from the Fyne website should work for `itgui`: https://developer.fyne.io/started/cross-compiling.
|
||||||
|
|
||||||
#### Screenshots
|
#### Screenshots
|
||||||
|
|
||||||
![Info tab](https://i.imgur.com/okxG9EI.png)
|
![Info tab](cmd/itgui/screenshots/info.png)
|
||||||
|
|
||||||
![Notify tab](https://i.imgur.com/DrVhOAq.png)
|
![Motion tab](cmd/itgui/screenshots/motion.png)
|
||||||
|
|
||||||
![Set time tab](https://i.imgur.com/j9civeY.png)
|
![Notify tab](cmd/itgui/screenshots/notify.png)
|
||||||
|
|
||||||
![Upgrade tab](https://i.imgur.com/1KY6fG4.png)
|
![FS tab](cmd/itgui/screenshots/fs.png)
|
||||||
|
|
||||||
![Upgrade in progress](https://i.imgur.com/w5qbWAw.png)
|
![FS mkdir](cmd/itgui/screenshots/mkdir.png)
|
||||||
|
|
||||||
---
|
![FS resource upload](cmd/itgui/screenshots/resources.png)
|
||||||
|
|
||||||
#### Interactive mode
|
![Time tab](cmd/itgui/screenshots/time.png)
|
||||||
|
|
||||||
Running `itctl` by itself will open interactive mode. It's essentially a shell where you can enter commands. For example:
|
![Firmware tab](cmd/itgui/screenshots/firmware.png)
|
||||||
|
|
||||||
```
|
![Upgrade in progress](cmd/itgui/screenshots/progress.png)
|
||||||
$ itctl
|
|
||||||
itctl> fw ver
|
![Metrics tab](cmd/itgui/screenshots/metrics.png)
|
||||||
1.3.0
|
|
||||||
itctl> get batt
|
|
||||||
81%
|
|
||||||
itctl> get heart
|
|
||||||
92 BPM
|
|
||||||
itctl> set time 2021-08-22T00:06:18-07:00
|
|
||||||
itctl> set time now
|
|
||||||
itctl> exit
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
To install, install the go compiler and make. Usually, go is provided by a package either named `go` or `golang`, and make is usually provided by `make`. The go compiler must be version 1.16 or newer for the `io/fs` module.
|
To install, install the go compiler and make. Usually, go is provided by a package either named `go` or `golang`, and make is usually provided by `make`. The go compiler must be version 1.17 or newer for various new `reflect` features.
|
||||||
|
|
||||||
To install, run
|
To install, run
|
||||||
```shell
|
```shell
|
||||||
|
37
api/api.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"go.arsenm.dev/lrpc/client"
|
||||||
|
"go.arsenm.dev/lrpc/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultAddr = "/tmp/itd/socket"
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(sockPath string) (*Client, error) {
|
||||||
|
conn, err := net.Dial("unix", sockPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &Client{
|
||||||
|
client: client.New(conn, codec.Default),
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFromConn(conn io.ReadWriteCloser) *Client {
|
||||||
|
return &Client{
|
||||||
|
client: client.New(conn, codec.Default),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
return c.client.Close()
|
||||||
|
}
|
153
api/client.go
@ -1,153 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"go.arsenm.dev/infinitime"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default socket address
|
|
||||||
const DefaultAddr = "/tmp/itd/socket"
|
|
||||||
|
|
||||||
// Client is the socket API client
|
|
||||||
type Client struct {
|
|
||||||
conn net.Conn
|
|
||||||
respCh chan types.Response
|
|
||||||
heartRateCh chan types.Response
|
|
||||||
battLevelCh chan types.Response
|
|
||||||
stepCountCh chan types.Response
|
|
||||||
motionCh chan types.Response
|
|
||||||
dfuProgressCh chan types.Response
|
|
||||||
readProgressCh chan types.FSTransferProgress
|
|
||||||
writeProgressCh chan types.FSTransferProgress
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new client and sets it up
|
|
||||||
func New(addr string) (*Client, error) {
|
|
||||||
conn, err := net.Dial("unix", addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
out := &Client{
|
|
||||||
conn: conn,
|
|
||||||
respCh: make(chan types.Response, 5),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
scanner := bufio.NewScanner(conn)
|
|
||||||
for scanner.Scan() {
|
|
||||||
var res types.Response
|
|
||||||
err = json.Unmarshal(scanner.Bytes(), &res)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out.handleResp(res)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Close() error {
|
|
||||||
err := c.conn.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
close(c.respCh)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// request sends a request to itd and waits for and returns the response
|
|
||||||
func (c *Client) request(req types.Request) (types.Response, error) {
|
|
||||||
// Encode request into connection
|
|
||||||
err := json.NewEncoder(c.conn).Encode(req)
|
|
||||||
if err != nil {
|
|
||||||
return types.Response{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res := <-c.respCh
|
|
||||||
|
|
||||||
if res.Error {
|
|
||||||
return res, errors.New(res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// requestNoRes sends a request to itd and does not wait for the response
|
|
||||||
func (c *Client) requestNoRes(req types.Request) error {
|
|
||||||
// Encode request into connection
|
|
||||||
err := json.NewEncoder(c.conn).Encode(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleResp handles the received response as needed
|
|
||||||
func (c *Client) handleResp(res types.Response) error {
|
|
||||||
switch res.Type {
|
|
||||||
case types.ReqTypeWatchHeartRate:
|
|
||||||
c.heartRateCh <- res
|
|
||||||
case types.ReqTypeWatchBattLevel:
|
|
||||||
c.battLevelCh <- res
|
|
||||||
case types.ReqTypeWatchStepCount:
|
|
||||||
c.stepCountCh <- res
|
|
||||||
case types.ReqTypeWatchMotion:
|
|
||||||
c.motionCh <- res
|
|
||||||
case types.ReqTypeFwUpgrade:
|
|
||||||
c.dfuProgressCh <- res
|
|
||||||
case types.ReqTypeFS:
|
|
||||||
if res.Value == nil {
|
|
||||||
c.respCh <- res
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var progress types.FSTransferProgress
|
|
||||||
if err := mapstructure.Decode(res.Value, &progress); err != nil {
|
|
||||||
c.respCh <- res
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch progress.Type {
|
|
||||||
case types.FSTypeRead:
|
|
||||||
c.readProgressCh <- progress
|
|
||||||
case types.FSTypeWrite:
|
|
||||||
c.writeProgressCh <- progress
|
|
||||||
default:
|
|
||||||
c.respCh <- res
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
c.respCh <- res
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeUint8(val interface{}) uint8 {
|
|
||||||
return uint8(val.(float64))
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeUint32(val interface{}) uint32 {
|
|
||||||
return uint32(val.(float64))
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeMotion(val interface{}) (infinitime.MotionValues, error) {
|
|
||||||
out := infinitime.MotionValues{}
|
|
||||||
err := mapstructure.Decode(val, &out)
|
|
||||||
if err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeDFUProgress(val interface{}) (DFUProgress, error) {
|
|
||||||
out := DFUProgress{}
|
|
||||||
err := mapstructure.Decode(val, &out)
|
|
||||||
if err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
26
api/firmware.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) FirmwareUpgrade(ctx context.Context, upgType UpgradeType, files ...string) (chan infinitime.DFUProgress, error) {
|
||||||
|
progressCh := make(chan infinitime.DFUProgress, 5)
|
||||||
|
err := c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"FirmwareUpgrade",
|
||||||
|
FwUpgradeData{
|
||||||
|
Type: upgType,
|
||||||
|
Files: files,
|
||||||
|
},
|
||||||
|
progressCh,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return progressCh, nil
|
||||||
|
}
|
150
api/fs.go
@ -1,102 +1,96 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import "context"
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) Rename(old, new string) error {
|
func (c *Client) RemoveAll(ctx context.Context, paths ...string) error {
|
||||||
_, err := c.request(types.Request{
|
return c.client.Call(
|
||||||
Type: types.ReqTypeFS,
|
ctx,
|
||||||
Data: types.ReqDataFS{
|
"FS",
|
||||||
Type: types.FSTypeMove,
|
"RemoveAll",
|
||||||
Files: []string{old, new},
|
paths,
|
||||||
},
|
nil,
|
||||||
})
|
)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Remove(paths ...string) error {
|
func (c *Client) Remove(ctx context.Context, paths ...string) error {
|
||||||
_, err := c.request(types.Request{
|
return c.client.Call(
|
||||||
Type: types.ReqTypeFS,
|
ctx,
|
||||||
Data: types.ReqDataFS{
|
"FS",
|
||||||
Type: types.FSTypeDelete,
|
"Remove",
|
||||||
Files: paths,
|
paths,
|
||||||
},
|
nil,
|
||||||
})
|
)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Mkdir(paths ...string) error {
|
func (c *Client) Rename(ctx context.Context, old, new string) error {
|
||||||
_, err := c.request(types.Request{
|
return c.client.Call(
|
||||||
Type: types.ReqTypeFS,
|
ctx,
|
||||||
Data: types.ReqDataFS{
|
"FS",
|
||||||
Type: types.FSTypeMkdir,
|
"Rename",
|
||||||
Files: paths,
|
[2]string{old, new},
|
||||||
},
|
nil,
|
||||||
})
|
)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ReadDir(path string) ([]types.FileInfo, error) {
|
func (c *Client) MkdirAll(ctx context.Context, paths ...string) error {
|
||||||
res, err := c.request(types.Request{
|
return c.client.Call(
|
||||||
Type: types.ReqTypeFS,
|
ctx,
|
||||||
Data: types.ReqDataFS{
|
"FS",
|
||||||
Type: types.FSTypeList,
|
"MkdirAll",
|
||||||
Files: []string{path},
|
paths,
|
||||||
},
|
nil,
|
||||||
})
|
)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var out []types.FileInfo
|
|
||||||
err = mapstructure.Decode(res.Value, &out)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ReadFile(localPath, remotePath string) (<-chan types.FSTransferProgress, error) {
|
func (c *Client) Mkdir(ctx context.Context, paths ...string) error {
|
||||||
c.readProgressCh = make(chan types.FSTransferProgress, 5)
|
return c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"FS",
|
||||||
|
"Mkdir",
|
||||||
|
paths,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
_, err := c.request(types.Request{
|
func (c *Client) ReadDir(ctx context.Context, dir string) (out []FileInfo, err error) {
|
||||||
Type: types.ReqTypeFS,
|
err = c.client.Call(
|
||||||
Data: types.ReqDataFS{
|
ctx,
|
||||||
Type: types.FSTypeRead,
|
"FS",
|
||||||
Files: []string{localPath, remotePath},
|
"ReadDir",
|
||||||
},
|
dir,
|
||||||
})
|
&out,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Upload(ctx context.Context, dst, src string) (chan FSTransferProgress, error) {
|
||||||
|
progressCh := make(chan FSTransferProgress, 5)
|
||||||
|
err := c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"FS",
|
||||||
|
"Upload",
|
||||||
|
[2]string{dst, src},
|
||||||
|
progressCh,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.readProgressCh, nil
|
return progressCh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) WriteFile(localPath, remotePath string) (<-chan types.FSTransferProgress, error) {
|
func (c *Client) Download(ctx context.Context, dst, src string) (chan FSTransferProgress, error) {
|
||||||
c.writeProgressCh = make(chan types.FSTransferProgress, 5)
|
progressCh := make(chan FSTransferProgress, 5)
|
||||||
|
err := c.client.Call(
|
||||||
_, err := c.request(types.Request{
|
ctx,
|
||||||
Type: types.ReqTypeFS,
|
"FS",
|
||||||
Data: types.ReqDataFS{
|
"Download",
|
||||||
Type: types.FSTypeWrite,
|
[2]string{dst, src},
|
||||||
Files: []string{remotePath, localPath},
|
progressCh,
|
||||||
},
|
)
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.writeProgressCh, nil
|
return progressCh, nil
|
||||||
}
|
}
|
||||||
|
73
api/get.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) HeartRate(ctx context.Context) (out uint8, err error) {
|
||||||
|
err = c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"HeartRate",
|
||||||
|
nil,
|
||||||
|
&out,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) BatteryLevel(ctx context.Context) (out uint8, err error) {
|
||||||
|
err = c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"BatteryLevel",
|
||||||
|
nil,
|
||||||
|
&out,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Motion(ctx context.Context) (out infinitime.MotionValues, err error) {
|
||||||
|
err = c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"Motion",
|
||||||
|
nil,
|
||||||
|
&out,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) StepCount(ctx context.Context) (out uint32, err error) {
|
||||||
|
err = c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"StepCount",
|
||||||
|
nil,
|
||||||
|
&out,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Version(ctx context.Context) (out string, err error) {
|
||||||
|
err = c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"Version",
|
||||||
|
nil,
|
||||||
|
&out,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Address(ctx context.Context) (out string, err error) {
|
||||||
|
err = c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"Address",
|
||||||
|
nil,
|
||||||
|
&out,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
209
api/info.go
@ -1,209 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"go.arsenm.dev/infinitime"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Address gets the bluetooth address of the connected device
|
|
||||||
func (c *Client) Address() (string, error) {
|
|
||||||
res, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeBtAddress,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Value.(string), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version gets the firmware version of the connected device
|
|
||||||
func (c *Client) Version() (string, error) {
|
|
||||||
res, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeFwVersion,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Value.(string), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatteryLevel gets the battery level of the connected device
|
|
||||||
func (c *Client) BatteryLevel() (uint8, error) {
|
|
||||||
res, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeBattLevel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint8(res.Value.(float64)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchBatteryLevel returns a channel which will contain
|
|
||||||
// new battery level values as they update. Do not use after
|
|
||||||
// calling cancellation function
|
|
||||||
func (c *Client) WatchBatteryLevel() (<-chan uint8, func(), error) {
|
|
||||||
c.battLevelCh = make(chan types.Response, 2)
|
|
||||||
err := c.requestNoRes(types.Request{
|
|
||||||
Type: types.ReqTypeWatchBattLevel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
res := <-c.battLevelCh
|
|
||||||
done, cancel := c.cancelFn(res.ID, c.battLevelCh)
|
|
||||||
out := make(chan uint8, 2)
|
|
||||||
go func() {
|
|
||||||
for res := range c.battLevelCh {
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
out <- decodeUint8(res.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return out, cancel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeartRate gets the heart rate from the connected device
|
|
||||||
func (c *Client) HeartRate() (uint8, error) {
|
|
||||||
res, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeHeartRate,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeUint8(res.Value), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchHeartRate returns a channel which will contain
|
|
||||||
// new heart rate values as they update. Do not use after
|
|
||||||
// calling cancellation function
|
|
||||||
func (c *Client) WatchHeartRate() (<-chan uint8, func(), error) {
|
|
||||||
c.heartRateCh = make(chan types.Response, 2)
|
|
||||||
err := c.requestNoRes(types.Request{
|
|
||||||
Type: types.ReqTypeWatchHeartRate,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
res := <-c.heartRateCh
|
|
||||||
done, cancel := c.cancelFn(res.ID, c.heartRateCh)
|
|
||||||
out := make(chan uint8, 2)
|
|
||||||
go func() {
|
|
||||||
for res := range c.heartRateCh {
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
out <- decodeUint8(res.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return out, cancel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cancelFn generates a cancellation function for the given
|
|
||||||
// request type and channel
|
|
||||||
func (c *Client) cancelFn(reqID string, ch chan types.Response) (chan struct{}, func()) {
|
|
||||||
done := make(chan struct{}, 1)
|
|
||||||
return done, func() {
|
|
||||||
done <- struct{}{}
|
|
||||||
close(ch)
|
|
||||||
c.requestNoRes(types.Request{
|
|
||||||
Type: types.ReqTypeCancel,
|
|
||||||
Data: reqID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StepCount gets the step count from the connected device
|
|
||||||
func (c *Client) StepCount() (uint32, error) {
|
|
||||||
res, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeStepCount,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint32(res.Value.(float64)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchStepCount returns a channel which will contain
|
|
||||||
// new step count values as they update. Do not use after
|
|
||||||
// calling cancellation function
|
|
||||||
func (c *Client) WatchStepCount() (<-chan uint32, func(), error) {
|
|
||||||
c.stepCountCh = make(chan types.Response, 2)
|
|
||||||
err := c.requestNoRes(types.Request{
|
|
||||||
Type: types.ReqTypeWatchStepCount,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
res := <-c.stepCountCh
|
|
||||||
done, cancel := c.cancelFn(res.ID, c.stepCountCh)
|
|
||||||
out := make(chan uint32, 2)
|
|
||||||
go func() {
|
|
||||||
for res := range c.stepCountCh {
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
out <- decodeUint32(res.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return out, cancel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Motion gets the motion values from the connected device
|
|
||||||
func (c *Client) Motion() (infinitime.MotionValues, error) {
|
|
||||||
out := infinitime.MotionValues{}
|
|
||||||
res, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeMotion,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
err = mapstructure.Decode(res.Value, &out)
|
|
||||||
if err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchMotion returns a channel which will contain
|
|
||||||
// new motion values as they update. Do not use after
|
|
||||||
// calling cancellation function
|
|
||||||
func (c *Client) WatchMotion() (<-chan infinitime.MotionValues, func(), error) {
|
|
||||||
c.motionCh = make(chan types.Response, 5)
|
|
||||||
err := c.requestNoRes(types.Request{
|
|
||||||
Type: types.ReqTypeWatchMotion,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
res := <-c.motionCh
|
|
||||||
done, cancel := c.cancelFn(res.ID, c.motionCh)
|
|
||||||
out := make(chan infinitime.MotionValues, 5)
|
|
||||||
go func() {
|
|
||||||
for res := range c.motionCh {
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
motion, err := decodeMotion(res.Value)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out <- motion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return out, cancel, nil
|
|
||||||
}
|
|
@ -1,14 +1,16 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import "go.arsenm.dev/itd/internal/types"
|
import "context"
|
||||||
|
|
||||||
func (c *Client) Notify(title string, body string) error {
|
func (c *Client) Notify(ctx context.Context, title, body string) error {
|
||||||
_, err := c.request(types.Request{
|
return c.client.Call(
|
||||||
Type: types.ReqTypeNotify,
|
ctx,
|
||||||
Data: types.ReqDataNotify{
|
"ITD",
|
||||||
|
"Notify",
|
||||||
|
NotifyData{
|
||||||
Title: title,
|
Title: title,
|
||||||
Body: body,
|
Body: body,
|
||||||
},
|
},
|
||||||
})
|
nil,
|
||||||
return err
|
)
|
||||||
}
|
}
|
26
api/resources.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadResources loads resources onto the watch from the given
|
||||||
|
// file path to the resources zip
|
||||||
|
func (c *Client) LoadResources(ctx context.Context, path string) (<-chan infinitime.ResourceLoadProgress, error) {
|
||||||
|
progCh := make(chan infinitime.ResourceLoadProgress)
|
||||||
|
|
||||||
|
err := c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"FS",
|
||||||
|
"LoadResources",
|
||||||
|
path,
|
||||||
|
progCh,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return progCh, nil
|
||||||
|
}
|
16
api/set.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) SetTime(ctx context.Context, t time.Time) error {
|
||||||
|
return c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"SetTime",
|
||||||
|
t,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
33
api/time.go
@ -1,33 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetTime sets the given time on the connected device
|
|
||||||
func (c *Client) SetTime(t time.Time) error {
|
|
||||||
_, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeSetTime,
|
|
||||||
Data: t.Format(time.RFC3339),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTimeNow sets the time on the connected device to
|
|
||||||
// the current time. This is more accurate than
|
|
||||||
// SetTime(time.Now()) due to RFC3339 formatting
|
|
||||||
func (c *Client) SetTimeNow() error {
|
|
||||||
_, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeSetTime,
|
|
||||||
Data: "now",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
96
api/types.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpgradeType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
UpgradeTypeArchive UpgradeType = iota
|
||||||
|
UpgradeTypeFiles
|
||||||
|
)
|
||||||
|
|
||||||
|
type FSData struct {
|
||||||
|
Files []string
|
||||||
|
Data string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FwUpgradeData struct {
|
||||||
|
Type UpgradeType
|
||||||
|
Files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotifyData struct {
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FSTransferProgress struct {
|
||||||
|
Total uint32
|
||||||
|
Sent uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileInfo struct {
|
||||||
|
Name string
|
||||||
|
Size int64
|
||||||
|
IsDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi FileInfo) String() string {
|
||||||
|
var isDirChar rune
|
||||||
|
if fi.IsDir {
|
||||||
|
isDirChar = 'd'
|
||||||
|
} else {
|
||||||
|
isDirChar = '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get human-readable value for file size
|
||||||
|
val, unit := bytesHuman(fi.Size)
|
||||||
|
prec := 0
|
||||||
|
// If value is less than 10, set precision to 1
|
||||||
|
if val < 10 {
|
||||||
|
prec = 1
|
||||||
|
}
|
||||||
|
// Convert float to string
|
||||||
|
valStr := strconv.FormatFloat(val, 'f', prec, 64)
|
||||||
|
|
||||||
|
// Return string formatted like so:
|
||||||
|
// - 10 kB file
|
||||||
|
// or:
|
||||||
|
// d 0 B .
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%c %3s %-2s %s",
|
||||||
|
isDirChar,
|
||||||
|
valStr,
|
||||||
|
unit,
|
||||||
|
fi.Name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytesHuman returns a human-readable string for
|
||||||
|
// the amount of bytes inputted.
|
||||||
|
func bytesHuman(b int64) (float64, string) {
|
||||||
|
const unit = 1000
|
||||||
|
// Set possible units prefixes (PineTime flash is 4MB)
|
||||||
|
units := [2]rune{'k', 'M'}
|
||||||
|
// If amount of bytes is less than smallest unit
|
||||||
|
if b < unit {
|
||||||
|
// Return unchanged with unit "B"
|
||||||
|
return float64(b), "B"
|
||||||
|
}
|
||||||
|
|
||||||
|
div, exp := int64(unit), 0
|
||||||
|
// Get decimal values and unit prefix index
|
||||||
|
for n := b / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create string for full unit
|
||||||
|
unitStr := string([]rune{units[exp], 'B'})
|
||||||
|
|
||||||
|
// Return decimal with unit string
|
||||||
|
return float64(b) / float64(div), unitStr
|
||||||
|
}
|
13
api/update.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func (c *Client) WeatherUpdate(ctx context.Context) error {
|
||||||
|
return c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"WeatherUpdate",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
@ -1,48 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DFUProgress stores the progress of a DFU upfate
|
|
||||||
type DFUProgress types.DFUProgress
|
|
||||||
|
|
||||||
// UpgradeType indicates the type of upgrade to be performed
|
|
||||||
type UpgradeType uint8
|
|
||||||
|
|
||||||
// Type of DFU upgrade
|
|
||||||
const (
|
|
||||||
UpgradeTypeArchive UpgradeType = iota
|
|
||||||
UpgradeTypeFiles
|
|
||||||
)
|
|
||||||
|
|
||||||
// FirmwareUpgrade initiates a DFU update and returns the progress channel
|
|
||||||
func (c *Client) FirmwareUpgrade(upgType UpgradeType, files ...string) (<-chan DFUProgress, error) {
|
|
||||||
err := json.NewEncoder(c.conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeFwUpgrade,
|
|
||||||
Data: types.ReqDataFwUpgrade{
|
|
||||||
Type: int(upgType),
|
|
||||||
Files: files,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.dfuProgressCh = make(chan types.Response, 5)
|
|
||||||
|
|
||||||
out := make(chan DFUProgress, 5)
|
|
||||||
go func() {
|
|
||||||
for res := range c.dfuProgressCh {
|
|
||||||
progress, err := decodeDFUProgress(res.Value)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out <- progress
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
71
api/watch.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) WatchHeartRate(ctx context.Context) (<-chan uint8, error) {
|
||||||
|
outCh := make(chan uint8, 2)
|
||||||
|
err := c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"WatchHeartRate",
|
||||||
|
nil,
|
||||||
|
outCh,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WatchBatteryLevel(ctx context.Context) (<-chan uint8, error) {
|
||||||
|
outCh := make(chan uint8, 2)
|
||||||
|
err := c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"WatchBatteryLevel",
|
||||||
|
nil,
|
||||||
|
outCh,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WatchStepCount(ctx context.Context) (<-chan uint32, error) {
|
||||||
|
outCh := make(chan uint32, 2)
|
||||||
|
err := c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"WatchStepCount",
|
||||||
|
nil,
|
||||||
|
outCh,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WatchMotion(ctx context.Context) (<-chan infinitime.MotionValues, error) {
|
||||||
|
outCh := make(chan infinitime.MotionValues, 2)
|
||||||
|
err := c.client.Call(
|
||||||
|
ctx,
|
||||||
|
"ITD",
|
||||||
|
"WatchMotion",
|
||||||
|
nil,
|
||||||
|
outCh,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outCh, nil
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UpdateWeather sends the update weather signal,
|
|
||||||
// immediately sending current weather data
|
|
||||||
func (c *Client) UpdateWeather() error {
|
|
||||||
_, err := c.request(types.Request{
|
|
||||||
Type: types.ReqTypeWeatherUpdate,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
31
calls.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
@ -8,15 +9,15 @@ import (
|
|||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initCallNotifs(dev *infinitime.Device) error {
|
func initCallNotifs(ctx context.Context, dev *infinitime.Device) error {
|
||||||
// Connect to system bus. This connection is for method calls.
|
// Connect to system bus. This connection is for method calls.
|
||||||
conn, err := newSystemBusConn()
|
conn, err := newSystemBusConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if modem manager interface exists
|
// Check if modem manager interface exists
|
||||||
exists, err := modemManagerExists(conn)
|
exists, err := modemManagerExists(ctx, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -28,7 +29,7 @@ func initCallNotifs(dev *infinitime.Device) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect to system bus. This connection is for monitoring.
|
// Connect to system bus. This connection is for monitoring.
|
||||||
monitorConn, err := newSystemBusConn()
|
monitorConn, err := newSystemBusConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -78,13 +79,13 @@ func initCallNotifs(dev *infinitime.Device) error {
|
|||||||
switch res {
|
switch res {
|
||||||
case infinitime.CallStatusAccepted:
|
case infinitime.CallStatusAccepted:
|
||||||
// Attempt to accept call
|
// Attempt to accept call
|
||||||
err = acceptCall(conn, callObj)
|
err = acceptCall(ctx, conn, callObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Msg("Error accepting call")
|
log.Warn().Err(err).Msg("Error accepting call")
|
||||||
}
|
}
|
||||||
case infinitime.CallStatusDeclined:
|
case infinitime.CallStatusDeclined:
|
||||||
// Attempt to decline call
|
// Attempt to decline call
|
||||||
err = declineCall(conn, callObj)
|
err = declineCall(ctx, conn, callObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Msg("Error declining call")
|
log.Warn().Err(err).Msg("Error declining call")
|
||||||
}
|
}
|
||||||
@ -101,9 +102,11 @@ func initCallNotifs(dev *infinitime.Device) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func modemManagerExists(conn *dbus.Conn) (bool, error) {
|
func modemManagerExists(ctx context.Context, conn *dbus.Conn) (bool, error) {
|
||||||
var names []string
|
var names []string
|
||||||
err := conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&names)
|
err := conn.BusObject().CallWithContext(
|
||||||
|
ctx, "org.freedesktop.DBus.ListNames", 0,
|
||||||
|
).Store(&names)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -122,9 +125,11 @@ func getPhoneNum(conn *dbus.Conn, callObj dbus.BusObject) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getPhoneNum accepts a call using a DBus connection
|
// getPhoneNum accepts a call using a DBus connection
|
||||||
func acceptCall(conn *dbus.Conn, callObj dbus.BusObject) error {
|
func acceptCall(ctx context.Context, conn *dbus.Conn, callObj dbus.BusObject) error {
|
||||||
// Call Accept() method on DBus object
|
// Call Accept() method on DBus object
|
||||||
call := callObj.Call("org.freedesktop.ModemManager1.Call.Accept", 0)
|
call := callObj.CallWithContext(
|
||||||
|
ctx, "org.freedesktop.ModemManager1.Call.Accept", 0,
|
||||||
|
)
|
||||||
if call.Err != nil {
|
if call.Err != nil {
|
||||||
return call.Err
|
return call.Err
|
||||||
}
|
}
|
||||||
@ -132,9 +137,11 @@ func acceptCall(conn *dbus.Conn, callObj dbus.BusObject) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getPhoneNum declines a call using a DBus connection
|
// getPhoneNum declines a call using a DBus connection
|
||||||
func declineCall(conn *dbus.Conn, callObj dbus.BusObject) error {
|
func declineCall(ctx context.Context, conn *dbus.Conn, callObj dbus.BusObject) error {
|
||||||
// Call Hangup() method on DBus object
|
// Call Hangup() method on DBus object
|
||||||
call := callObj.Call("org.freedesktop.ModemManager1.Call.Hangup", 0)
|
call := callObj.CallWithContext(
|
||||||
|
ctx, "org.freedesktop.ModemManager1.Call.Hangup", 0,
|
||||||
|
)
|
||||||
if call.Err != nil {
|
if call.Err != nil {
|
||||||
return call.Err
|
return call.Err
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,26 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cheggaaa/pb/v3"
|
"github.com/cheggaaa/pb/v3"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/api"
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func fwUpgrade(c *cli.Context) error {
|
func fwUpgrade(c *cli.Context) error {
|
||||||
|
resources := c.String("resources")
|
||||||
|
if resources != "" {
|
||||||
|
absRes, err := filepath.Abs(resources)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = resLoad(c.Context, []string{absRes})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Resource loading has returned an error. This can happen if your current version of InfiniTime doesn't support BLE FS. Try updating without resource loading, and then load them after using the `itctl res load` command.")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
var upgType api.UpgradeType
|
var upgType api.UpgradeType
|
||||||
@ -19,17 +33,17 @@ func fwUpgrade(c *cli.Context) error {
|
|||||||
// Get relevant data struct
|
// Get relevant data struct
|
||||||
if c.String("archive") != "" {
|
if c.String("archive") != "" {
|
||||||
// Get archive data struct
|
// Get archive data struct
|
||||||
upgType = types.UpgradeTypeArchive
|
upgType = api.UpgradeTypeArchive
|
||||||
files = []string{c.String("archive")}
|
files = []string{c.String("archive")}
|
||||||
} else if c.String("init-packet") != "" && c.String("firmware") != "" {
|
} else if c.String("init-packet") != "" && c.String("firmware") != "" {
|
||||||
// Get files data struct
|
// Get files data struct
|
||||||
upgType = types.UpgradeTypeFiles
|
upgType = api.UpgradeTypeFiles
|
||||||
files = []string{c.String("init-packet"), c.String("firmware")}
|
files = []string{c.String("init-packet"), c.String("firmware")}
|
||||||
} else {
|
} else {
|
||||||
return cli.Exit("Upgrade command requires either archive or init packet and firmware.", 1)
|
return cli.Exit("Upgrade command requires either archive or init packet and firmware.", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
progress, err := client.FirmwareUpgrade(upgType, abs(files)...)
|
progress, err := client.FirmwareUpgrade(c.Context, upgType, abs(files)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -43,9 +57,9 @@ func fwUpgrade(c *cli.Context) error {
|
|||||||
// Set total bytes in progress bar
|
// Set total bytes in progress bar
|
||||||
bar.SetTotal(event.Total)
|
bar.SetTotal(event.Total)
|
||||||
// Set amount of bytes received in progress bar
|
// Set amount of bytes received in progress bar
|
||||||
bar.SetCurrent(event.Received)
|
bar.SetCurrent(int64(event.Received))
|
||||||
// If transfer finished, break
|
// If transfer finished, break
|
||||||
if event.Sent == event.Total {
|
if int64(event.Sent) == event.Total {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,7 +73,7 @@ func fwUpgrade(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fwVersion(c *cli.Context) error {
|
func fwVersion(c *cli.Context) error {
|
||||||
version, err := client.Version()
|
version, err := client.Version(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ func fsList(c *cli.Context) error {
|
|||||||
dirPath = c.Args().Get(0)
|
dirPath = c.Args().Get(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
listing, err := client.ReadDir(dirPath)
|
listing, err := client.ReadDir(c.Context, dirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -34,7 +34,12 @@ func fsMkdir(c *cli.Context) error {
|
|||||||
return cli.Exit("Command mkdir requires one or more arguments", 1)
|
return cli.Exit("Command mkdir requires one or more arguments", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.Mkdir(c.Args().Slice()...)
|
var err error
|
||||||
|
if c.Bool("parents") {
|
||||||
|
err = client.MkdirAll(c.Context, c.Args().Slice()...)
|
||||||
|
} else {
|
||||||
|
err = client.Mkdir(c.Context, c.Args().Slice()...)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -47,7 +52,7 @@ func fsMove(c *cli.Context) error {
|
|||||||
return cli.Exit("Command move requires two arguments", 1)
|
return cli.Exit("Command move requires two arguments", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.Rename(c.Args().Get(0), c.Args().Get(1))
|
err := client.Rename(c.Context, c.Args().Get(0), c.Args().Get(1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -76,7 +81,7 @@ func fsRead(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
progress, err := client.ReadFile(path, c.Args().Get(0))
|
progress, err := client.Download(c.Context, path, c.Args().Get(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -91,12 +96,8 @@ func fsRead(c *cli.Context) error {
|
|||||||
bar.SetTotal(int64(event.Total))
|
bar.SetTotal(int64(event.Total))
|
||||||
// Set amount of bytes sent in progress bar
|
// Set amount of bytes sent in progress bar
|
||||||
bar.SetCurrent(int64(event.Sent))
|
bar.SetCurrent(int64(event.Sent))
|
||||||
// If transfer finished, break
|
|
||||||
if event.Done {
|
|
||||||
bar.Finish()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
bar.Finish()
|
||||||
|
|
||||||
if c.Args().Get(1) == "-" {
|
if c.Args().Get(1) == "-" {
|
||||||
io.Copy(os.Stdout, tmpFile)
|
io.Copy(os.Stdout, tmpFile)
|
||||||
@ -113,7 +114,12 @@ func fsRemove(c *cli.Context) error {
|
|||||||
return cli.Exit("Command remove requires one or more arguments", 1)
|
return cli.Exit("Command remove requires one or more arguments", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.Remove(c.Args().Slice()...)
|
var err error
|
||||||
|
if c.Bool("recursive") {
|
||||||
|
err = client.RemoveAll(c.Context, c.Args().Slice()...)
|
||||||
|
} else {
|
||||||
|
err = client.Remove(c.Context, c.Args().Slice()...)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -148,7 +154,7 @@ func fsWrite(c *cli.Context) error {
|
|||||||
defer os.Remove(path)
|
defer os.Remove(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
progress, err := client.WriteFile(path, c.Args().Get(1))
|
progress, err := client.Upload(c.Context, c.Args().Get(1), path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -163,11 +169,6 @@ func fsWrite(c *cli.Context) error {
|
|||||||
bar.SetTotal(int64(event.Total))
|
bar.SetTotal(int64(event.Total))
|
||||||
// Set amount of bytes sent in progress bar
|
// Set amount of bytes sent in progress bar
|
||||||
bar.SetCurrent(int64(event.Sent))
|
bar.SetCurrent(int64(event.Sent))
|
||||||
// If transfer finished, break
|
|
||||||
if event.Done {
|
|
||||||
bar.Finish()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getAddress(c *cli.Context) error {
|
func getAddress(c *cli.Context) error {
|
||||||
address, err := client.Address()
|
address, err := client.Address(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ func getAddress(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getBattery(c *cli.Context) error {
|
func getBattery(c *cli.Context) error {
|
||||||
battLevel, err := client.BatteryLevel()
|
battLevel, err := client.BatteryLevel(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ func getBattery(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getHeart(c *cli.Context) error {
|
func getHeart(c *cli.Context) error {
|
||||||
heartRate, err := client.HeartRate()
|
heartRate, err := client.HeartRate(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ func getHeart(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getMotion(c *cli.Context) error {
|
func getMotion(c *cli.Context) error {
|
||||||
motionVals, err := client.Motion()
|
motionVals, err := client.Motion(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ func getMotion(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getSteps(c *cli.Context) error {
|
func getSteps(c *cli.Context) error {
|
||||||
stepCount, err := client.StepCount()
|
stepCount, err := client.StepCount(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@ -14,8 +18,24 @@ var client *api.Client
|
|||||||
func main() {
|
func main() {
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, _ = signal.NotifyContext(
|
||||||
|
ctx,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
)
|
||||||
|
|
||||||
|
// This goroutine ensures that itctl will exit
|
||||||
|
// at most 200ms after the user sends SIGINT/SIGTERM.
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
app := cli.App{
|
app := cli.App{
|
||||||
Name: "itctl",
|
Name: "itctl",
|
||||||
|
HideHelpCommand: true,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "socket-path",
|
Name: "socket-path",
|
||||||
@ -25,6 +45,25 @@ func main() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "help",
|
||||||
|
ArgsUsage: "<command>",
|
||||||
|
Usage: "Display help screen for a command",
|
||||||
|
Action: helpCmd,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "resources",
|
||||||
|
Aliases: []string{"res"},
|
||||||
|
Usage: "Handle InfiniTime resource loading",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "load",
|
||||||
|
ArgsUsage: "<path>",
|
||||||
|
Usage: "Load an InifiniTime resources package",
|
||||||
|
Action: resourcesLoad,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "filesystem",
|
Name: "filesystem",
|
||||||
Aliases: []string{"fs"},
|
Aliases: []string{"fs"},
|
||||||
@ -38,6 +77,13 @@ func main() {
|
|||||||
Action: fsList,
|
Action: fsList,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "parents",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "Make parent directories if needed, no error if already existing",
|
||||||
|
},
|
||||||
|
},
|
||||||
Name: "mkdir",
|
Name: "mkdir",
|
||||||
ArgsUsage: "<paths...>",
|
ArgsUsage: "<paths...>",
|
||||||
Usage: "Create new directories",
|
Usage: "Create new directories",
|
||||||
@ -58,6 +104,13 @@ func main() {
|
|||||||
Action: fsRead,
|
Action: fsRead,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "recursive",
|
||||||
|
Aliases: []string{"r", "R"},
|
||||||
|
Usage: "Remove directories and their contents recursively",
|
||||||
|
},
|
||||||
|
},
|
||||||
Name: "remove",
|
Name: "remove",
|
||||||
ArgsUsage: "<paths...>",
|
ArgsUsage: "<paths...>",
|
||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
@ -90,6 +143,11 @@ func main() {
|
|||||||
Aliases: []string{"f"},
|
Aliases: []string{"f"},
|
||||||
Usage: "Path to firmware image (.bin file)",
|
Usage: "Path to firmware image (.bin file)",
|
||||||
},
|
},
|
||||||
|
&cli.PathFlag{
|
||||||
|
Name: "resources",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "Path to resources file (.zip file)",
|
||||||
|
},
|
||||||
&cli.PathFlag{
|
&cli.PathFlag{
|
||||||
Name: "archive",
|
Name: "archive",
|
||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
@ -174,13 +232,58 @@ func main() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "watch",
|
||||||
|
Usage: "Watch a value for changes",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{Name: "json"},
|
||||||
|
&cli.BoolFlag{Name: "shell"},
|
||||||
|
},
|
||||||
|
Name: "heart",
|
||||||
|
Usage: "Watch heart rate value for changes",
|
||||||
|
Action: watchHeart,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{Name: "json"},
|
||||||
|
&cli.BoolFlag{Name: "shell"},
|
||||||
|
},
|
||||||
|
Name: "steps",
|
||||||
|
Usage: "Watch step count value for changes",
|
||||||
|
Action: watchStepCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{Name: "json"},
|
||||||
|
&cli.BoolFlag{Name: "shell"},
|
||||||
|
},
|
||||||
|
Name: "motion",
|
||||||
|
Usage: "Watch motion coordinates for changes",
|
||||||
|
Action: watchMotion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{Name: "json"},
|
||||||
|
&cli.BoolFlag{Name: "shell"},
|
||||||
|
},
|
||||||
|
Name: "battery",
|
||||||
|
Aliases: []string{"batt"},
|
||||||
|
Usage: "Watch battery level value for changes",
|
||||||
|
Action: watchBattLevel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Before: func(c *cli.Context) error {
|
Before: func(c *cli.Context) error {
|
||||||
newClient, err := api.New(c.String("socket-path"))
|
if !isHelpCmd() {
|
||||||
if err != nil {
|
newClient, err := api.New(c.String("socket-path"))
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client = newClient
|
||||||
}
|
}
|
||||||
client = newClient
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
After: func(*cli.Context) error {
|
After: func(*cli.Context) error {
|
||||||
@ -191,8 +294,26 @@ func main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := app.Run(os.Args)
|
err := app.RunContext(ctx, os.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error while running app")
|
log.Fatal().Err(err).Msg("Error while running app")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func helpCmd(c *cli.Context) error {
|
||||||
|
cmdArgs := append([]string{os.Args[0]}, c.Args().Slice()...)
|
||||||
|
cmdArgs = append(cmdArgs, "-h")
|
||||||
|
return c.App.RunContext(c.Context, cmdArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHelpCmd() bool {
|
||||||
|
if len(os.Args) == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, arg := range os.Args {
|
||||||
|
if arg == "-h" || arg == "help" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@ func notify(c *cli.Context) error {
|
|||||||
return cli.Exit("Command notify requires two arguments", 1)
|
return cli.Exit("Command notify requires two arguments", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.Notify(c.Args().Get(0), c.Args().Get(1))
|
err := client.Notify(c.Context, c.Args().Get(0), c.Args().Get(1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
57
cmd/itctl/resources.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/cheggaaa/pb/v3"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourcesLoad(c *cli.Context) error {
|
||||||
|
return resLoad(c.Context, c.Args().Slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
func resLoad(ctx context.Context, args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return cli.Exit("Command load requires one argument.", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create progress bar templates
|
||||||
|
rmTmpl := `Removing {{string . "filename"}}`
|
||||||
|
upTmpl := `Uploading {{string . "filename"}} {{counters . }} B {{bar . "|" "-" (cycle .) " " "|"}} {{percent . }} {{rtime . "%s"}}`
|
||||||
|
// Start full bar at 0 total
|
||||||
|
bar := pb.ProgressBarTemplate(rmTmpl).Start(0)
|
||||||
|
|
||||||
|
path, err := filepath.Abs(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
progCh, err := client.LoadResources(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for evt := range progCh {
|
||||||
|
if evt.Err != nil {
|
||||||
|
return evt.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
if evt.Operation == infinitime.ResourceOperationRemoveObsolete {
|
||||||
|
bar.SetTemplateString(rmTmpl)
|
||||||
|
bar.Set("filename", evt.Name)
|
||||||
|
} else {
|
||||||
|
bar.SetTemplateString(upTmpl)
|
||||||
|
bar.Set("filename", evt.Name)
|
||||||
|
|
||||||
|
bar.SetTotal(evt.Total)
|
||||||
|
bar.SetCurrent(evt.Sent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bar.Finish()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -13,12 +13,12 @@ func setTime(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Args().Get(0) == "now" {
|
if c.Args().Get(0) == "now" {
|
||||||
return client.SetTimeNow()
|
return client.SetTime(c.Context, time.Now())
|
||||||
} else {
|
} else {
|
||||||
parsed, err := time.Parse(time.RFC3339, c.Args().Get(0))
|
parsed, err := time.Parse(time.RFC3339, c.Args().Get(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return client.SetTime(parsed)
|
return client.SetTime(c.Context, parsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,5 @@ package main
|
|||||||
import "github.com/urfave/cli/v2"
|
import "github.com/urfave/cli/v2"
|
||||||
|
|
||||||
func updateWeather(c *cli.Context) error {
|
func updateWeather(c *cli.Context) error {
|
||||||
return client.UpdateWeather()
|
return client.WeatherUpdate(c.Context)
|
||||||
}
|
}
|
||||||
|
108
cmd/itctl/watch.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func watchHeart(c *cli.Context) error {
|
||||||
|
heartCh, err := client.WatchHeartRate(c.Context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case heartRate := <-heartCh:
|
||||||
|
if c.Bool("json") {
|
||||||
|
json.NewEncoder(os.Stdout).Encode(
|
||||||
|
map[string]uint8{"heartRate": heartRate},
|
||||||
|
)
|
||||||
|
} else if c.Bool("shell") {
|
||||||
|
fmt.Printf("HEART_RATE=%d\n", heartRate)
|
||||||
|
} else {
|
||||||
|
fmt.Println(heartRate, "BPM")
|
||||||
|
}
|
||||||
|
case <-c.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchBattLevel(c *cli.Context) error {
|
||||||
|
battLevelCh, err := client.WatchBatteryLevel(c.Context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case battLevel := <-battLevelCh:
|
||||||
|
if c.Bool("json") {
|
||||||
|
json.NewEncoder(os.Stdout).Encode(
|
||||||
|
map[string]uint8{"battLevel": battLevel},
|
||||||
|
)
|
||||||
|
} else if c.Bool("shell") {
|
||||||
|
fmt.Printf("BATTERY_LEVEL=%d\n", battLevel)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%d%%\n", battLevel)
|
||||||
|
}
|
||||||
|
case <-c.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchStepCount(c *cli.Context) error {
|
||||||
|
stepCountCh, err := client.WatchStepCount(c.Context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case stepCount := <-stepCountCh:
|
||||||
|
if c.Bool("json") {
|
||||||
|
json.NewEncoder(os.Stdout).Encode(
|
||||||
|
map[string]uint32{"stepCount": stepCount},
|
||||||
|
)
|
||||||
|
} else if c.Bool("shell") {
|
||||||
|
fmt.Printf("STEP_COUNT=%d\n", stepCount)
|
||||||
|
} else {
|
||||||
|
fmt.Println(stepCount, "Steps")
|
||||||
|
}
|
||||||
|
case <-c.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchMotion(c *cli.Context) error {
|
||||||
|
motionCh, err := client.WatchMotion(c.Context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case motionVals := <-motionCh:
|
||||||
|
if c.Bool("json") {
|
||||||
|
json.NewEncoder(os.Stdout).Encode(motionVals)
|
||||||
|
} else if c.Bool("shell") {
|
||||||
|
fmt.Printf(
|
||||||
|
"X=%d\nY=%d\nZ=%d\n",
|
||||||
|
motionVals.X,
|
||||||
|
motionVals.Y,
|
||||||
|
motionVals.Z,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
fmt.Println(motionVals)
|
||||||
|
}
|
||||||
|
case <-c.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,10 +28,15 @@ func guiErr(err error, msg string, fatal bool, parent fyne.Window) {
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Create new label containing error text
|
// Create new label containing error text
|
||||||
errLbl := widget.NewLabel(err.Error())
|
errEntry := widget.NewEntry()
|
||||||
|
errEntry.SetText(err.Error())
|
||||||
|
// If text changed, change it back
|
||||||
|
errEntry.OnChanged = func(string) {
|
||||||
|
errEntry.SetText(err.Error())
|
||||||
|
}
|
||||||
// Create new dropdown containing error label
|
// Create new dropdown containing error label
|
||||||
content.Add(widget.NewAccordion(
|
content.Add(widget.NewAccordion(
|
||||||
widget.NewAccordionItem("More Details", errLbl),
|
widget.NewAccordionItem("More Details", errEntry),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
if fatal {
|
if fatal {
|
||||||
@ -49,5 +54,4 @@ func guiErr(err error, msg string, fatal bool, parent fyne.Window) {
|
|||||||
// Show error dialog
|
// Show error dialog
|
||||||
dialog.NewCustom("Error", "Ok", content, parent).Show()
|
dialog.NewCustom("Error", "Ok", content, parent).Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
163
cmd/itgui/firmware.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/layout"
|
||||||
|
"fyne.io/fyne/v2/storage"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"go.arsenm.dev/itd/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func firmwareTab(ctx context.Context, client *api.Client, w fyne.Window) fyne.CanvasObject {
|
||||||
|
// Create select to chose between archive and files upgrade
|
||||||
|
typeSelect := widget.NewSelect([]string{"Archive", "Files"}, nil)
|
||||||
|
typeSelect.PlaceHolder = "Upgrade Type"
|
||||||
|
|
||||||
|
// Create map to store files
|
||||||
|
files := map[string]string{}
|
||||||
|
|
||||||
|
// Create and disable start button
|
||||||
|
startBtn := widget.NewButton("Start", nil)
|
||||||
|
startBtn.Disable()
|
||||||
|
|
||||||
|
// Create new file open dialog for archive
|
||||||
|
archiveDlg := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {
|
||||||
|
if err != nil || uc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer uc.Close()
|
||||||
|
// Set archive path in map
|
||||||
|
files[".zip"] = uc.URI().Path()
|
||||||
|
// Enable start button
|
||||||
|
startBtn.Enable()
|
||||||
|
}, w)
|
||||||
|
// Only allow .zip files
|
||||||
|
archiveDlg.SetFilter(storage.NewExtensionFileFilter([]string{".zip"}))
|
||||||
|
// Create button to show dialog
|
||||||
|
archiveBtn := widget.NewButton("Select Archive (.zip)", archiveDlg.Show)
|
||||||
|
|
||||||
|
// Create new file open dialog for firmware image
|
||||||
|
imageDlg := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {
|
||||||
|
if err != nil || uc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer uc.Close()
|
||||||
|
|
||||||
|
// Set firmware image path in map
|
||||||
|
files[".bin"] = uc.URI().Path()
|
||||||
|
|
||||||
|
// If the init packet was already selected
|
||||||
|
_, datOk := files[".dat"]
|
||||||
|
if datOk {
|
||||||
|
// Enable start button
|
||||||
|
startBtn.Enable()
|
||||||
|
}
|
||||||
|
}, w)
|
||||||
|
// Only allow .bin files
|
||||||
|
imageDlg.SetFilter(storage.NewExtensionFileFilter([]string{".bin"}))
|
||||||
|
// Create button to show dialog
|
||||||
|
imageBtn := widget.NewButton("Select Firmware (.bin)", imageDlg.Show)
|
||||||
|
|
||||||
|
// Create new file open dialog for init packet
|
||||||
|
initDlg := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {
|
||||||
|
if err != nil || uc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer uc.Close()
|
||||||
|
|
||||||
|
// Set init packet path in map
|
||||||
|
files[".dat"] = uc.URI().Path()
|
||||||
|
|
||||||
|
// If the firmware image was already selected
|
||||||
|
_, binOk := files[".bin"]
|
||||||
|
if binOk {
|
||||||
|
// Enable start button
|
||||||
|
startBtn.Enable()
|
||||||
|
}
|
||||||
|
}, w)
|
||||||
|
// Only allow .dat files
|
||||||
|
initDlg.SetFilter(storage.NewExtensionFileFilter([]string{".dat"}))
|
||||||
|
// Create button to show dialog
|
||||||
|
initBtn := widget.NewButton("Select Init Packet (.dat)", initDlg.Show)
|
||||||
|
|
||||||
|
var upgType api.UpgradeType = 255
|
||||||
|
// When upgrade type changes
|
||||||
|
typeSelect.OnChanged = func(s string) {
|
||||||
|
// Delete all files from map
|
||||||
|
delete(files, ".bin")
|
||||||
|
delete(files, ".dat")
|
||||||
|
delete(files, ".zip")
|
||||||
|
// Hide all dialog buttons
|
||||||
|
imageBtn.Hide()
|
||||||
|
initBtn.Hide()
|
||||||
|
archiveBtn.Hide()
|
||||||
|
// Disable start button
|
||||||
|
startBtn.Disable()
|
||||||
|
|
||||||
|
switch s {
|
||||||
|
case "Files":
|
||||||
|
// Set file upgrade type
|
||||||
|
upgType = api.UpgradeTypeFiles
|
||||||
|
// Show firmware image and init packet buttons
|
||||||
|
imageBtn.Show()
|
||||||
|
initBtn.Show()
|
||||||
|
case "Archive":
|
||||||
|
// Set archive upgrade type
|
||||||
|
upgType = api.UpgradeTypeArchive
|
||||||
|
// Show archive button
|
||||||
|
archiveBtn.Show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Select archive by default
|
||||||
|
typeSelect.SetSelectedIndex(0)
|
||||||
|
|
||||||
|
// When start button pressed
|
||||||
|
startBtn.OnTapped = func() {
|
||||||
|
var args []string
|
||||||
|
// Append the appropriate files for upgrade type
|
||||||
|
switch upgType {
|
||||||
|
case api.UpgradeTypeArchive:
|
||||||
|
args = append(args, files[".zip"])
|
||||||
|
case api.UpgradeTypeFiles:
|
||||||
|
args = append(args, files[".dat"], files[".bin"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// If args are nil (invalid upgrade type)
|
||||||
|
if args == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new progress dialog
|
||||||
|
progress := newProgress(w)
|
||||||
|
// Start firmware upgrade
|
||||||
|
progressCh, err := client.FirmwareUpgrade(ctx, upgType, args...)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error performing firmware upgrade", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Show progress dialog
|
||||||
|
progress.Show()
|
||||||
|
// For every progress event
|
||||||
|
for progressEvt := range progressCh {
|
||||||
|
// Set progress bar values
|
||||||
|
progress.SetTotal(float64(progressEvt.Total))
|
||||||
|
progress.SetValue(float64(progressEvt.Sent))
|
||||||
|
}
|
||||||
|
// Hide progress dialog
|
||||||
|
progress.Hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
return container.NewVBox(
|
||||||
|
layout.NewSpacer(),
|
||||||
|
typeSelect,
|
||||||
|
archiveBtn,
|
||||||
|
imageBtn,
|
||||||
|
initBtn,
|
||||||
|
startBtn,
|
||||||
|
layout.NewSpacer(),
|
||||||
|
)
|
||||||
|
}
|
407
cmd/itgui/fs.go
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/data/binding"
|
||||||
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/storage"
|
||||||
|
"fyne.io/fyne/v2/theme"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
"go.arsenm.dev/itd/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fsTab(ctx context.Context, client *api.Client, w fyne.Window, opened chan struct{}) fyne.CanvasObject {
|
||||||
|
c := container.NewVBox()
|
||||||
|
|
||||||
|
// Create new binding to store current directory
|
||||||
|
cwdData := binding.NewString()
|
||||||
|
cwdData.Set("/")
|
||||||
|
|
||||||
|
// Create new list binding to store fs listing entries
|
||||||
|
lsData := binding.NewUntypedList()
|
||||||
|
|
||||||
|
// This goroutine waits until the fs tab is opened to
|
||||||
|
// request the listing from the watch
|
||||||
|
go func() {
|
||||||
|
// Wait for opened signal
|
||||||
|
<-opened
|
||||||
|
|
||||||
|
// Show loading pop up
|
||||||
|
loading := newLoadingPopUp(w)
|
||||||
|
loading.Show()
|
||||||
|
|
||||||
|
// Read root directory
|
||||||
|
ls, err := client.ReadDir(ctx, "/")
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error reading directory", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Set ls binding
|
||||||
|
lsData.Set(lsToAny(ls))
|
||||||
|
|
||||||
|
// Hide loading pop up
|
||||||
|
loading.Hide()
|
||||||
|
}()
|
||||||
|
|
||||||
|
toolbar := widget.NewToolbar(
|
||||||
|
widget.NewToolbarAction(
|
||||||
|
theme.ViewRefreshIcon(),
|
||||||
|
func() {
|
||||||
|
refresh(ctx, cwdData, lsData, client, w, c)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
widget.NewToolbarAction(
|
||||||
|
theme.FileApplicationIcon(),
|
||||||
|
func() {
|
||||||
|
dlg := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {
|
||||||
|
if err != nil || uc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resPath := uc.URI().Path()
|
||||||
|
uc.Close()
|
||||||
|
|
||||||
|
progressDlg := newProgress(w)
|
||||||
|
progressDlg.Show()
|
||||||
|
|
||||||
|
progCh, err := client.LoadResources(ctx, resPath)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error loading resources", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for evt := range progCh {
|
||||||
|
if evt.Err != nil {
|
||||||
|
guiErr(evt.Err, "Error loading resources", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch evt.Operation {
|
||||||
|
case infinitime.ResourceOperationRemoveObsolete:
|
||||||
|
progressDlg.SetText("Removing " + evt.Name)
|
||||||
|
case infinitime.ResourceOperationUpload:
|
||||||
|
progressDlg.SetText("Uploading " + evt.Name)
|
||||||
|
progressDlg.SetTotal(float64(evt.Total))
|
||||||
|
progressDlg.SetValue(float64(evt.Sent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progressDlg.Hide()
|
||||||
|
refresh(ctx, cwdData, lsData, client, w, c)
|
||||||
|
}, w)
|
||||||
|
dlg.SetConfirmText("Upload Resources")
|
||||||
|
dlg.SetFilter(storage.NewExtensionFileFilter([]string{
|
||||||
|
".zip",
|
||||||
|
}))
|
||||||
|
dlg.Show()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
widget.NewToolbarAction(
|
||||||
|
theme.UploadIcon(),
|
||||||
|
func() {
|
||||||
|
// Create open dialog for file that will be uploaded
|
||||||
|
dlg := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {
|
||||||
|
if err != nil || uc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get filepath and close
|
||||||
|
localPath := uc.URI().Path()
|
||||||
|
uc.Close()
|
||||||
|
|
||||||
|
// Create new entry to store filepath
|
||||||
|
filenameEntry := widget.NewEntry()
|
||||||
|
// Set entry text to the file name of the selected file
|
||||||
|
filenameEntry.SetText(filepath.Base(localPath))
|
||||||
|
// Create new dialog asking for the filename of the file to be stored on the watch
|
||||||
|
uploadDlg := dialog.NewForm("Upload", "Upload", "Cancel", []*widget.FormItem{
|
||||||
|
widget.NewFormItem("Filename", filenameEntry),
|
||||||
|
}, func(ok bool) {
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current directory
|
||||||
|
cwd, _ := cwdData.Get()
|
||||||
|
// Get remote path by joining current directory with filename
|
||||||
|
remotePath := filepath.Join(cwd, filenameEntry.Text)
|
||||||
|
|
||||||
|
// Create new progress dialog
|
||||||
|
progressDlg := newProgress(w)
|
||||||
|
progressDlg.Show()
|
||||||
|
|
||||||
|
// Upload file
|
||||||
|
progressCh, err := client.Upload(ctx, remotePath, localPath)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error uploading file", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for progressEvt := range progressCh {
|
||||||
|
progressDlg.SetTotal(float64(progressEvt.Total))
|
||||||
|
progressDlg.SetValue(float64(progressEvt.Sent))
|
||||||
|
if progressEvt.Sent == progressEvt.Total {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close progress dialog
|
||||||
|
progressDlg.Hide()
|
||||||
|
|
||||||
|
// Add file to listing (avoids full refresh)
|
||||||
|
lsData.Append(api.FileInfo{
|
||||||
|
IsDir: false,
|
||||||
|
Name: filepath.Base(remotePath),
|
||||||
|
})
|
||||||
|
}, w)
|
||||||
|
uploadDlg.Show()
|
||||||
|
}, w)
|
||||||
|
dlg.Show()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
widget.NewToolbarAction(
|
||||||
|
theme.FolderNewIcon(),
|
||||||
|
func() {
|
||||||
|
// Create new entry for filename
|
||||||
|
filenameEntry := widget.NewEntry()
|
||||||
|
// Create new dialog to ask for the filename
|
||||||
|
mkdirDialog := dialog.NewForm("Make Directory", "Create", "Cancel", []*widget.FormItem{
|
||||||
|
widget.NewFormItem("Filename", filenameEntry),
|
||||||
|
}, func(ok bool) {
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current directory
|
||||||
|
cwd, _ := cwdData.Get()
|
||||||
|
// Get remote path by joining current directory and filename
|
||||||
|
remotePath := filepath.Join(cwd, filenameEntry.Text)
|
||||||
|
|
||||||
|
// Make directory
|
||||||
|
err := client.Mkdir(ctx, remotePath)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error creating directory", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add directory to listing (avoids full refresh)
|
||||||
|
lsData.Append(api.FileInfo{
|
||||||
|
IsDir: true,
|
||||||
|
Name: filepath.Base(remotePath),
|
||||||
|
})
|
||||||
|
}, w)
|
||||||
|
mkdirDialog.Show()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add listener to listing data to create the new items on the GUI
|
||||||
|
// whenever the listing changes
|
||||||
|
lsData.AddListener(binding.NewDataListener(func() {
|
||||||
|
c.Objects = makeItems(ctx, client, lsData, cwdData, w, c)
|
||||||
|
c.Refresh()
|
||||||
|
}))
|
||||||
|
|
||||||
|
return container.NewBorder(
|
||||||
|
nil,
|
||||||
|
toolbar,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
container.NewVScroll(c),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeItems creates GUI objects from listing data
|
||||||
|
func makeItems(
|
||||||
|
ctx context.Context,
|
||||||
|
client *api.Client,
|
||||||
|
lsData binding.UntypedList,
|
||||||
|
cwdData binding.String,
|
||||||
|
w fyne.Window,
|
||||||
|
c *fyne.Container,
|
||||||
|
) []fyne.CanvasObject {
|
||||||
|
// Get listing data
|
||||||
|
ls, _ := lsData.Get()
|
||||||
|
|
||||||
|
// Create output slice with dame length as listing
|
||||||
|
out := make([]fyne.CanvasObject, len(ls))
|
||||||
|
for index, val := range ls {
|
||||||
|
// Assert value as file info
|
||||||
|
item := val.(api.FileInfo)
|
||||||
|
|
||||||
|
var icon fyne.Resource
|
||||||
|
// Decide which icon to use
|
||||||
|
if item.IsDir {
|
||||||
|
if item.Name == ".." {
|
||||||
|
icon = theme.NavigateBackIcon()
|
||||||
|
} else {
|
||||||
|
icon = theme.FolderIcon()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
icon = theme.FileIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new button with the decided icon and the item name
|
||||||
|
btn := widget.NewButtonWithIcon(item.Name, icon, nil)
|
||||||
|
// Align left
|
||||||
|
btn.Alignment = widget.ButtonAlignLeading
|
||||||
|
// Decide which callback function to use
|
||||||
|
if item.IsDir {
|
||||||
|
btn.OnTapped = func() {
|
||||||
|
// Get current directory
|
||||||
|
cwd, _ := cwdData.Get()
|
||||||
|
// Join current directory with item name
|
||||||
|
cwd = filepath.Join(cwd, item.Name)
|
||||||
|
// Set new current directory
|
||||||
|
cwdData.Set(cwd)
|
||||||
|
// Refresh GUI to display new directory
|
||||||
|
refresh(ctx, cwdData, lsData, client, w, c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
btn.OnTapped = func() {
|
||||||
|
// Get current directory
|
||||||
|
cwd, _ := cwdData.Get()
|
||||||
|
// Join current directory with item name
|
||||||
|
remotePath := filepath.Join(cwd, item.Name)
|
||||||
|
// Create new save dialog
|
||||||
|
dlg := dialog.NewFileSave(func(uc fyne.URIWriteCloser, err error) {
|
||||||
|
if err != nil || uc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get path of selected file
|
||||||
|
localPath := uc.URI().Path()
|
||||||
|
// Close WriteCloser (it's not needed)
|
||||||
|
uc.Close()
|
||||||
|
|
||||||
|
// Create new progress dialog
|
||||||
|
progressDlg := newProgress(w)
|
||||||
|
progressDlg.Show()
|
||||||
|
|
||||||
|
// Download file
|
||||||
|
progressCh, err := client.Download(ctx, localPath, remotePath)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error downloading file", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For every progress event
|
||||||
|
for progressEvt := range progressCh {
|
||||||
|
progressDlg.SetTotal(float64(progressEvt.Total))
|
||||||
|
progressDlg.SetValue(float64(progressEvt.Sent))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close progress dialog
|
||||||
|
progressDlg.Hide()
|
||||||
|
}, w)
|
||||||
|
// Set filename to the item name
|
||||||
|
dlg.SetFileName(item.Name)
|
||||||
|
dlg.Show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.Name == ".." {
|
||||||
|
out[index] = btn
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
moveBtn := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() {
|
||||||
|
moveEntry := widget.NewEntry()
|
||||||
|
dlg := dialog.NewForm("Move", "Move", "Cancel", []*widget.FormItem{
|
||||||
|
widget.NewFormItem("New Path", moveEntry),
|
||||||
|
}, func(ok bool) {
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current directory
|
||||||
|
cwd, _ := cwdData.Get()
|
||||||
|
// Join current directory with item name
|
||||||
|
oldPath := filepath.Join(cwd, item.Name)
|
||||||
|
|
||||||
|
// Rename file
|
||||||
|
err := client.Rename(ctx, oldPath, moveEntry.Text)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error renaming file", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh GUI
|
||||||
|
refresh(ctx, cwdData, lsData, client, w, c)
|
||||||
|
}, w)
|
||||||
|
dlg.Show()
|
||||||
|
})
|
||||||
|
|
||||||
|
removeBtn := widget.NewButtonWithIcon("", theme.DeleteIcon(), func() {
|
||||||
|
// Get current directory
|
||||||
|
cwd, _ := cwdData.Get()
|
||||||
|
// Join current directory with item name
|
||||||
|
path := filepath.Join(cwd, item.Name)
|
||||||
|
|
||||||
|
// Remove file
|
||||||
|
err := client.Remove(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error removing file", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh GUI
|
||||||
|
refresh(ctx, cwdData, lsData, client, w, c)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add button to GUI component list
|
||||||
|
out[index] = container.NewBorder(
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
container.NewHBox(moveBtn, removeBtn),
|
||||||
|
btn,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func refresh(
|
||||||
|
ctx context.Context,
|
||||||
|
cwdData binding.String,
|
||||||
|
lsData binding.UntypedList,
|
||||||
|
client *api.Client,
|
||||||
|
w fyne.Window,
|
||||||
|
c *fyne.Container,
|
||||||
|
) {
|
||||||
|
// Create and show new loading pop up
|
||||||
|
loading := newLoadingPopUp(w)
|
||||||
|
loading.Show()
|
||||||
|
// Close pop up at the end of the function
|
||||||
|
defer loading.Hide()
|
||||||
|
|
||||||
|
// Get current directory
|
||||||
|
cwd, _ := cwdData.Get()
|
||||||
|
// Read directory
|
||||||
|
ls, err := client.ReadDir(ctx, cwd)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error reading directory", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Set new listing data
|
||||||
|
lsData.Set(lsToAny(ls))
|
||||||
|
// Create new GUI objects
|
||||||
|
c.Objects = makeItems(ctx, client, lsData, cwdData, w, c)
|
||||||
|
// Refresh GUI
|
||||||
|
c.Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
func lsToAny(ls []api.FileInfo) []interface{} {
|
||||||
|
out := make([]interface{}, len(ls)-1)
|
||||||
|
for i, e := range ls {
|
||||||
|
// Skip first element as it is always "."
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[i-1] = e
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
150
cmd/itgui/graph.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"image/color"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/theme"
|
||||||
|
"fyne.io/x/fyne/widget/charts"
|
||||||
|
"go.arsenm.dev/itd/api"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func graphTab(ctx context.Context, client *api.Client, w fyne.Window) fyne.CanvasObject {
|
||||||
|
// Get user configuration directory
|
||||||
|
userCfgDir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cfgDir := filepath.Join(userCfgDir, "itd")
|
||||||
|
dbPath := filepath.Join(cfgDir, "metrics.db")
|
||||||
|
|
||||||
|
// If stat on database returns error, return nil
|
||||||
|
if _, err := os.Stat(dbPath); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open database
|
||||||
|
db, err := sql.Open("sqlite", dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get heart rate data and create chart
|
||||||
|
heartRateData := getData(db, "bpm", "heartRate")
|
||||||
|
heartRate := newLineChartData(nil, heartRateData)
|
||||||
|
|
||||||
|
// Get step count data and create chart
|
||||||
|
stepCountData := getData(db, "steps", "stepCount")
|
||||||
|
stepCount := newLineChartData(nil, stepCountData)
|
||||||
|
|
||||||
|
// Get battery level data and create chart
|
||||||
|
battLevelData := getData(db, "percent", "battLevel")
|
||||||
|
battLevel := newLineChartData(nil, battLevelData)
|
||||||
|
|
||||||
|
// Get motion data
|
||||||
|
motionData := getMotionData(db)
|
||||||
|
// Create chart for each coordinate
|
||||||
|
xChart := newLineChartData(theme.PrimaryColorNamed(theme.ColorRed), motionData["X"])
|
||||||
|
yChart := newLineChartData(theme.PrimaryColorNamed(theme.ColorGreen), motionData["Y"])
|
||||||
|
zChart := newLineChartData(theme.PrimaryColorNamed(theme.ColorBlue), motionData["Z"])
|
||||||
|
|
||||||
|
// Create new max container with all the charts
|
||||||
|
motion := container.NewMax(xChart, yChart, zChart)
|
||||||
|
|
||||||
|
// Create tabs for charts
|
||||||
|
chartTabs := container.NewAppTabs(
|
||||||
|
container.NewTabItem("Heart Rate", heartRate),
|
||||||
|
container.NewTabItem("Step Count", stepCount),
|
||||||
|
container.NewTabItem("Battery Level", battLevel),
|
||||||
|
container.NewTabItem("Motion", motion),
|
||||||
|
)
|
||||||
|
// Place tabs on left
|
||||||
|
chartTabs.SetTabLocation(container.TabLocationLeading)
|
||||||
|
return chartTabs
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLineChartData(col color.Color, data []float64) *charts.LineChart {
|
||||||
|
// Create new line chart
|
||||||
|
lc := charts.NewLineChart(nil)
|
||||||
|
setOpts(lc, col)
|
||||||
|
// If no data, make the stroke transparent
|
||||||
|
if len(data) == 0 {
|
||||||
|
lc.Options().StrokeColor = color.RGBA{0, 0, 0, 0}
|
||||||
|
}
|
||||||
|
// Set data
|
||||||
|
lc.SetData(data)
|
||||||
|
return lc
|
||||||
|
}
|
||||||
|
|
||||||
|
func setOpts(lc *charts.LineChart, col color.Color) {
|
||||||
|
// Get pointer to options
|
||||||
|
opts := lc.Options()
|
||||||
|
// Set fill color to transparent
|
||||||
|
opts.FillColor = color.RGBA{0, 0, 0, 0}
|
||||||
|
// Set stroke width
|
||||||
|
opts.StrokeWidth = 2
|
||||||
|
// If color provided
|
||||||
|
if col != nil {
|
||||||
|
// Set stroke color
|
||||||
|
opts.StrokeColor = col
|
||||||
|
} else {
|
||||||
|
// Set stroke color to orange primary color
|
||||||
|
opts.StrokeColor = theme.PrimaryColorNamed(theme.ColorOrange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getData(db *sql.DB, field, table string) []float64 {
|
||||||
|
// Get data from database
|
||||||
|
rows, err := db.Query("SELECT " + field + " FROM " + table + " ORDER BY time;")
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var out []float64
|
||||||
|
for rows.Next() {
|
||||||
|
var val int64
|
||||||
|
// Scan data into int
|
||||||
|
err := rows.Scan(&val)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to float64 and append to data slice
|
||||||
|
out = append(out, float64(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMotionData(db *sql.DB) map[string][]float64 {
|
||||||
|
// Get data from database
|
||||||
|
rows, err := db.Query("SELECT X, Y, Z FROM motion ORDER BY time;")
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
out := map[string][]float64{}
|
||||||
|
for rows.Next() {
|
||||||
|
var x, y, z int64
|
||||||
|
// Scan data into ints
|
||||||
|
err := rows.Scan(&x, &y, &z)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to float64 and append to appropriate slice
|
||||||
|
out["X"] = append(out["X"], float64(x))
|
||||||
|
out["Y"] = append(out["Y"], float64(y))
|
||||||
|
out["Z"] = append(out["Z"], float64(z))
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
@ -1,123 +1,86 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/theme"
|
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func infoTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
func infoTab(ctx context.Context, client *api.Client, w fyne.Window) fyne.CanvasObject {
|
||||||
infoLayout := container.NewVBox(
|
c := container.NewVBox()
|
||||||
// Add rectangle for a bit of padding
|
|
||||||
canvas.NewRectangle(color.Transparent),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create label for heart rate
|
// Create titled text for heart rate
|
||||||
heartRateLbl := newText("0 BPM", 24)
|
heartRateText := newTitledText("Heart Rate", "0 BPM")
|
||||||
// Creae container to store heart rate section
|
c.Add(heartRateText)
|
||||||
heartRateSect := container.NewVBox(
|
// Watch heart rate
|
||||||
newText("Heart Rate", 12),
|
heartRateCh, err := client.WatchHeartRate(ctx)
|
||||||
heartRateLbl,
|
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
|
||||||
)
|
|
||||||
infoLayout.Add(heartRateSect)
|
|
||||||
|
|
||||||
heartRateCh, cancel, err := client.WatchHeartRate()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error getting heart rate channel", true, parent)
|
guiErr(err, "Error watching heart rate", true, w)
|
||||||
}
|
}
|
||||||
onClose = append(onClose, cancel)
|
|
||||||
go func() {
|
go func() {
|
||||||
|
// For every heart rate sample
|
||||||
for heartRate := range heartRateCh {
|
for heartRate := range heartRateCh {
|
||||||
// Change text of heart rate label
|
// Set body of titled text
|
||||||
heartRateLbl.Text = fmt.Sprintf("%d BPM", heartRate)
|
heartRateText.SetBody(fmt.Sprintf("%d BPM", heartRate))
|
||||||
// Refresh label
|
|
||||||
heartRateLbl.Refresh()
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Create label for heart rate
|
// Create titled text for battery level
|
||||||
stepCountLbl := newText("0 Steps", 24)
|
battLevelText := newTitledText("Battery Level", "0%")
|
||||||
// Creae container to store heart rate section
|
c.Add(battLevelText)
|
||||||
stepCountSect := container.NewVBox(
|
// Watch battery level
|
||||||
newText("Step Count", 12),
|
battLevelCh, err := client.WatchBatteryLevel(ctx)
|
||||||
stepCountLbl,
|
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
|
||||||
)
|
|
||||||
infoLayout.Add(stepCountSect)
|
|
||||||
|
|
||||||
stepCountCh, cancel, err := client.WatchStepCount()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error getting step count channel", true, parent)
|
guiErr(err, "Error watching battery level", true, w)
|
||||||
}
|
}
|
||||||
onClose = append(onClose, cancel)
|
|
||||||
go func() {
|
|
||||||
for stepCount := range stepCountCh {
|
|
||||||
// Change text of heart rate label
|
|
||||||
stepCountLbl.Text = fmt.Sprintf("%d Steps", stepCount)
|
|
||||||
// Refresh label
|
|
||||||
stepCountLbl.Refresh()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create label for battery level
|
|
||||||
battLevelLbl := newText("0%", 24)
|
|
||||||
// Create container to store battery level section
|
|
||||||
battLevel := container.NewVBox(
|
|
||||||
newText("Battery Level", 12),
|
|
||||||
battLevelLbl,
|
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
|
||||||
)
|
|
||||||
infoLayout.Add(battLevel)
|
|
||||||
|
|
||||||
battLevelCh, cancel, err := client.WatchBatteryLevel()
|
|
||||||
if err != nil {
|
|
||||||
guiErr(err, "Error getting battery level channel", true, parent)
|
|
||||||
}
|
|
||||||
onClose = append(onClose, cancel)
|
|
||||||
go func() {
|
go func() {
|
||||||
|
// For every battery level sample
|
||||||
for battLevel := range battLevelCh {
|
for battLevel := range battLevelCh {
|
||||||
// Change text of battery level label
|
// Set body of titled text
|
||||||
battLevelLbl.Text = fmt.Sprintf("%d%%", battLevel)
|
battLevelText.SetBody(fmt.Sprintf("%d%%", battLevel))
|
||||||
// Refresh label
|
|
||||||
battLevelLbl.Refresh()
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fwVerString, err := client.Version()
|
// Create titled text for step count
|
||||||
|
stepCountText := newTitledText("Step Count", "0 Steps")
|
||||||
|
c.Add(stepCountText)
|
||||||
|
// Watch step count
|
||||||
|
stepCountCh, err := client.WatchStepCount(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error getting firmware string", true, parent)
|
guiErr(err, "Error watching step count", true, w)
|
||||||
}
|
}
|
||||||
|
go func() {
|
||||||
|
// For every step count sample
|
||||||
|
for stepCount := range stepCountCh {
|
||||||
|
// Set body of titled text
|
||||||
|
stepCountText.SetBody(fmt.Sprintf("%d Steps", stepCount))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
fwVer := container.NewVBox(
|
// Create new titled text for address
|
||||||
newText("Firmware Version", 12),
|
addressText := newTitledText("Address", "")
|
||||||
newText(fwVerString, 24),
|
c.Add(addressText)
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
// Get address
|
||||||
)
|
address, err := client.Address(ctx)
|
||||||
infoLayout.Add(fwVer)
|
|
||||||
|
|
||||||
btAddrString, err := client.Address()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
guiErr(err, "Error getting address", true, w)
|
||||||
}
|
}
|
||||||
|
// Set body of titled text
|
||||||
|
addressText.SetBody(address)
|
||||||
|
|
||||||
btAddr := container.NewVBox(
|
// Create new titled text for version
|
||||||
newText("Bluetooth Address", 12),
|
versionText := newTitledText("Version", "")
|
||||||
newText(btAddrString, 24),
|
c.Add(versionText)
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
// Get version
|
||||||
)
|
version, err := client.Version(ctx)
|
||||||
infoLayout.Add(btAddr)
|
if err != nil {
|
||||||
|
guiErr(err, "Error getting version", true, w)
|
||||||
|
}
|
||||||
|
// Set body of titled text
|
||||||
|
versionText.SetBody(version)
|
||||||
|
|
||||||
return infoLayout
|
return container.NewVScroll(c)
|
||||||
}
|
|
||||||
|
|
||||||
func newText(t string, size float32) *canvas.Text {
|
|
||||||
text := canvas.NewText(t, theme.ForegroundColor())
|
|
||||||
text.TextSize = size
|
|
||||||
return text
|
|
||||||
}
|
}
|
||||||
|
21
cmd/itgui/loading.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/canvas"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newLoadingPopUp(w fyne.Window) *widget.PopUp {
|
||||||
|
pb := widget.NewProgressBarInfinite()
|
||||||
|
rect := canvas.NewRectangle(color.Transparent)
|
||||||
|
rect.SetMinSize(fyne.NewSize(200, 0))
|
||||||
|
|
||||||
|
return widget.NewModalPopUp(
|
||||||
|
container.NewMax(rect, pb),
|
||||||
|
w.Canvas(),
|
||||||
|
)
|
||||||
|
}
|
@ -1,43 +1,60 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var onClose []func()
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create new app
|
|
||||||
a := app.New()
|
a := app.New()
|
||||||
// Create new window with title "itgui"
|
w := a.NewWindow("itgui")
|
||||||
window := a.NewWindow("itgui")
|
|
||||||
window.SetOnClosed(func() {
|
|
||||||
for _, closeFn := range onClose {
|
|
||||||
closeFn()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// Create new context for use with the API client
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// Connect to ITD API
|
||||||
client, err := api.New(api.DefaultAddr)
|
client, err := api.New(api.DefaultAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error connecting to itd", true, window)
|
guiErr(err, "Error connecting to ITD", true, w)
|
||||||
}
|
}
|
||||||
onClose = append(onClose, func() {
|
|
||||||
client.Close()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create new app tabs container
|
// Create channel to signal that the fs tab has been opened
|
||||||
|
fsOpened := make(chan struct{})
|
||||||
|
fsOnce := &sync.Once{}
|
||||||
|
|
||||||
|
// Create app tabs
|
||||||
tabs := container.NewAppTabs(
|
tabs := container.NewAppTabs(
|
||||||
container.NewTabItem("Info", infoTab(window, client)),
|
container.NewTabItem("Info", infoTab(ctx, client, w)),
|
||||||
container.NewTabItem("Motion", motionTab(window, client)),
|
container.NewTabItem("Motion", motionTab(ctx, client, w)),
|
||||||
container.NewTabItem("Notify", notifyTab(window, client)),
|
container.NewTabItem("Notify", notifyTab(ctx, client, w)),
|
||||||
container.NewTabItem("Set Time", timeTab(window, client)),
|
container.NewTabItem("FS", fsTab(ctx, client, w, fsOpened)),
|
||||||
container.NewTabItem("Upgrade", upgradeTab(window, client)),
|
container.NewTabItem("Time", timeTab(ctx, client, w)),
|
||||||
|
container.NewTabItem("Firmware", firmwareTab(ctx, client, w)),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set tabs as window content
|
metricsTab := graphTab(ctx, client, w)
|
||||||
window.SetContent(tabs)
|
if metricsTab != nil {
|
||||||
// Show window and run app
|
tabs.Append(container.NewTabItem("Metrics", metricsTab))
|
||||||
window.ShowAndRun()
|
}
|
||||||
|
|
||||||
|
// When a tab is selected
|
||||||
|
tabs.OnSelected = func(ti *container.TabItem) {
|
||||||
|
// If the tab's name is FS
|
||||||
|
if ti.Text == "FS" {
|
||||||
|
// Signal fsOpened only once
|
||||||
|
fsOnce.Do(func() {
|
||||||
|
fsOpened <- struct{}{}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel context on close
|
||||||
|
w.SetOnClosed(cancel)
|
||||||
|
// Set content and show window
|
||||||
|
w.SetContent(tabs)
|
||||||
|
w.ShowAndRun()
|
||||||
}
|
}
|
||||||
|
@ -1,105 +1,62 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"context"
|
||||||
"strconv"
|
"fmt"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/theme"
|
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func motionTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
func motionTab(ctx context.Context, client *api.Client, w fyne.Window) fyne.CanvasObject {
|
||||||
// Create label for heart rate
|
// Create titledText for each coordinate
|
||||||
xCoordLbl := newText("0", 24)
|
xText := newTitledText("X Coordinate", "0")
|
||||||
// Creae container to store heart rate section
|
yText := newTitledText("Y Coordinate", "0")
|
||||||
xCoordSect := container.NewVBox(
|
zText := newTitledText("Z Coordinate", "0")
|
||||||
newText("X Coordinate", 12),
|
|
||||||
xCoordLbl,
|
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create label for heart rate
|
var ctxCancel func()
|
||||||
yCoordLbl := newText("0", 24)
|
|
||||||
// Creae container to store heart rate section
|
|
||||||
yCoordSect := container.NewVBox(
|
|
||||||
newText("Y Coordinate", 12),
|
|
||||||
yCoordLbl,
|
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
|
||||||
)
|
|
||||||
// Create label for heart rate
|
|
||||||
zCoordLbl := newText("0", 24)
|
|
||||||
// Creae container to store heart rate section
|
|
||||||
zCoordSect := container.NewVBox(
|
|
||||||
newText("Z Coordinate", 12),
|
|
||||||
zCoordLbl,
|
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create variable to keep track of whether motion started
|
// Create start button
|
||||||
started := false
|
toggleBtn := widget.NewButton("Start", nil)
|
||||||
|
// Set button's on tapped callback
|
||||||
// Create button to stop motion
|
toggleBtn.OnTapped = func() {
|
||||||
stopBtn := widget.NewButton("Stop", nil)
|
switch toggleBtn.Text {
|
||||||
// Create button to start motion
|
case "Start":
|
||||||
startBtn := widget.NewButton("Start", func() {
|
// Create new context for motion
|
||||||
// if motion is started
|
motionCtx, cancel := context.WithCancel(ctx)
|
||||||
if started {
|
// Set ctxCancel to function so that stop button can run it
|
||||||
// Do nothing
|
ctxCancel = cancel
|
||||||
return
|
// Watch motion
|
||||||
}
|
motionCh, err := client.WatchMotion(motionCtx)
|
||||||
// Set motion started
|
if err != nil {
|
||||||
started = true
|
guiErr(err, "Error watching motion", false, w)
|
||||||
// Watch motion values
|
return
|
||||||
motionCh, cancel, err := client.WatchMotion()
|
|
||||||
if err != nil {
|
|
||||||
guiErr(err, "Error getting heart rate channel", true, parent)
|
|
||||||
}
|
|
||||||
// Create done channel
|
|
||||||
done := make(chan struct{}, 1)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
case motion := <-motionCh:
|
|
||||||
// Set labels to new values
|
|
||||||
xCoordLbl.Text = strconv.Itoa(int(motion.X))
|
|
||||||
yCoordLbl.Text = strconv.Itoa(int(motion.Y))
|
|
||||||
zCoordLbl.Text = strconv.Itoa(int(motion.Z))
|
|
||||||
// Refresh labels to display new values
|
|
||||||
xCoordLbl.Refresh()
|
|
||||||
yCoordLbl.Refresh()
|
|
||||||
zCoordLbl.Refresh()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
go func() {
|
||||||
// Create stop function
|
// For every motion event
|
||||||
stopBtn.OnTapped = func() {
|
for motion := range motionCh {
|
||||||
done <- struct{}{}
|
// Set coordinates
|
||||||
started = false
|
xText.SetBody(fmt.Sprint(motion.X))
|
||||||
cancel()
|
yText.SetBody(fmt.Sprint(motion.Y))
|
||||||
|
zText.SetBody(fmt.Sprint(motion.Z))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Set button text to "Stop"
|
||||||
|
toggleBtn.SetText("Stop")
|
||||||
|
case "Stop":
|
||||||
|
// Cancel motion context
|
||||||
|
ctxCancel()
|
||||||
|
// Set button text to "Start"
|
||||||
|
toggleBtn.SetText("Start")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
})
|
return container.NewVScroll(container.NewVBox(
|
||||||
// Run stop button function on close if possible
|
toggleBtn,
|
||||||
onClose = append(onClose, func() {
|
xText,
|
||||||
if stopBtn.OnTapped != nil {
|
yText,
|
||||||
stopBtn.OnTapped()
|
zText,
|
||||||
}
|
))
|
||||||
})
|
|
||||||
|
|
||||||
// Return new container containing all elements
|
|
||||||
return container.NewVBox(
|
|
||||||
// Add rectangle for a bit of padding
|
|
||||||
canvas.NewRectangle(color.Transparent),
|
|
||||||
startBtn,
|
|
||||||
stopBtn,
|
|
||||||
xCoordSect,
|
|
||||||
yCoordSect,
|
|
||||||
zCoordSect,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/layout"
|
"fyne.io/fyne/v2/layout"
|
||||||
@ -8,30 +10,31 @@ import (
|
|||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func notifyTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
func notifyTab(ctx context.Context, client *api.Client, w fyne.Window) fyne.CanvasObject {
|
||||||
// Create new entry for notification title
|
c := container.NewVBox()
|
||||||
|
c.Add(layout.NewSpacer())
|
||||||
|
|
||||||
|
// Create new entry for title
|
||||||
titleEntry := widget.NewEntry()
|
titleEntry := widget.NewEntry()
|
||||||
titleEntry.SetPlaceHolder("Title")
|
titleEntry.SetPlaceHolder("Title")
|
||||||
|
c.Add(titleEntry)
|
||||||
|
|
||||||
// Create multiline entry for notification body
|
// Create new multiline entry for body
|
||||||
bodyEntry := widget.NewMultiLineEntry()
|
bodyEntry := widget.NewMultiLineEntry()
|
||||||
bodyEntry.SetPlaceHolder("Body")
|
bodyEntry.SetPlaceHolder("Body")
|
||||||
|
c.Add(bodyEntry)
|
||||||
|
|
||||||
// Create new button to send notification
|
// Create new send button
|
||||||
sendBtn := widget.NewButton("Send", func() {
|
sendBtn := widget.NewButton("Send", func() {
|
||||||
err := client.Notify(titleEntry.Text, bodyEntry.Text)
|
// Send notification
|
||||||
|
err := client.Notify(ctx, titleEntry.Text, bodyEntry.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error sending notification", false, parent)
|
guiErr(err, "Error sending notification", false, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
c.Add(sendBtn)
|
||||||
|
|
||||||
// Return new container containing all elements
|
c.Add(layout.NewSpacer())
|
||||||
return container.NewVBox(
|
return container.NewVScroll(c)
|
||||||
layout.NewSpacer(),
|
|
||||||
titleEntry,
|
|
||||||
bodyEntry,
|
|
||||||
sendBtn,
|
|
||||||
layout.NewSpacer(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
64
cmd/itgui/progress.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/canvas"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
type progress struct {
|
||||||
|
lbl *widget.Label
|
||||||
|
progLbl *widget.Label
|
||||||
|
pb *widget.ProgressBar
|
||||||
|
*widget.PopUp
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProgress(w fyne.Window) progress {
|
||||||
|
out := progress{}
|
||||||
|
|
||||||
|
out.lbl = widget.NewLabel("")
|
||||||
|
out.lbl.Hide()
|
||||||
|
|
||||||
|
// Create label to show how many bytes transfered and center it
|
||||||
|
out.progLbl = widget.NewLabel("0 / 0 B")
|
||||||
|
out.progLbl.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
|
// Create new progress bar
|
||||||
|
out.pb = widget.NewProgressBar()
|
||||||
|
|
||||||
|
// Create new rectangle to set the size of the popup
|
||||||
|
sizeRect := canvas.NewRectangle(color.Transparent)
|
||||||
|
sizeRect.SetMinSize(fyne.NewSize(300, 50))
|
||||||
|
|
||||||
|
// Create vbox for label and progress bar
|
||||||
|
l := container.NewVBox(out.lbl, out.progLbl, out.pb)
|
||||||
|
// Create popup
|
||||||
|
out.PopUp = widget.NewModalPopUp(container.NewMax(l, sizeRect), w.Canvas())
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p progress) SetText(s string) {
|
||||||
|
p.lbl.SetText(s)
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
p.lbl.Hide()
|
||||||
|
} else {
|
||||||
|
p.lbl.Show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p progress) SetTotal(v float64) {
|
||||||
|
p.pb.Max = v
|
||||||
|
p.pb.Refresh()
|
||||||
|
p.progLbl.SetText(fmt.Sprintf("%.0f / %.0f B", p.pb.Value, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p progress) SetValue(v float64) {
|
||||||
|
p.pb.SetValue(v)
|
||||||
|
p.progLbl.SetText(fmt.Sprintf("%.0f / %.0f B", v, p.pb.Max))
|
||||||
|
}
|
BIN
cmd/itgui/screenshots/firmware.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
cmd/itgui/screenshots/fs.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
cmd/itgui/screenshots/info.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
cmd/itgui/screenshots/metrics.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
cmd/itgui/screenshots/mkdir.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
cmd/itgui/screenshots/motion.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
cmd/itgui/screenshots/notify.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
cmd/itgui/screenshots/progress.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
cmd/itgui/screenshots/resources.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
cmd/itgui/screenshots/time.png
Normal file
After Width: | Height: | Size: 16 KiB |
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@ -10,51 +11,47 @@ import (
|
|||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func timeTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
func timeTab(ctx context.Context, client *api.Client, w fyne.Window) fyne.CanvasObject {
|
||||||
// Create new entry for time string
|
c := container.NewVBox()
|
||||||
|
c.Add(layout.NewSpacer())
|
||||||
|
|
||||||
|
// Create entry for time string
|
||||||
timeEntry := widget.NewEntry()
|
timeEntry := widget.NewEntry()
|
||||||
// Set text to current time formatter properly
|
|
||||||
timeEntry.SetText(time.Now().Format(time.RFC1123))
|
timeEntry.SetText(time.Now().Format(time.RFC1123))
|
||||||
|
timeEntry.SetPlaceHolder("RFC1123")
|
||||||
|
|
||||||
// Create button to set current time
|
// Create button to set current time
|
||||||
currentBtn := widget.NewButton("Set Current", func() {
|
setCurrentBtn := widget.NewButton("Set current time", func() {
|
||||||
timeEntry.SetText(time.Now().Format(time.RFC1123))
|
// Set current time
|
||||||
setTime(client, true)
|
err := client.SetTime(ctx, time.Now())
|
||||||
})
|
|
||||||
|
|
||||||
// Create button to set time inside entry
|
|
||||||
timeBtn := widget.NewButton("Set", func() {
|
|
||||||
// Parse time as RFC1123 string
|
|
||||||
parsedTime, err := time.Parse(time.RFC1123, timeEntry.Text)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error parsing time string", false, parent)
|
guiErr(err, "Error setting time", false, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Set time to parsed time
|
// Set time entry to current time
|
||||||
setTime(client, false, parsedTime)
|
timeEntry.SetText(time.Now().Format(time.RFC1123))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Return new container with all elements centered
|
// Create button to set time from entry
|
||||||
return container.NewVBox(
|
setBtn := widget.NewButton("Set", func() {
|
||||||
layout.NewSpacer(),
|
// Parse RFC1123 time string in entry
|
||||||
timeEntry,
|
newTime, err := time.Parse(time.RFC1123, timeEntry.Text)
|
||||||
currentBtn,
|
if err != nil {
|
||||||
timeBtn,
|
guiErr(err, "Error parsing time string", false, w)
|
||||||
layout.NewSpacer(),
|
return
|
||||||
)
|
}
|
||||||
}
|
// Set time from parsed string
|
||||||
|
err = client.SetTime(ctx, newTime)
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error setting time", false, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// setTime sets the first element in the variadic parameter
|
c.Add(timeEntry)
|
||||||
// if current is false, otherwise, it sets the current time.
|
c.Add(setBtn)
|
||||||
func setTime(client *api.Client, current bool, t ...time.Time) error {
|
c.Add(setCurrentBtn)
|
||||||
var err error
|
|
||||||
if current {
|
c.Add(layout.NewSpacer())
|
||||||
err = client.SetTimeNow()
|
return c
|
||||||
} else {
|
|
||||||
err = client.SetTime(t[0])
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
35
cmd/itgui/titledText.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fyne.io/fyne/v2/widget"
|
||||||
|
|
||||||
|
type titledText struct {
|
||||||
|
*widget.RichText
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTitledText(title, text string) titledText {
|
||||||
|
titleStyle := widget.RichTextStyleHeading
|
||||||
|
titleStyle.TextStyle.Bold = false
|
||||||
|
return titledText{
|
||||||
|
widget.NewRichText(
|
||||||
|
&widget.TextSegment{
|
||||||
|
Style: widget.RichTextStyleParagraph,
|
||||||
|
Text: title,
|
||||||
|
},
|
||||||
|
&widget.TextSegment{
|
||||||
|
Style: titleStyle,
|
||||||
|
Text: text,
|
||||||
|
},
|
||||||
|
&widget.SeparatorSegment{},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t titledText) SetTitle(s string) {
|
||||||
|
t.RichText.Segments[0].(*widget.TextSegment).Text = s
|
||||||
|
t.Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t titledText) SetBody(s string) {
|
||||||
|
t.RichText.Segments[1].(*widget.TextSegment).Text = s
|
||||||
|
t.Refresh()
|
||||||
|
}
|
@ -1,181 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
|
||||||
"fyne.io/fyne/v2/container"
|
|
||||||
"fyne.io/fyne/v2/dialog"
|
|
||||||
"fyne.io/fyne/v2/layout"
|
|
||||||
"fyne.io/fyne/v2/storage"
|
|
||||||
"fyne.io/fyne/v2/widget"
|
|
||||||
"go.arsenm.dev/itd/api"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func upgradeTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
|
||||||
var (
|
|
||||||
archivePath string
|
|
||||||
firmwarePath string
|
|
||||||
initPktPath string
|
|
||||||
)
|
|
||||||
|
|
||||||
var archiveBtn *widget.Button
|
|
||||||
// Create archive selection dialog
|
|
||||||
archiveDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
|
||||||
if e != nil || uc == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uc.Close()
|
|
||||||
archivePath = uc.URI().Path()
|
|
||||||
archiveBtn.SetText(fmt.Sprintf("Select archive (.zip) [%s]", filepath.Base(archivePath)))
|
|
||||||
}, parent)
|
|
||||||
// Limit dialog to .zip files
|
|
||||||
archiveDialog.SetFilter(storage.NewExtensionFileFilter([]string{".zip"}))
|
|
||||||
// Create button to show dialog
|
|
||||||
archiveBtn = widget.NewButton("Select archive (.zip)", archiveDialog.Show)
|
|
||||||
|
|
||||||
var firmwareBtn *widget.Button
|
|
||||||
// Create firmware selection dialog
|
|
||||||
firmwareDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
|
||||||
if e != nil || uc == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uc.Close()
|
|
||||||
firmwarePath = uc.URI().Path()
|
|
||||||
firmwareBtn.SetText(fmt.Sprintf("Select firmware (.bin) [%s]", filepath.Base(firmwarePath)))
|
|
||||||
}, parent)
|
|
||||||
// Limit dialog to .bin files
|
|
||||||
firmwareDialog.SetFilter(storage.NewExtensionFileFilter([]string{".bin"}))
|
|
||||||
// Create button to show dialog
|
|
||||||
firmwareBtn = widget.NewButton("Select firmware (.bin)", firmwareDialog.Show)
|
|
||||||
|
|
||||||
var initPktBtn *widget.Button
|
|
||||||
// Create init packet selection dialog
|
|
||||||
initPktDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
|
||||||
if e != nil || uc == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uc.Close()
|
|
||||||
initPktPath = uc.URI().Path()
|
|
||||||
initPktBtn.SetText(fmt.Sprintf("Select init packet (.dat) [%s]", filepath.Base(initPktPath)))
|
|
||||||
}, parent)
|
|
||||||
// Limit dialog to .dat files
|
|
||||||
initPktDialog.SetFilter(storage.NewExtensionFileFilter([]string{".dat"}))
|
|
||||||
// Create button to show dialog
|
|
||||||
initPktBtn = widget.NewButton("Select init packet (.dat)", initPktDialog.Show)
|
|
||||||
|
|
||||||
// Hide init packet and firmware buttons
|
|
||||||
initPktBtn.Hide()
|
|
||||||
firmwareBtn.Hide()
|
|
||||||
|
|
||||||
// Create dropdown to select upgrade type
|
|
||||||
upgradeTypeSelect := widget.NewSelect([]string{
|
|
||||||
"Archive",
|
|
||||||
"Files",
|
|
||||||
}, func(s string) {
|
|
||||||
// Hide all buttons
|
|
||||||
archiveBtn.Hide()
|
|
||||||
initPktBtn.Hide()
|
|
||||||
firmwareBtn.Hide()
|
|
||||||
// Unhide appropriate button(s)
|
|
||||||
switch s {
|
|
||||||
case "Archive":
|
|
||||||
archiveBtn.Show()
|
|
||||||
case "Files":
|
|
||||||
initPktBtn.Show()
|
|
||||||
firmwareBtn.Show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// Select first elemetn
|
|
||||||
upgradeTypeSelect.SetSelectedIndex(0)
|
|
||||||
|
|
||||||
// Create new button to start DFU
|
|
||||||
startBtn := widget.NewButton("Start", func() {
|
|
||||||
// If archive path does not exist and both init packet and firmware paths
|
|
||||||
// also do not exist, return error
|
|
||||||
if archivePath == "" && (initPktPath == "" && firmwarePath == "") {
|
|
||||||
guiErr(nil, "Upgrade requires archive or files selected", false, parent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new label for byte progress
|
|
||||||
progressLbl := widget.NewLabelWithStyle("0 / 0 B", fyne.TextAlignCenter, fyne.TextStyle{})
|
|
||||||
// Create new progress bar
|
|
||||||
progressBar := widget.NewProgressBar()
|
|
||||||
// Create modal dialog containing label and progress bar
|
|
||||||
progressDlg := widget.NewModalPopUp(container.NewVBox(
|
|
||||||
layout.NewSpacer(),
|
|
||||||
progressLbl,
|
|
||||||
progressBar,
|
|
||||||
layout.NewSpacer(),
|
|
||||||
), parent.Canvas())
|
|
||||||
// Resize modal to 300x100
|
|
||||||
progressDlg.Resize(fyne.NewSize(300, 100))
|
|
||||||
|
|
||||||
var fwUpgType api.UpgradeType
|
|
||||||
var files []string
|
|
||||||
// Get appropriate upgrade type and file paths
|
|
||||||
switch upgradeTypeSelect.Selected {
|
|
||||||
case "Archive":
|
|
||||||
fwUpgType = types.UpgradeTypeArchive
|
|
||||||
files = append(files, archivePath)
|
|
||||||
case "Files":
|
|
||||||
fwUpgType = types.UpgradeTypeFiles
|
|
||||||
files = append(files, initPktPath, firmwarePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
progress, err := client.FirmwareUpgrade(fwUpgType, files...)
|
|
||||||
if err != nil {
|
|
||||||
guiErr(err, "Error initiating DFU", false, parent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show progress dialog
|
|
||||||
progressDlg.Show()
|
|
||||||
|
|
||||||
for event := range progress {
|
|
||||||
// Set label text to received / total B
|
|
||||||
progressLbl.SetText(fmt.Sprintf("%d / %d B", event.Received, event.Total))
|
|
||||||
// Set progress bar values
|
|
||||||
progressBar.Max = float64(event.Total)
|
|
||||||
progressBar.Value = float64(event.Received)
|
|
||||||
// Refresh progress bar
|
|
||||||
progressBar.Refresh()
|
|
||||||
// If transfer finished, break
|
|
||||||
if event.Sent == event.Total {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide progress dialog after completion
|
|
||||||
progressDlg.Hide()
|
|
||||||
|
|
||||||
// Reset screen to default
|
|
||||||
upgradeTypeSelect.SetSelectedIndex(0)
|
|
||||||
firmwareBtn.SetText("Select firmware (.bin)")
|
|
||||||
initPktBtn.SetText("Select init packet (.dat)")
|
|
||||||
archiveBtn.SetText("Select archive (.zip)")
|
|
||||||
firmwarePath = ""
|
|
||||||
initPktPath = ""
|
|
||||||
archivePath = ""
|
|
||||||
|
|
||||||
dialog.NewInformation(
|
|
||||||
"Upgrade Complete",
|
|
||||||
"The firmware was transferred successfully.\nRemember to validate the firmware in InfiniTime settings.",
|
|
||||||
parent,
|
|
||||||
).Show()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Return container containing all elements
|
|
||||||
return container.NewVBox(
|
|
||||||
layout.NewSpacer(),
|
|
||||||
upgradeTypeSelect,
|
|
||||||
archiveBtn,
|
|
||||||
firmwareBtn,
|
|
||||||
initPktBtn,
|
|
||||||
startBtn,
|
|
||||||
layout.NewSpacer(),
|
|
||||||
)
|
|
||||||
}
|
|
31
config.go
@ -13,22 +13,47 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var cfgDir string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Set up logger
|
// Set up logger
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
|
||||||
// Get user's configuration directory
|
// Get user's configuration directory
|
||||||
cfgDir, err := os.UserConfigDir()
|
userCfgDir, err := os.UserConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
cfgDir = filepath.Join(userCfgDir, "itd")
|
||||||
|
|
||||||
|
// If config dir is not readable
|
||||||
|
if _, err = os.ReadDir(cfgDir); err != nil {
|
||||||
|
// Create config dir with 700 permissions
|
||||||
|
err = os.MkdirAll(cfgDir, 0700)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current and old config paths
|
||||||
|
cfgPath := filepath.Join(cfgDir, "itd.toml")
|
||||||
|
oldCfgPath := filepath.Join(userCfgDir, "itd.toml")
|
||||||
|
|
||||||
|
// If old config path exists
|
||||||
|
if _, err = os.Stat(oldCfgPath); err == nil {
|
||||||
|
// Move old config to new path
|
||||||
|
err = os.Rename(oldCfgPath, cfgPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set config defaults
|
// Set config defaults
|
||||||
setCfgDefaults()
|
setCfgDefaults()
|
||||||
|
|
||||||
// Load config files
|
// Load config files
|
||||||
etcProvider := file.Provider("/etc/itd.toml")
|
etcProvider := file.Provider("/etc/itd.toml")
|
||||||
cfgProvider := file.Provider(filepath.Join(cfgDir, "itd.toml"))
|
cfgProvider := file.Provider(cfgPath)
|
||||||
k.Load(etcProvider, toml.Parser())
|
k.Load(etcProvider, toml.Parser())
|
||||||
k.Load(cfgProvider, toml.Parser())
|
k.Load(cfgProvider, toml.Parser())
|
||||||
|
|
||||||
@ -55,6 +80,8 @@ func cfgWatch(provider *file.File) {
|
|||||||
|
|
||||||
func setCfgDefaults() {
|
func setCfgDefaults() {
|
||||||
k.Load(confmap.Provider(map[string]interface{}{
|
k.Load(confmap.Provider(map[string]interface{}{
|
||||||
|
"bluetooth.adapter": "hci0",
|
||||||
|
|
||||||
"socket.path": "/tmp/itd/socket",
|
"socket.path": "/tmp/itd/socket",
|
||||||
|
|
||||||
"conn.reconnect": true,
|
"conn.reconnect": true,
|
||||||
|
14
dbus.go
@ -1,10 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/godbus/dbus/v5"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
func newSystemBusConn() (*dbus.Conn, error) {
|
"github.com/godbus/dbus/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newSystemBusConn(ctx context.Context) (*dbus.Conn, error) {
|
||||||
// Connect to dbus session bus
|
// Connect to dbus session bus
|
||||||
conn, err := dbus.SystemBusPrivate()
|
conn, err := dbus.SystemBusPrivate(dbus.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -19,9 +23,9 @@ func newSystemBusConn() (*dbus.Conn, error) {
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSessionBusConn() (*dbus.Conn, error) {
|
func newSessionBusConn(ctx context.Context) (*dbus.Conn, error) {
|
||||||
// Connect to dbus session bus
|
// Connect to dbus session bus
|
||||||
conn, err := dbus.SessionBusPrivate()
|
conn, err := dbus.SessionBusPrivate(dbus.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
98
go.mod
@ -1,37 +1,83 @@
|
|||||||
module go.arsenm.dev/itd
|
module go.arsenm.dev/itd
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
|
replace fyne.io/x/fyne => github.com/metal3d/fyne-x v0.0.0-20220508095732-177117e583fb
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/fyne/v2 v2.1.2
|
fyne.io/fyne/v2 v2.2.3
|
||||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
fyne.io/x/fyne v0.0.0-20220107050838-c4a1de51d4ce
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8
|
github.com/cheggaaa/pb/v3 v3.0.8
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
|
||||||
github.com/fatih/color v1.13.0 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
|
||||||
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b
|
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211204153444-caad923f49f4 // indirect
|
|
||||||
github.com/godbus/dbus/v5 v5.0.6
|
|
||||||
github.com/google/uuid v1.3.0
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
|
|
||||||
github.com/knadh/koanf v1.4.0
|
github.com/knadh/koanf v1.4.0
|
||||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.14
|
github.com/mattn/go-isatty v0.0.14
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.4.3
|
|
||||||
github.com/mozillazg/go-pinyin v0.19.0
|
github.com/mozillazg/go-pinyin v0.19.0
|
||||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
github.com/rs/zerolog v1.26.1
|
||||||
github.com/rs/zerolog v1.26.0
|
github.com/urfave/cli/v2 v2.4.0
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
go.arsenm.dev/infinitime v0.0.0-20221107042015-72b558707ee3
|
||||||
github.com/srwiley/oksvg v0.0.0-20211120171407-1837d6608d8c // indirect
|
go.arsenm.dev/lrpc v0.0.0-20220513001344-3bcc01fdb6a0
|
||||||
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 // indirect
|
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
|
||||||
github.com/yuin/goldmark v1.4.4 // indirect
|
|
||||||
go.arsenm.dev/infinitime v0.0.0-20220416112421-b7a50271bece
|
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
|
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
modernc.org/sqlite v1.17.2
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 // indirect
|
||||||
|
github.com/VividCortex/ewma v1.1.1 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fatih/color v1.10.0 // indirect
|
||||||
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
|
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||||
|
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
|
||||||
|
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 // indirect
|
||||||
|
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect
|
||||||
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec // indirect
|
||||||
|
github.com/gofrs/uuid v4.2.0+incompatible // indirect
|
||||||
|
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||||
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 // indirect
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41 // indirect
|
||||||
|
github.com/stretchr/testify v1.7.2 // indirect
|
||||||
|
github.com/tevino/abool v1.2.0 // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
|
github.com/yuin/goldmark v1.4.10 // indirect
|
||||||
|
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd // indirect
|
||||||
|
golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee // indirect
|
||||||
|
golang.org/x/mod v0.4.2 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||||
|
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect
|
||||||
|
lukechampine.com/uint128 v1.1.1 // indirect
|
||||||
|
modernc.org/cc/v3 v3.36.0 // indirect
|
||||||
|
modernc.org/ccgo/v3 v3.16.6 // indirect
|
||||||
|
modernc.org/libc v1.16.7 // indirect
|
||||||
|
modernc.org/mathutil v1.4.1 // indirect
|
||||||
|
modernc.org/memory v1.1.1 // indirect
|
||||||
|
modernc.org/opt v0.1.1 // indirect
|
||||||
|
modernc.org/strutil v1.1.1 // indirect
|
||||||
|
modernc.org/token v1.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
644
go.sum
@ -1,13 +1,59 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
fyne.io/fyne/v2 v2.1.2 h1:avp9CvLAUdvE7fDMtH1tVKyjxEWHWcpow6aI6L7Kvvw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
fyne.io/fyne/v2 v2.1.2/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||||
|
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||||
|
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||||
|
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||||
|
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
fyne.io/fyne/v2 v2.1.0/go.mod h1:c1vwI38Ebd0dAdxVa6H1Pj6/+cK1xtDy61+I31g+s14=
|
||||||
|
fyne.io/fyne/v2 v2.2.3 h1:Umi3vVVW8XnWWPJmMkhIWQOMU/jxB1OqpWVUmjhODD0=
|
||||||
|
fyne.io/fyne/v2 v2.2.3/go.mod h1:MBoGuHzLLSXdQOWFAwWhIhYTEMp33zqtGCReSWhaQTA=
|
||||||
|
fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 h1:V2IC9t0Zj9Ur6qDbfhUuzVmIvXKFyxZXRJyigUvovs4=
|
||||||
|
fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
|
||||||
|
github.com/Andrew-M-C/go.jsonvalue v1.1.2-0.20211223013816-e873b56b4a84/go.mod h1:oTJGG91FhtsxvUFVwHSvr6zuaTcAuroj/ToxfT7Ox8U=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
||||||
|
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
|
||||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
|
||||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
|
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
||||||
@ -21,99 +67,224 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+
|
|||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
|
||||||
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA=
|
github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA=
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA=
|
github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
|
||||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||||
|
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4=
|
||||||
|
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg=
|
||||||
|
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 h1:+31CdF/okdokeFNoy9L/2PccG3JFidQT3ev64/r4pYU=
|
||||||
|
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E=
|
||||||
|
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk=
|
||||||
|
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0=
|
||||||
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b h1:M0/hjawi9ur15zpqL/h66ga87jlYA7iAuZ4HC6ak08k=
|
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b h1:M0/hjawi9ur15zpqL/h66ga87jlYA7iAuZ4HC6ak08k=
|
||||||
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU=
|
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211204153444-caad923f49f4 h1:KgfIc81yNEUKNAsF+Mt3C1Cl+iQqKF1r7nWEKzL0c2Y=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211204153444-caad923f49f4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec h1:3FLiRYO6PlQFDpUU7OEFlWgjGD1jnBIVSJ5SYRWk+9c=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||||
|
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
|
||||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||||
|
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||||
|
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c h1:JGCm/+tJ9gC6THUxooTldS+CUDsba0qvkvU3DHklqW8=
|
||||||
|
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
|
||||||
|
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
|
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
|
||||||
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||||
|
github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
||||||
|
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
|
||||||
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
|
||||||
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/knadh/koanf v1.4.0 h1:/k0Bh49SqLyLNfte9r6cvuZWrApOQhglOmhIU3L/zDw=
|
github.com/knadh/koanf v1.4.0 h1:/k0Bh49SqLyLNfte9r6cvuZWrApOQhglOmhIU3L/zDw=
|
||||||
github.com/knadh/koanf v1.4.0/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs=
|
github.com/knadh/koanf v1.4.0/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
||||||
|
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
|
||||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
@ -121,175 +292,516 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
|
|||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
|
||||||
|
github.com/metal3d/fyne-x v0.0.0-20220508095732-177117e583fb h1:+fP6ENsbd+BUOmD/kSjNtrOmi2vgJ/JfWDSWjTKmTVY=
|
||||||
|
github.com/metal3d/fyne-x v0.0.0-20220508095732-177117e583fb/go.mod h1:jBspDudEQ+Rdono8vBGHDtMUPE8ZpB/xq7FUYRqT3CI=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c=
|
github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c=
|
||||||
github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a h1:fnzS9RRQW8B5AgNCxkN0vJ/AoX+Xfqk3sAYon3iVrzA=
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268 h1:kOnq7TfaAO2Vc/MHxPqFIXe00y1qBxJAvhctXdko6vo=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
||||||
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
|
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
|
github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
||||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
|
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
|
||||||
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
|
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||||
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
||||||
github.com/srwiley/oksvg v0.0.0-20211120171407-1837d6608d8c h1:+e9myEHblxwU1r2Jb5PKzepMcsuig7+NUz+K53lBNaQ=
|
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 h1:XPYXKIuH/n5zpUoEWk2jWV/SjEMNYmqDYmTgbjmhtaI=
|
||||||
github.com/srwiley/oksvg v0.0.0-20211120171407-1837d6608d8c/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||||
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
||||||
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 h1:oDMiXaTMyBEuZMU53atpxqYsSB3U1CHkeAu2zr6wTeY=
|
|
||||||
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41 h1:YR16ysw3I1bqwtEcYV9dpvhHEe7j55hIClkLoAqY31I=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||||
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8=
|
github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8=
|
||||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
|
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
|
||||||
|
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
|
github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I=
|
||||||
|
github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ=
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs=
|
github.com/yuin/goldmark v1.4.10 h1:+WgKGo8CQrlMTRJpGCFCyNddOhW801TKC2QijVV9QVg=
|
||||||
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
github.com/yuin/goldmark v1.4.10/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||||
go.arsenm.dev/infinitime v0.0.0-20220416112421-b7a50271bece h1:ns/GMc4E8ZUZ9TEXhXgU4M+5sRaOLTFFoBWEJ67p3YM=
|
go.arsenm.dev/infinitime v0.0.0-20221107042015-72b558707ee3 h1:BfZkb41Gq6h9gy5Cg5jDd5hEk9kI27/h+EX0KN3qZv8=
|
||||||
go.arsenm.dev/infinitime v0.0.0-20220416112421-b7a50271bece/go.mod h1:Prvwx7Y2y8HsNRA1tPptduW9jzuw/JffmocvoHcDbYo=
|
go.arsenm.dev/infinitime v0.0.0-20221107042015-72b558707ee3/go.mod h1:K3NJ6fyPv5qqHUedB3MccKOE0whJMJZ80l/yTzzTrgc=
|
||||||
|
go.arsenm.dev/lrpc v0.0.0-20220513001344-3bcc01fdb6a0 h1:1K96g1eww+77GeGchwMhd0NTrs7Mk/Hc3M3ItW5NbG4=
|
||||||
|
go.arsenm.dev/lrpc v0.0.0-20220513001344-3bcc01fdb6a0/go.mod h1:goK9z735lfXmqlDxu9qN7FS8t0HJHN3PjyDtCToUY4w=
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||||
|
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||||
|
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw=
|
||||||
|
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee h1:/tShaw8UTf0XzI8DOZwQHzC7d6Vi3EtrBnftiZ4vAvU=
|
||||||
|
golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||||
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||||
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
|
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
|
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 h1:YuekqPskqwCCPM79F1X5Dhv4ezTCj+Ki1oNwiafxkA0=
|
||||||
|
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
|
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||||
|
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
|
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||||
|
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||||
|
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||||
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
@ -297,7 +809,53 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 h1:oomkgU6VaQDsV6qZby2uz1Lap0eXmku8+2em3A/l700=
|
||||||
|
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
|
||||||
|
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
|
modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo=
|
||||||
|
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||||
|
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
|
||||||
|
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
|
||||||
|
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||||
|
modernc.org/ccgo/v3 v3.16.6 h1:3l18poV+iUemQ98O3X5OMr97LOqlzis+ytivU4NqGhA=
|
||||||
|
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||||
|
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||||
|
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||||
|
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||||
|
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||||
|
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
|
||||||
|
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
|
||||||
|
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
|
||||||
|
modernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA=
|
||||||
|
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
|
||||||
|
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||||
|
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
|
||||||
|
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||||
|
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
|
||||||
|
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
|
modernc.org/sqlite v1.17.2 h1:TjmF36Wi5QcPYqRoAacV1cAyJ7xB/CD0ExpVUEMebnw=
|
||||||
|
modernc.org/sqlite v1.17.2/go.mod h1:GOQmuiXd6pTTes1Fi2s9apiCcD/wbKQtBZ0Nw6/etjM=
|
||||||
|
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
|
||||||
|
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||||
|
modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
|
||||||
|
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
|
||||||
|
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
||||||
|
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
|
||||||
|
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ReqTypeHeartRate = iota
|
|
||||||
ReqTypeBattLevel
|
|
||||||
ReqTypeFwVersion
|
|
||||||
ReqTypeFwUpgrade
|
|
||||||
ReqTypeBtAddress
|
|
||||||
ReqTypeNotify
|
|
||||||
ReqTypeSetTime
|
|
||||||
ReqTypeWatchHeartRate
|
|
||||||
ReqTypeWatchBattLevel
|
|
||||||
ReqTypeMotion
|
|
||||||
ReqTypeWatchMotion
|
|
||||||
ReqTypeStepCount
|
|
||||||
ReqTypeWatchStepCount
|
|
||||||
ReqTypeCancel
|
|
||||||
ReqTypeFS
|
|
||||||
ReqTypeWeatherUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
UpgradeTypeArchive = iota
|
|
||||||
UpgradeTypeFiles
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
FSTypeWrite = iota
|
|
||||||
FSTypeRead
|
|
||||||
FSTypeMove
|
|
||||||
FSTypeDelete
|
|
||||||
FSTypeList
|
|
||||||
FSTypeMkdir
|
|
||||||
)
|
|
||||||
|
|
||||||
type ReqDataFS struct {
|
|
||||||
Type int `json:"type"`
|
|
||||||
Files []string `json:"files"`
|
|
||||||
Data string `json:"data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReqDataFwUpgrade struct {
|
|
||||||
Type int
|
|
||||||
Files []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Response struct {
|
|
||||||
Type int `json:"type"`
|
|
||||||
Value interface{} `json:"value,omitempty"`
|
|
||||||
Message string `json:"msg,omitempty"`
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Error bool `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Request struct {
|
|
||||||
Type int `json:"type"`
|
|
||||||
Data interface{} `json:"data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReqDataNotify struct {
|
|
||||||
Title string
|
|
||||||
Body string
|
|
||||||
}
|
|
||||||
|
|
||||||
type DFUProgress struct {
|
|
||||||
Received int64 `mapstructure:"recvd"`
|
|
||||||
Total int64 `mapstructure:"total"`
|
|
||||||
Sent int64 `mapstructure:"sent"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FSTransferProgress struct {
|
|
||||||
Type int `json:"type" mapstructure:"type"`
|
|
||||||
Total uint32 `json:"total" mapstructure:"total"`
|
|
||||||
Sent uint32 `json:"sent" mapstructure:"sent"`
|
|
||||||
Done bool `json:"done" mapstructure:"done"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MotionValues struct {
|
|
||||||
X int16
|
|
||||||
Y int16
|
|
||||||
Z int16
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileInfo struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Size int64 `json:"size"`
|
|
||||||
IsDir bool `json:"isDir"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi FileInfo) String() string {
|
|
||||||
var isDirChar rune
|
|
||||||
if fi.IsDir {
|
|
||||||
isDirChar = 'd'
|
|
||||||
} else {
|
|
||||||
isDirChar = '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get human-readable value for file size
|
|
||||||
val, unit := bytesHuman(fi.Size)
|
|
||||||
prec := 0
|
|
||||||
// If value is less than 10, set precision to 1
|
|
||||||
if val < 10 {
|
|
||||||
prec = 1
|
|
||||||
}
|
|
||||||
// Convert float to string
|
|
||||||
valStr := strconv.FormatFloat(val, 'f', prec, 64)
|
|
||||||
|
|
||||||
// Return string formatted like so:
|
|
||||||
// - 10 kB file
|
|
||||||
// or:
|
|
||||||
// d 0 B .
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"%c %3s %-2s %s",
|
|
||||||
isDirChar,
|
|
||||||
valStr,
|
|
||||||
unit,
|
|
||||||
fi.Name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytesHuman returns a human-readable string for
|
|
||||||
// the amount of bytes inputted.
|
|
||||||
func bytesHuman(b int64) (float64, string) {
|
|
||||||
const unit = 1000
|
|
||||||
// Set possible units prefixes (PineTime flash is 4MB)
|
|
||||||
units := [2]rune{'k', 'M'}
|
|
||||||
// If amount of bytes is less than smallest unit
|
|
||||||
if b < unit {
|
|
||||||
// Return unchanged with unit "B"
|
|
||||||
return float64(b), "B"
|
|
||||||
}
|
|
||||||
|
|
||||||
div, exp := int64(unit), 0
|
|
||||||
// Get decimal values and unit prefix index
|
|
||||||
for n := b / unit; n >= unit; n /= unit {
|
|
||||||
div *= unit
|
|
||||||
exp++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create string for full unit
|
|
||||||
unitStr := string([]rune{units[exp], 'B'})
|
|
||||||
|
|
||||||
// Return decimal with unit string
|
|
||||||
return float64(b) / float64(div), unitStr
|
|
||||||
}
|
|
22
itd.toml
@ -1,6 +1,25 @@
|
|||||||
|
[bluetooth]
|
||||||
|
adapter = "hci0"
|
||||||
|
|
||||||
[socket]
|
[socket]
|
||||||
path = "/tmp/itd/socket"
|
path = "/tmp/itd/socket"
|
||||||
|
|
||||||
|
[metrics]
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[metrics.heartRate]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[metrics.stepCount]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[metrics.battLevel]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[metrics.motion]
|
||||||
|
# This may lower the battery life of the PineTime
|
||||||
|
enabled = false
|
||||||
|
|
||||||
[conn]
|
[conn]
|
||||||
reconnect = true
|
reconnect = true
|
||||||
|
|
||||||
@ -29,3 +48,6 @@
|
|||||||
[weather]
|
[weather]
|
||||||
enabled = true
|
enabled = true
|
||||||
location = "Los Angeles, CA"
|
location = "Los Angeles, CA"
|
||||||
|
|
||||||
|
[logging]
|
||||||
|
level = "info"
|
54
main.go
@ -19,11 +19,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gen2brain/dlgs"
|
"github.com/gen2brain/dlgs"
|
||||||
@ -36,9 +39,6 @@ import (
|
|||||||
|
|
||||||
var k = koanf.New(".")
|
var k = koanf.New(".")
|
||||||
|
|
||||||
//go:embed version.txt
|
|
||||||
var version string
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
firmwareUpdating = false
|
firmwareUpdating = false
|
||||||
// The FS must be updated when the watch is reconnected
|
// The FS must be updated when the watch is reconnected
|
||||||
@ -54,8 +54,13 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
level, err := zerolog.ParseLevel(k.String("logging.level"))
|
||||||
|
if err != nil || level == zerolog.NoLevel {
|
||||||
|
level = zerolog.InfoLevel
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize infinitime library
|
// Initialize infinitime library
|
||||||
infinitime.Init()
|
infinitime.Init(k.String("bluetooth.adapter"))
|
||||||
// Cleanly exit after function
|
// Cleanly exit after function
|
||||||
defer infinitime.Exit()
|
defer infinitime.Exit()
|
||||||
|
|
||||||
@ -66,11 +71,27 @@ func main() {
|
|||||||
Whitelist: k.Strings("conn.whitelist.devices"),
|
Whitelist: k.Strings("conn.whitelist.devices"),
|
||||||
OnReqPasskey: onReqPasskey,
|
OnReqPasskey: onReqPasskey,
|
||||||
Logger: log.Logger,
|
Logger: log.Logger,
|
||||||
LogLevel: zerolog.WarnLevel,
|
LogLevel: level,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
go func() {
|
||||||
|
<-sigCh
|
||||||
|
cancel()
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
signal.Notify(
|
||||||
|
sigCh,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
)
|
||||||
|
|
||||||
// Connect to InfiniTime with default options
|
// Connect to InfiniTime with default options
|
||||||
dev, err := infinitime.Connect(opts)
|
dev, err := infinitime.Connect(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error connecting to InfiniTime")
|
log.Fatal().Err(err).Msg("Error connecting to InfiniTime")
|
||||||
}
|
}
|
||||||
@ -131,29 +152,40 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start control socket
|
// Start control socket
|
||||||
err = initCallNotifs(dev)
|
err = initCallNotifs(ctx, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Error initializing call notifications")
|
log.Error().Err(err).Msg("Error initializing call notifications")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize notification relay
|
// Initialize notification relay
|
||||||
err = initNotifRelay(dev)
|
err = initNotifRelay(ctx, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Error initializing notification relay")
|
log.Error().Err(err).Msg("Error initializing notification relay")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializa weather
|
// Initializa weather
|
||||||
err = initWeather(dev)
|
err = initWeather(ctx, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Error initializing weather")
|
log.Error().Err(err).Msg("Error initializing weather")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize metrics collection
|
||||||
|
err = initMetrics(ctx, dev)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error intializing metrics collection")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize metrics collection
|
||||||
|
err = initPureMaps(ctx, dev)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error intializing puremaps integration")
|
||||||
|
}
|
||||||
|
|
||||||
// Start control socket
|
// Start control socket
|
||||||
err = startSocket(dev)
|
err = startSocket(ctx, dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Error starting socket")
|
log.Error().Err(err).Msg("Error starting socket")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
210
maps.go
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
interfaceName = "io.github.rinigus.PureMaps.navigator"
|
||||||
|
iconProperty = interfaceName + ".icon"
|
||||||
|
narrativeProperty = interfaceName + ".narrative"
|
||||||
|
manDistProperty = interfaceName + ".manDist"
|
||||||
|
progressProperty = interfaceName + ".progress"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initPureMaps(ctx context.Context, dev *infinitime.Device) error {
|
||||||
|
// Connect to session bus. This connection is for method calls.
|
||||||
|
conn, err := newSessionBusConn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, err := pureMapsExists(ctx, conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to session bus. This connection is for method calls.
|
||||||
|
monitorConn, err := newSessionBusConn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define rules to listen for
|
||||||
|
var rules = []string{
|
||||||
|
"type='signal',interface='io.github.rinigus.PureMaps.navigator'",
|
||||||
|
}
|
||||||
|
var flag uint = 0
|
||||||
|
// Becode monitor for notifications
|
||||||
|
call := monitorConn.BusObject().CallWithContext(
|
||||||
|
ctx, "org.freedesktop.DBus.Monitoring.BecomeMonitor", 0, rules, flag,
|
||||||
|
)
|
||||||
|
if call.Err != nil {
|
||||||
|
return call.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var navigator dbus.BusObject
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
navigator = conn.Object("io.github.rinigus.PureMaps", "/io/github/rinigus/PureMaps/navigator")
|
||||||
|
err = setAll(navigator, dev)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error setting all navigation fields")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
signalCh := make(chan *dbus.Message, 10)
|
||||||
|
monitorConn.Eavesdrop(signalCh)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case sig := <-signalCh:
|
||||||
|
if sig.Type != dbus.TypeSignal {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var member string
|
||||||
|
err = sig.Headers[dbus.FieldMember].Store(&member)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error getting dbus member field")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(member, "Changed") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Str("member", member).Msg("Signal received from PureMaps navigator")
|
||||||
|
|
||||||
|
// The object must be retrieved in this loop in case PureMaps was not
|
||||||
|
// open at the time ITD was started.
|
||||||
|
navigator = conn.Object("io.github.rinigus.PureMaps", "/io/github/rinigus/PureMaps/navigator")
|
||||||
|
member = strings.TrimSuffix(member, "Changed")
|
||||||
|
|
||||||
|
switch member {
|
||||||
|
case "icon":
|
||||||
|
var icon string
|
||||||
|
err = navigator.StoreProperty(iconProperty, &icon)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error getting property")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Navigation.SetFlag(infinitime.NavFlag(icon))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error setting flag")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case "narrative":
|
||||||
|
var narrative string
|
||||||
|
err = navigator.StoreProperty(narrativeProperty, &narrative)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error getting property")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Navigation.SetNarrative(narrative)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error setting flag")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case "manDist":
|
||||||
|
var manDist string
|
||||||
|
err = navigator.StoreProperty(manDistProperty, &manDist)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error getting property")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Navigation.SetManDist(manDist)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error setting flag")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case "progress":
|
||||||
|
var progress int32
|
||||||
|
err = navigator.StoreProperty(progressProperty, &progress)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error getting property")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Navigation.SetProgress(uint8(progress))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("property", member).Msg("Error setting flag")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
log.Info().Msg("Sending PureMaps data to InfiniTime")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAll(navigator dbus.BusObject, dev *infinitime.Device) error {
|
||||||
|
var icon string
|
||||||
|
err := navigator.StoreProperty(iconProperty, &icon)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Navigation.SetFlag(infinitime.NavFlag(icon))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var narrative string
|
||||||
|
err = navigator.StoreProperty(narrativeProperty, &narrative)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Navigation.SetNarrative(narrative)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var manDist string
|
||||||
|
err = navigator.StoreProperty(manDistProperty, &manDist)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Navigation.SetManDist(manDist)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var progress int32
|
||||||
|
err = navigator.StoreProperty(progressProperty, &progress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev.Navigation.SetProgress(uint8(progress))
|
||||||
|
}
|
||||||
|
|
||||||
|
// pureMapsExists checks to make sure the PureMaps service exists on the bus
|
||||||
|
func pureMapsExists(ctx context.Context, conn *dbus.Conn) (bool, error) {
|
||||||
|
var names []string
|
||||||
|
err := conn.BusObject().CallWithContext(
|
||||||
|
ctx, "org.freedesktop.DBus.ListNames", 0,
|
||||||
|
).Store(&names)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return strSlcContains(names, "io.github.rinigus.PureMaps"), nil
|
||||||
|
}
|
131
metrics.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initMetrics(ctx context.Context, dev *infinitime.Device) error {
|
||||||
|
// If metrics disabled, return nil
|
||||||
|
if !k.Bool("metrics.enabled") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open metrics database
|
||||||
|
db, err := sql.Open("sqlite", filepath.Join(cfgDir, "metrics.db"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create heartRate table
|
||||||
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS heartRate(time INT, bpm INT);")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create stepCount table
|
||||||
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS stepCount(time INT, steps INT);")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create battLevel table
|
||||||
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS battLevel(time INT, percent INT);")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create motion table
|
||||||
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS motion(time INT, X INT, Y INT, Z INT);")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If heart rate metrics enabled in config
|
||||||
|
if k.Bool("metrics.heartRate.enabled") {
|
||||||
|
// Watch heart rate
|
||||||
|
heartRateCh, err := dev.WatchHeartRate(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
// For every heart rate sample
|
||||||
|
for heartRate := range heartRateCh {
|
||||||
|
// Get current time
|
||||||
|
unixTime := time.Now().UnixNano()
|
||||||
|
// Insert sample and time into database
|
||||||
|
db.Exec("INSERT INTO heartRate VALUES (?, ?);", unixTime, heartRate)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If step count metrics enabled in config
|
||||||
|
if k.Bool("metrics.stepCount.enabled") {
|
||||||
|
// Watch step count
|
||||||
|
stepCountCh, err := dev.WatchStepCount(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
// For every step count sample
|
||||||
|
for stepCount := range stepCountCh {
|
||||||
|
// Get current time
|
||||||
|
unixTime := time.Now().UnixNano()
|
||||||
|
// Insert sample and time into database
|
||||||
|
db.Exec("INSERT INTO stepCount VALUES (?, ?);", unixTime, stepCount)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If battery level metrics enabled in config
|
||||||
|
if k.Bool("metrics.battLevel.enabled") {
|
||||||
|
// Watch battery level
|
||||||
|
battLevelCh, err := dev.WatchBatteryLevel(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
// For every battery level sample
|
||||||
|
for battLevel := range battLevelCh {
|
||||||
|
// Get current time
|
||||||
|
unixTime := time.Now().UnixNano()
|
||||||
|
// Insert sample and time into database
|
||||||
|
db.Exec("INSERT INTO battLevel VALUES (?, ?);", unixTime, battLevel)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If motion metrics enabled in config
|
||||||
|
if k.Bool("metrics.motion.enabled") {
|
||||||
|
// Watch motion values
|
||||||
|
motionCh, err := dev.WatchMotion(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
// For every motion sample
|
||||||
|
for motionVals := range motionCh {
|
||||||
|
// Get current time
|
||||||
|
unixTime := time.Now().UnixNano()
|
||||||
|
// Insert sample values and time into database
|
||||||
|
db.Exec(
|
||||||
|
"INSERT INTO motion VALUES (?, ?, ?, ?);",
|
||||||
|
unixTime,
|
||||||
|
motionVals.X,
|
||||||
|
motionVals.Y,
|
||||||
|
motionVals.Z,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msg("Initialized metrics collection")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
@ -27,9 +28,9 @@ import (
|
|||||||
"go.arsenm.dev/itd/translit"
|
"go.arsenm.dev/itd/translit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initNotifRelay(dev *infinitime.Device) error {
|
func initNotifRelay(ctx context.Context, dev *infinitime.Device) error {
|
||||||
// Connect to dbus session bus
|
// Connect to dbus session bus
|
||||||
bus, err := newSessionBusConn()
|
bus, err := newSessionBusConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -40,7 +41,9 @@ func initNotifRelay(dev *infinitime.Device) error {
|
|||||||
}
|
}
|
||||||
var flag uint = 0
|
var flag uint = 0
|
||||||
// Becode monitor for notifications
|
// Becode monitor for notifications
|
||||||
call := bus.BusObject().Call("org.freedesktop.DBus.Monitoring.BecomeMonitor", 0, rules, flag)
|
call := bus.BusObject().CallWithContext(
|
||||||
|
ctx, "org.freedesktop.DBus.Monitoring.BecomeMonitor", 0, rules, flag,
|
||||||
|
)
|
||||||
if call.Err != nil {
|
if call.Err != nil {
|
||||||
return call.Err
|
return call.Err
|
||||||
}
|
}
|
||||||
|
3
scripts/gen-version.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
git describe --tags > version.txt
|
@ -301,7 +301,7 @@ var Transliterators = map[string]Transliterator{
|
|||||||
"Ð", "D",
|
"Ð", "D",
|
||||||
"ð", "d",
|
"ð", "d",
|
||||||
},
|
},
|
||||||
"Czeck": Map{
|
"Czech": Map{
|
||||||
"ř", "r",
|
"ř", "r",
|
||||||
"ě", "e",
|
"ě", "e",
|
||||||
"ý", "y",
|
"ý", "y",
|
||||||
|
8
version.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:generate scripts/gen-version.sh
|
||||||
|
|
||||||
|
//go:embed version.txt
|
||||||
|
var version string
|
@ -1 +0,0 @@
|
|||||||
unknown
|
|
20
weather.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@ -60,13 +61,13 @@ type OSMData []struct {
|
|||||||
|
|
||||||
var sendWeatherCh = make(chan struct{}, 1)
|
var sendWeatherCh = make(chan struct{}, 1)
|
||||||
|
|
||||||
func initWeather(dev *infinitime.Device) error {
|
func initWeather(ctx context.Context, dev *infinitime.Device) error {
|
||||||
if !k.Bool("weather.enabled") {
|
if !k.Bool("weather.enabled") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get location based on string in config
|
// Get location based on string in config
|
||||||
lat, lon, err := getLocation(k.String("weather.location"))
|
lat, lon, err := getLocation(ctx, k.String("weather.location"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -76,7 +77,7 @@ func initWeather(dev *infinitime.Device) error {
|
|||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
// Attempt to get weather
|
// Attempt to get weather
|
||||||
data, err := getWeather(lat, lon)
|
data, err := getWeather(ctx, lat, lon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Msg("Error getting weather data")
|
log.Warn().Err(err).Msg("Error getting weather data")
|
||||||
// Wait 15 minutes before retrying
|
// Wait 15 minutes before retrying
|
||||||
@ -181,10 +182,14 @@ func initWeather(dev *infinitime.Device) error {
|
|||||||
|
|
||||||
// getLocation returns the latitude and longitude
|
// getLocation returns the latitude and longitude
|
||||||
// given a location
|
// given a location
|
||||||
func getLocation(loc string) (lat, lon float64, err error) {
|
func getLocation(ctx context.Context, loc string) (lat, lon float64, err error) {
|
||||||
// Create request URL and perform GET request
|
// Create request URL and perform GET request
|
||||||
reqURL := fmt.Sprintf("https://nominatim.openstreetmap.org/search.php?q=%s&format=jsonv2", url.QueryEscape(loc))
|
reqURL := fmt.Sprintf("https://nominatim.openstreetmap.org/search.php?q=%s&format=jsonv2", url.QueryEscape(loc))
|
||||||
res, err := http.Get(reqURL)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -218,9 +223,10 @@ func getLocation(loc string) (lat, lon float64, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getWeather gets weather data given a latitude and longitude
|
// getWeather gets weather data given a latitude and longitude
|
||||||
func getWeather(lat, lon float64) (*METResponse, error) {
|
func getWeather(ctx context.Context, lat, lon float64) (*METResponse, error) {
|
||||||
// Create new GET request
|
// Create new GET request
|
||||||
req, err := http.NewRequest(
|
req, err := http.NewRequestWithContext(
|
||||||
|
ctx,
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=%.2f&lon=%.2f",
|
"https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=%.2f&lon=%.2f",
|
||||||
|