Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d318c584da | |||
| c8c617c10a | |||
| 365414f951 | |||
| 9b04d06560 | |||
| 23e9195e70 | |||
| cd68fbd7f3 | |||
| 205a041758 | |||
| 62597f70ee | |||
| 32bb141244 | |||
| f28c68438a | |||
| aa90e9eb26 | |||
| 553709ce8d | |||
| 2ded0d36b1 | |||
| a885eacc70 | |||
| 9e63401db3 | |||
| 2f14e70721 | |||
| 614d14e399 | |||
| c08ddfd810 | |||
| 4bdb82b1bc | |||
| b4d302caf6 | |||
| 4b2694ee0d | |||
| 4c36144b0b | |||
| e88dea40fb | |||
| 23b9cfe8a3 | |||
| 518fe74e96 | |||
| 27aabdceba | |||
| c019d7523b | |||
| 03c3c6b22f | |||
| 69d1027f01 | |||
| 873df67d1f | |||
| a9ef386883 | |||
| 24cfda82d7 | |||
| 655af5c446 | |||
| b363a20a9d | |||
| f5d326124d | |||
| cb8fb2c0bc | |||
| 38119435f1 | |||
| 034a69c12f | |||
| 70006a3d7b | |||
| 8aada58d64 | |||
| 5d231207cd | |||
| 584d9426e6 | |||
| 7a772a5458 | |||
| 079c733b60 | |||
| e24a8e9088 | |||
| 0b5d777077 | |||
| 75327286ef | |||
| 099b0cd849 | |||
| c9c00e0072 | |||
| b2ffb2062a | |||
| 2e8c825fff | |||
| 7b870950d1 | |||
| 3a877c41a4 | |||
| 04fb390bee | |||
| 50b17d3266 | |||
| 763d408405 | |||
| fbb7cd9bc1 | |||
| f1b7f70313 | |||
| 552f19676b | |||
| 9d58ea0ae7 | |||
| 76875db7ea | |||
| be5bdc625b | |||
| 0d0db949af | |||
| 0d164aef3d | |||
| 28610d9ebb | |||
| dff34b484d | |||
| 2ea9f99db6 | |||
| 44dc5f8e47 | |||
| 4d35912466 | |||
| ef29b9bee4 | |||
| e198b769f9 | |||
| ef4bad94b5 | |||
| 8cf2b47733 | |||
| f20fdcb161 | |||
| eeba9b2964 | |||
| d7057e3f9c | |||
| 80a5867d6b | |||
| f001dd6079 | |||
| b87586ef15 | |||
| 295892c8a8 | |||
| 1492db7566 | |||
| e7de7bd7bb | |||
| 7b849a3fc7 | |||
| 21d4964207 | |||
| 604ea57c5f | |||
| b15cbb6349 | |||
| 843e369bab | |||
| eec7a3db48 | |||
| c23201e18c | |||
| 4bc6eb9d41 | |||
| 2a59e74a2c | |||
| df743cca96 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/itctl
|
/itctl
|
||||||
/itd
|
/itd
|
||||||
/itgui
|
/itgui
|
||||||
|
/version.txt
|
||||||
8
Makefile
8
Makefile
@@ -3,13 +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:
|
all: version
|
||||||
go build $(GOFLAGS)
|
go build $(GOFLAGS)
|
||||||
go build ./cmd/itctl $(GOFLAGS)
|
go build ./cmd/itctl $(GOFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f itctl
|
rm -f itctl
|
||||||
rm -f itd
|
rm -f itd
|
||||||
|
printf "unknown" > version.txt
|
||||||
|
|
||||||
install:
|
install:
|
||||||
install -Dm755 ./itd $(BIN_PREFIX)/itd
|
install -Dm755 ./itd $(BIN_PREFIX)/itd
|
||||||
@@ -23,4 +24,7 @@ uninstall:
|
|||||||
rm $(SERVICE_PREFIX)/itd.service
|
rm $(SERVICE_PREFIX)/itd.service
|
||||||
rm $(CFG_PREFIX)/itd.toml
|
rm $(CFG_PREFIX)/itd.toml
|
||||||
|
|
||||||
.PHONY: all clean install uninstall
|
version:
|
||||||
|
printf "r%s.%s" "$(shell git rev-list --count HEAD)" "$(shell git rev-parse --short HEAD)" > version.txt
|
||||||
|
|
||||||
|
.PHONY: all clean install uninstall version
|
||||||
23
README.md
23
README.md
@@ -12,9 +12,10 @@
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Notification relay
|
- Notification relay
|
||||||
- Notificstion transliteration
|
- Notification transliteration
|
||||||
|
- Call Notifications (ModemManager)
|
||||||
- Music control
|
- Music control
|
||||||
- Get info from watch (HRM, Battery level, Firmware version)
|
- Get info from watch (HRM, Battery level, Firmware version, Motion)
|
||||||
- Set current time
|
- Set current time
|
||||||
- Control socket
|
- Control socket
|
||||||
- Firmware upgrades
|
- Firmware upgrades
|
||||||
@@ -28,7 +29,7 @@ This daemon creates a UNIX socket at `/tmp/itd/socket`. It allows you to directl
|
|||||||
The socket accepts JSON requests. For example, sending a notification looks like this:
|
The socket accepts JSON requests. For example, sending a notification looks like this:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"type": "notify", "data": {"title": "title1", "body": "body1"}}
|
{"type": 5, "data": {"title": "title1", "body": "body1"}}
|
||||||
```
|
```
|
||||||
|
|
||||||
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 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.
|
||||||
@@ -59,6 +60,7 @@ Since the PineTime does not have enough space to store all unicode glyphs, it on
|
|||||||
- Armenian
|
- Armenian
|
||||||
- Korean
|
- Korean
|
||||||
- Chinese
|
- Chinese
|
||||||
|
- Romanian
|
||||||
- Emoji
|
- Emoji
|
||||||
|
|
||||||
Place the desired map names in an array as `notifs.translit.use`. They will be evaluated in order. You can also put custom transliterations in `notifs.translit.custom`. These take priority over any other maps. The `notifs.translit` config section should look like this:
|
Place the desired map names in an array as `notifs.translit.use`. They will be evaluated in order. You can also put custom transliterations in `notifs.translit.custom`. These take priority over any other maps. The `notifs.translit` config section should look like this:
|
||||||
@@ -82,10 +84,10 @@ This is the `itctl` usage screen:
|
|||||||
Control the itd daemon for InfiniTime smartwatches
|
Control the itd daemon for InfiniTime smartwatches
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
itctl [flags]
|
||||||
itctl [command]
|
itctl [command]
|
||||||
|
|
||||||
Available Commands:
|
Available Commands:
|
||||||
completion generate the autocompletion script for the specified shell
|
|
||||||
firmware Manage InfiniTime firmware
|
firmware Manage InfiniTime firmware
|
||||||
get Get information from InfiniTime
|
get Get information from InfiniTime
|
||||||
help Help about any command
|
help Help about any command
|
||||||
@@ -93,7 +95,8 @@ Available Commands:
|
|||||||
set Set information on InfiniTime
|
set Set information on InfiniTime
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for itctl
|
-h, --help help for itctl
|
||||||
|
-s, --socket-path string Path to itd socket
|
||||||
|
|
||||||
Use "itctl [command] --help" for more information about a command.
|
Use "itctl [command] --help" for more information about a command.
|
||||||
```
|
```
|
||||||
@@ -175,7 +178,7 @@ To cross compile, simply set the go environment variables. For example, for Pine
|
|||||||
make GOOS=linux GOARCH=arm64
|
make GOOS=linux GOARCH=arm64
|
||||||
```
|
```
|
||||||
|
|
||||||
This will compile `itd` and `itctl` for Linux aarch64 which is what runs on the PinePhone. This daemon only runs on Linux due to the library's dependencies (`dbus`, `bluez`, and `playerctl` specifically).
|
This will compile `itd` and `itctl` for Linux aarch64 which is what runs on the PinePhone. This daemon only runs on Linux due to the library's dependencies (`dbus`, and `bluez` specifically).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -184,3 +187,11 @@ This will compile `itd` and `itctl` for Linux aarch64 which is what runs on the
|
|||||||
This daemon places a config file at `/etc/itd.toml`. This is the global config. `itd` will also look for a config at `~/.config/itd.toml`.
|
This daemon places a config file at `/etc/itd.toml`. This is the global config. `itd` will also look for a config at `~/.config/itd.toml`.
|
||||||
|
|
||||||
Most of the time, the daemon does not need to be restarted for config changes to take effect.
|
Most of the time, the daemon does not need to be restarted for config changes to take effect.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Attribution
|
||||||
|
|
||||||
|
Location data from OpenStreetMap Nominatim, © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors
|
||||||
|
|
||||||
|
Weather data from the [Norwegian Meteorological Institute](https://www.met.no/en)
|
||||||
153
api/client.go
Normal file
153
api/client.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
102
api/fs.go
Normal file
102
api/fs.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"go.arsenm.dev/itd/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) Rename(old, new string) error {
|
||||||
|
_, err := c.request(types.Request{
|
||||||
|
Type: types.ReqTypeFS,
|
||||||
|
Data: types.ReqDataFS{
|
||||||
|
Type: types.FSTypeMove,
|
||||||
|
Files: []string{old, new},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Remove(paths ...string) error {
|
||||||
|
_, err := c.request(types.Request{
|
||||||
|
Type: types.ReqTypeFS,
|
||||||
|
Data: types.ReqDataFS{
|
||||||
|
Type: types.FSTypeDelete,
|
||||||
|
Files: paths,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Mkdir(paths ...string) error {
|
||||||
|
_, err := c.request(types.Request{
|
||||||
|
Type: types.ReqTypeFS,
|
||||||
|
Data: types.ReqDataFS{
|
||||||
|
Type: types.FSTypeMkdir,
|
||||||
|
Files: paths,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ReadDir(path string) ([]types.FileInfo, error) {
|
||||||
|
res, err := c.request(types.Request{
|
||||||
|
Type: types.ReqTypeFS,
|
||||||
|
Data: types.ReqDataFS{
|
||||||
|
Type: types.FSTypeList,
|
||||||
|
Files: []string{path},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
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) {
|
||||||
|
c.readProgressCh = make(chan types.FSTransferProgress, 5)
|
||||||
|
|
||||||
|
_, err := c.request(types.Request{
|
||||||
|
Type: types.ReqTypeFS,
|
||||||
|
Data: types.ReqDataFS{
|
||||||
|
Type: types.FSTypeRead,
|
||||||
|
Files: []string{localPath, remotePath},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.readProgressCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WriteFile(localPath, remotePath string) (<-chan types.FSTransferProgress, error) {
|
||||||
|
c.writeProgressCh = make(chan types.FSTransferProgress, 5)
|
||||||
|
|
||||||
|
_, err := c.request(types.Request{
|
||||||
|
Type: types.ReqTypeFS,
|
||||||
|
Data: types.ReqDataFS{
|
||||||
|
Type: types.FSTypeWrite,
|
||||||
|
Files: []string{remotePath, localPath},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.writeProgressCh, nil
|
||||||
|
}
|
||||||
209
api/info.go
Normal file
209
api/info.go
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
14
api/notify.go
Normal file
14
api/notify.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "go.arsenm.dev/itd/internal/types"
|
||||||
|
|
||||||
|
func (c *Client) Notify(title string, body string) error {
|
||||||
|
_, err := c.request(types.Request{
|
||||||
|
Type: types.ReqTypeNotify,
|
||||||
|
Data: types.ReqDataNotify{
|
||||||
|
Title: title,
|
||||||
|
Body: body,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
33
api/time.go
Normal file
33
api/time.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
48
api/upgrade.go
Normal file
48
api/upgrade.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
17
api/weather.go
Normal file
17
api/weather.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
142
calls.go
Normal file
142
calls.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initCallNotifs(dev *infinitime.Device) error {
|
||||||
|
// Connect to system bus. This connection is for method calls.
|
||||||
|
conn, err := newSystemBusConn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if modem manager interface exists
|
||||||
|
exists, err := modemManagerExists(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it does not exist, stop function
|
||||||
|
if !exists {
|
||||||
|
conn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to system bus. This connection is for monitoring.
|
||||||
|
monitorConn, err := newSystemBusConn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add match for new calls to monitor connection
|
||||||
|
err = monitorConn.AddMatchSignal(
|
||||||
|
dbus.WithMatchSender("org.freedesktop.ModemManager1"),
|
||||||
|
dbus.WithMatchInterface("org.freedesktop.ModemManager1.Modem.Voice"),
|
||||||
|
dbus.WithMatchMember("CallAdded"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create channel to receive calls
|
||||||
|
callCh := make(chan *dbus.Message, 5)
|
||||||
|
// Notify channel upon received message
|
||||||
|
monitorConn.Eavesdrop(callCh)
|
||||||
|
|
||||||
|
var respHandlerOnce sync.Once
|
||||||
|
var callObj dbus.BusObject
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// For every message received
|
||||||
|
for event := range callCh {
|
||||||
|
// Get path to call object
|
||||||
|
callPath := event.Body[0].(dbus.ObjectPath)
|
||||||
|
// Get call object
|
||||||
|
callObj = conn.Object("org.freedesktop.ModemManager1", callPath)
|
||||||
|
|
||||||
|
// Get phone number from call object using method call connection
|
||||||
|
phoneNum, err := getPhoneNum(conn, callObj)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error getting phone number")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send call notification to InfiniTime
|
||||||
|
resCh, err := dev.NotifyCall(phoneNum)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go respHandlerOnce.Do(func() {
|
||||||
|
// Wait for PineTime response
|
||||||
|
for res := range resCh {
|
||||||
|
switch res {
|
||||||
|
case infinitime.CallStatusAccepted:
|
||||||
|
// Attempt to accept call
|
||||||
|
err = acceptCall(conn, callObj)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Error accepting call")
|
||||||
|
}
|
||||||
|
case infinitime.CallStatusDeclined:
|
||||||
|
// Attempt to decline call
|
||||||
|
err = declineCall(conn, callObj)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Error declining call")
|
||||||
|
}
|
||||||
|
case infinitime.CallStatusMuted:
|
||||||
|
// Warn about unimplemented muting
|
||||||
|
log.Warn().Msg("Muting calls is not implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Info().Msg("Relaying calls to InfiniTime")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modemManagerExists(conn *dbus.Conn) (bool, error) {
|
||||||
|
var names []string
|
||||||
|
err := conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&names)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return strSlcContains(names, "org.freedesktop.ModemManager1"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPhoneNum gets a phone number from a call object using a DBus connection
|
||||||
|
func getPhoneNum(conn *dbus.Conn, callObj dbus.BusObject) (string, error) {
|
||||||
|
var out string
|
||||||
|
// Get number property on DBus object and store return value in out
|
||||||
|
err := callObj.StoreProperty("org.freedesktop.ModemManager1.Call.Number", &out)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPhoneNum accepts a call using a DBus connection
|
||||||
|
func acceptCall(conn *dbus.Conn, callObj dbus.BusObject) error {
|
||||||
|
// Call Accept() method on DBus object
|
||||||
|
call := callObj.Call("org.freedesktop.ModemManager1.Call.Accept", 0)
|
||||||
|
if call.Err != nil {
|
||||||
|
return call.Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPhoneNum declines a call using a DBus connection
|
||||||
|
func declineCall(conn *dbus.Conn, callObj dbus.BusObject) error {
|
||||||
|
// Call Hangup() method on DBus object
|
||||||
|
call := callObj.Call("org.freedesktop.ModemManager1.Call.Hangup", 0)
|
||||||
|
if call.Err != nil {
|
||||||
|
return call.Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// addressCmd represents the address command
|
|
||||||
var addressCmd = &cobra.Command{
|
|
||||||
Use: "address",
|
|
||||||
Aliases: []string{"addr"},
|
|
||||||
Short: "Get InfiniTime's bluetooth address",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Connect to itd UNIX socket
|
|
||||||
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error dialing socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Encode request into connection
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeBtAddress,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error making request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read one line from connection
|
|
||||||
line, _, err := bufio.NewReader(conn).ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error reading line from connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res types.Response
|
|
||||||
// Decode line into response
|
|
||||||
err = json.Unmarshal(line, &res)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding JSON data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Error {
|
|
||||||
log.Fatal().Msg(res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print returned value
|
|
||||||
fmt.Println(res.Value)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
getCmd.AddCommand(addressCmd)
|
|
||||||
|
|
||||||
// Here you will define your flags and configuration settings.
|
|
||||||
|
|
||||||
// Cobra supports Persistent Flags which will work for this command
|
|
||||||
// and all subcommands, e.g.:
|
|
||||||
// addressCmd.PersistentFlags().String("foo", "", "A help for foo")
|
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
|
||||||
// is called directly, e.g.:
|
|
||||||
// addressCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// batteryCmd represents the batt command
|
|
||||||
var batteryCmd = &cobra.Command{
|
|
||||||
Use: "battery",
|
|
||||||
Aliases: []string{"batt"},
|
|
||||||
Short: "Get battery level from InfiniTime",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Connect to itd UNIX socket
|
|
||||||
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error dialing socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Encode request into connection
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeBattLevel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error making request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read one line from connection
|
|
||||||
line, _, err := bufio.NewReader(conn).ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error reading line from connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res types.Response
|
|
||||||
// Deocde line into response
|
|
||||||
err = json.Unmarshal(line, &res)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding JSON data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Error {
|
|
||||||
log.Fatal().Msg(res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print returned percentage
|
|
||||||
fmt.Printf("%d%%\n", int(res.Value.(float64)))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
getCmd.AddCommand(batteryCmd)
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
type DFUProgress struct {
|
|
||||||
Received int64 `mapstructure:"recvd"`
|
|
||||||
Total int64 `mapstructure:"total"`
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// firmwareCmd represents the firmware command
|
|
||||||
var firmwareCmd = &cobra.Command{
|
|
||||||
Use: "firmware",
|
|
||||||
Short: "Manage InfiniTime firmware",
|
|
||||||
Aliases: []string{"fw"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(firmwareCmd)
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// getCmd represents the get command
|
|
||||||
var getCmd = &cobra.Command{
|
|
||||||
Use: "get",
|
|
||||||
Short: "Get information from InfiniTime",
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(getCmd)
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// heartCmd represents the heart command
|
|
||||||
var heartCmd = &cobra.Command{
|
|
||||||
Use: "heart",
|
|
||||||
Short: "Get heart rate from InfiniTime",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Connect to itd UNIX socket
|
|
||||||
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error dialing socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Encode request into connection
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeHeartRate,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error making request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read one line from connection
|
|
||||||
line, _, err := bufio.NewReader(conn).ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error reading line from connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res types.Response
|
|
||||||
// Decode line into response
|
|
||||||
err = json.Unmarshal(line, &res)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding JSON data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Error {
|
|
||||||
log.Fatal().Msg(res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print returned BPM
|
|
||||||
fmt.Printf("%d BPM\n", int(res.Value.(float64)))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
getCmd.AddCommand(heartCmd)
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// notifyCmd represents the notify command
|
|
||||||
var notifyCmd = &cobra.Command{
|
|
||||||
Use: "notify <title> <body>",
|
|
||||||
Short: "Send notification to InfiniTime",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Ensure required arguments
|
|
||||||
if len(args) != 2 {
|
|
||||||
cmd.Usage()
|
|
||||||
log.Fatal().Msg("Command notify requires two arguments")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to itd UNIX socket
|
|
||||||
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error dialing socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Encode request into connection
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeNotify,
|
|
||||||
Data: types.ReqDataNotify{
|
|
||||||
Title: args[0],
|
|
||||||
Body: args[1],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error making request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read one line from connection
|
|
||||||
line, _, err := bufio.NewReader(conn).ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error reading line from connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res types.Response
|
|
||||||
// Decode line into response
|
|
||||||
err = json.Unmarshal(line, &res)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding JSON data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Error {
|
|
||||||
log.Fatal().Msg(res.Message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(notifyCmd)
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/abiosoft/ishell"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
// rootCmd represents the base command when called without any subcommands
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: "itctl",
|
|
||||||
Short: "Control the itd daemon for InfiniTime smartwatches",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
|
|
||||||
// Create new shell
|
|
||||||
sh := ishell.New()
|
|
||||||
sh.SetPrompt("itctl> ")
|
|
||||||
|
|
||||||
// For every command in cobra
|
|
||||||
for _, subCmd := range cmd.Commands() {
|
|
||||||
// Add top level command to ishell
|
|
||||||
sh.AddCmd(&ishell.Cmd{
|
|
||||||
Name: subCmd.Name(),
|
|
||||||
Help: subCmd.Short,
|
|
||||||
Aliases: subCmd.Aliases,
|
|
||||||
LongHelp: subCmd.Long,
|
|
||||||
Func: func(ctx *ishell.Context) {
|
|
||||||
// Append name and arguments of command
|
|
||||||
args := append([]string{ctx.Cmd.Name}, ctx.Args...)
|
|
||||||
// Set root command arguments
|
|
||||||
cmd.SetArgs(args)
|
|
||||||
// Execute root command with new arguments
|
|
||||||
cmd.Execute()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start shell
|
|
||||||
sh.Run()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
|
||||||
func Execute() {
|
|
||||||
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
|
||||||
cobra.CheckErr(rootCmd.Execute())
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Register flag for socket path
|
|
||||||
rootCmd.Flags().StringP("socket-path", "s", "", "Path to itd socket")
|
|
||||||
|
|
||||||
// Bind flag and environment variable to viper key
|
|
||||||
viper.BindPFlag("sockPath", rootCmd.Flags().Lookup("socket-path"))
|
|
||||||
viper.BindEnv("sockPath", "ITCTL_SOCKET_PATH")
|
|
||||||
|
|
||||||
// Set default value for socket path
|
|
||||||
viper.SetDefault("sockPath", "/tmp/itd/socket")
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// setCmd represents the set command
|
|
||||||
var setCmd = &cobra.Command{
|
|
||||||
Use: "set",
|
|
||||||
Short: "Set information on InfiniTime",
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(setCmd)
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// timeCmd represents the time command
|
|
||||||
var timeCmd = &cobra.Command{
|
|
||||||
Use: `time <ISO8601|"now">`,
|
|
||||||
Short: "Set InfiniTime's clock to specified time",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Ensure required arguments
|
|
||||||
if len(args) != 1 {
|
|
||||||
cmd.Usage()
|
|
||||||
log.Warn().Msg("Command time requires one argument")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to itd UNIX socket
|
|
||||||
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error dialing socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Encode request into connection
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeSetTime,
|
|
||||||
Data: args[0],
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error making request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read one line from connetion
|
|
||||||
line, _, err := bufio.NewReader(conn).ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error reading line from connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res types.Response
|
|
||||||
// Decode line into response
|
|
||||||
err = json.Unmarshal(line, &res)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding JSON data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Error {
|
|
||||||
log.Fatal().Msg(res.Message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
setCmd.AddCommand(timeCmd)
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/cheggaaa/pb/v3"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// upgradeCmd represents the upgrade command
|
|
||||||
var upgradeCmd = &cobra.Command{
|
|
||||||
Use: "upgrade",
|
|
||||||
Short: "Upgrade InfiniTime firmware using files or archive",
|
|
||||||
Aliases: []string{"upg"},
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Connect to itd UNIX socket
|
|
||||||
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error dialing socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
var data types.ReqDataFwUpgrade
|
|
||||||
// Get relevant data struct
|
|
||||||
if viper.GetString("archive") != "" {
|
|
||||||
// Get archive data struct
|
|
||||||
data = types.ReqDataFwUpgrade{
|
|
||||||
Type: types.UpgradeTypeArchive,
|
|
||||||
Files: []string{viper.GetString("archive")},
|
|
||||||
}
|
|
||||||
} else if viper.GetString("initPkt") != "" && viper.GetString("firmware") != "" {
|
|
||||||
// Get files data struct
|
|
||||||
data = types.ReqDataFwUpgrade{
|
|
||||||
Type: types.UpgradeTypeFiles,
|
|
||||||
Files: []string{viper.GetString("initPkt"), viper.GetString("firmware")},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cmd.Usage()
|
|
||||||
log.Warn().Msg("Upgrade command requires either archive or init packet and firmware.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode response into connection
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeFwUpgrade,
|
|
||||||
Data: data,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error making request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create progress bar template
|
|
||||||
barTmpl := `{{counters . }} B {{bar . "|" "-" (cycle .) " " "|"}} {{percent . }} {{rtime . "%s"}}`
|
|
||||||
// Start full bar at 0 total
|
|
||||||
bar := pb.ProgressBarTemplate(barTmpl).Start(0)
|
|
||||||
// Create new scanner of connection
|
|
||||||
scanner := bufio.NewScanner(conn)
|
|
||||||
for scanner.Scan() {
|
|
||||||
var res types.Response
|
|
||||||
// Decode scanned line into response struct
|
|
||||||
err = json.Unmarshal(scanner.Bytes(), &res)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding JSON response")
|
|
||||||
}
|
|
||||||
if res.Error {
|
|
||||||
log.Fatal().Msg(res.Message)
|
|
||||||
}
|
|
||||||
var event DFUProgress
|
|
||||||
// Decode response data into progress struct
|
|
||||||
err = mapstructure.Decode(res.Value, &event)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding response data")
|
|
||||||
}
|
|
||||||
// Set total bytes in progress bar
|
|
||||||
bar.SetTotal(event.Total)
|
|
||||||
// Set amount of bytes received in progress bar
|
|
||||||
bar.SetCurrent(event.Received)
|
|
||||||
// If transfer finished, break
|
|
||||||
if event.Received == event.Total {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Finish progress bar
|
|
||||||
bar.Finish()
|
|
||||||
if scanner.Err() != nil {
|
|
||||||
log.Fatal().Err(scanner.Err()).Msg("Error while scanning output")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
firmwareCmd.AddCommand(upgradeCmd)
|
|
||||||
|
|
||||||
// Register flags
|
|
||||||
upgradeCmd.Flags().StringP("archive", "a", "", "Path to firmware archive")
|
|
||||||
upgradeCmd.Flags().StringP("init-pkt", "i", "", "Path to init packet (.dat file)")
|
|
||||||
upgradeCmd.Flags().StringP("firmware", "f", "", "Path to firmware image (.bin file)")
|
|
||||||
|
|
||||||
// Bind flags to viper keys
|
|
||||||
viper.BindPFlag("archive", upgradeCmd.Flags().Lookup("archive"))
|
|
||||||
viper.BindPFlag("initPkt", upgradeCmd.Flags().Lookup("init-pkt"))
|
|
||||||
viper.BindPFlag("firmware", upgradeCmd.Flags().Lookup("firmware"))
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// versionCmd represents the version command
|
|
||||||
var versionCmd = &cobra.Command{
|
|
||||||
Use: "version",
|
|
||||||
Aliases: []string{"ver"},
|
|
||||||
Short: "Get firmware version of InfiniTime",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Connect to itd UNIX socket
|
|
||||||
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error dialing socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Encode request into connection
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeFwVersion,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error making request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read one line from connection
|
|
||||||
line, _, err := bufio.NewReader(conn).ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error reading line from connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res types.Response
|
|
||||||
// Decode line into response
|
|
||||||
err = json.Unmarshal(line, &res)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error decoding JSON data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Error {
|
|
||||||
log.Fatal().Msg(res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print returned value
|
|
||||||
fmt.Println(res.Value)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
firmwareCmd.AddCommand(versionCmd)
|
|
||||||
}
|
|
||||||
80
cmd/itctl/firmware.go
Normal file
80
cmd/itctl/firmware.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cheggaaa/pb/v3"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.arsenm.dev/itd/api"
|
||||||
|
"go.arsenm.dev/itd/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fwUpgrade(c *cli.Context) error {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
var upgType api.UpgradeType
|
||||||
|
var files []string
|
||||||
|
// Get relevant data struct
|
||||||
|
if c.String("archive") != "" {
|
||||||
|
// Get archive data struct
|
||||||
|
upgType = types.UpgradeTypeArchive
|
||||||
|
files = []string{c.String("archive")}
|
||||||
|
} else if c.String("init-packet") != "" && c.String("firmware") != "" {
|
||||||
|
// Get files data struct
|
||||||
|
upgType = types.UpgradeTypeFiles
|
||||||
|
files = []string{c.String("init-packet"), c.String("firmware")}
|
||||||
|
} else {
|
||||||
|
return cli.Exit("Upgrade command requires either archive or init packet and firmware.", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
progress, err := client.FirmwareUpgrade(upgType, abs(files)...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create progress bar template
|
||||||
|
barTmpl := `{{counters . }} B {{bar . "|" "-" (cycle .) " " "|"}} {{percent . }} {{rtime . "%s"}}`
|
||||||
|
// Start full bar at 0 total
|
||||||
|
bar := pb.ProgressBarTemplate(barTmpl).Start(0)
|
||||||
|
// Create new scanner of connection
|
||||||
|
for event := range progress {
|
||||||
|
// Set total bytes in progress bar
|
||||||
|
bar.SetTotal(event.Total)
|
||||||
|
// Set amount of bytes received in progress bar
|
||||||
|
bar.SetCurrent(event.Received)
|
||||||
|
// If transfer finished, break
|
||||||
|
if event.Sent == event.Total {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finish progress bar
|
||||||
|
bar.Finish()
|
||||||
|
|
||||||
|
fmt.Printf("Transferred %d B in %s.\n", bar.Total(), time.Since(start))
|
||||||
|
fmt.Println("Remember to validate the new firmware in the InfiniTime settings.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fwVersion(c *cli.Context) error {
|
||||||
|
version, err := client.Version()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(version)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(paths []string) []string {
|
||||||
|
for index, path := range paths {
|
||||||
|
newPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
paths[index] = newPath
|
||||||
|
}
|
||||||
|
return paths
|
||||||
|
}
|
||||||
174
cmd/itctl/fs.go
Normal file
174
cmd/itctl/fs.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/cheggaaa/pb/v3"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fsList(c *cli.Context) error {
|
||||||
|
dirPath := "/"
|
||||||
|
if c.Args().Len() > 0 {
|
||||||
|
dirPath = c.Args().Get(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
listing, err := client.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range listing {
|
||||||
|
fmt.Println(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fsMkdir(c *cli.Context) error {
|
||||||
|
if c.Args().Len() < 1 {
|
||||||
|
return cli.Exit("Command mkdir requires one or more arguments", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Mkdir(c.Args().Slice()...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fsMove(c *cli.Context) error {
|
||||||
|
if c.Args().Len() != 2 {
|
||||||
|
return cli.Exit("Command move requires two arguments", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Rename(c.Args().Get(0), c.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fsRead(c *cli.Context) error {
|
||||||
|
if c.Args().Len() != 2 {
|
||||||
|
return cli.Exit("Command read requires two arguments", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpFile *os.File
|
||||||
|
var path string
|
||||||
|
var err error
|
||||||
|
if c.Args().Get(1) == "-" {
|
||||||
|
tmpFile, err = ioutil.TempFile("/tmp", "itctl.*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path = tmpFile.Name()
|
||||||
|
} else {
|
||||||
|
path, err = filepath.Abs(c.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress, err := client.ReadFile(path, c.Args().Get(0))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create progress bar template
|
||||||
|
barTmpl := `{{counters . }} B {{bar . "|" "-" (cycle .) " " "|"}} {{percent . }} {{rtime . "%s"}}`
|
||||||
|
// Start full bar at 0 total
|
||||||
|
bar := pb.ProgressBarTemplate(barTmpl).Start(0)
|
||||||
|
// Get progress events
|
||||||
|
for event := range progress {
|
||||||
|
// Set total bytes in progress bar
|
||||||
|
bar.SetTotal(int64(event.Total))
|
||||||
|
// Set amount of bytes sent in progress bar
|
||||||
|
bar.SetCurrent(int64(event.Sent))
|
||||||
|
// If transfer finished, break
|
||||||
|
if event.Done {
|
||||||
|
bar.Finish()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Args().Get(1) == "-" {
|
||||||
|
io.Copy(os.Stdout, tmpFile)
|
||||||
|
os.Stdout.WriteString("\n")
|
||||||
|
os.Stdout.Sync()
|
||||||
|
tmpFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fsRemove(c *cli.Context) error {
|
||||||
|
if c.Args().Len() < 1 {
|
||||||
|
return cli.Exit("Command remove requires one or more arguments", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Remove(c.Args().Slice()...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fsWrite(c *cli.Context) error {
|
||||||
|
if c.Args().Len() != 2 {
|
||||||
|
return cli.Exit("Command write requires two arguments", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpFile *os.File
|
||||||
|
var path string
|
||||||
|
var err error
|
||||||
|
if c.Args().Get(0) == "-" {
|
||||||
|
tmpFile, err = ioutil.TempFile("/tmp", "itctl.*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path = tmpFile.Name()
|
||||||
|
} else {
|
||||||
|
path, err = filepath.Abs(c.Args().Get(0))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Args().Get(0) == "-" {
|
||||||
|
io.Copy(tmpFile, os.Stdin)
|
||||||
|
defer tmpFile.Close()
|
||||||
|
defer os.Remove(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
progress, err := client.WriteFile(path, c.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create progress bar template
|
||||||
|
barTmpl := `{{counters . }} B {{bar . "|" "-" (cycle .) " " "|"}} {{percent . }} {{rtime . "%s"}}`
|
||||||
|
// Start full bar at 0 total
|
||||||
|
bar := pb.ProgressBarTemplate(barTmpl).Start(0)
|
||||||
|
// Get progress events
|
||||||
|
for event := range progress {
|
||||||
|
// Set total bytes in progress bar
|
||||||
|
bar.SetTotal(int64(event.Total))
|
||||||
|
// Set amount of bytes sent in progress bar
|
||||||
|
bar.SetCurrent(int64(event.Sent))
|
||||||
|
// If transfer finished, break
|
||||||
|
if event.Done {
|
||||||
|
bar.Finish()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
71
cmd/itctl/get.go
Normal file
71
cmd/itctl/get.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getAddress(c *cli.Context) error {
|
||||||
|
address, err := client.Address()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(address)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBattery(c *cli.Context) error {
|
||||||
|
battLevel, err := client.BatteryLevel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print returned percentage
|
||||||
|
fmt.Printf("%d%%\n", battLevel)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHeart(c *cli.Context) error {
|
||||||
|
heartRate, err := client.HeartRate()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print returned BPM
|
||||||
|
fmt.Printf("%d BPM\n", heartRate)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMotion(c *cli.Context) error {
|
||||||
|
motionVals, err := client.Motion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bool("shell") {
|
||||||
|
fmt.Printf(
|
||||||
|
"X=%d\nY=%d\nZ=%d\n",
|
||||||
|
motionVals.X,
|
||||||
|
motionVals.Y,
|
||||||
|
motionVals.Z,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return json.NewEncoder(os.Stdout).Encode(motionVals)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSteps(c *cli.Context) error {
|
||||||
|
stepCount, err := client.StepCount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print returned BPM
|
||||||
|
fmt.Printf("%d Steps\n", stepCount)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,36 +1,198 @@
|
|||||||
/*
|
|
||||||
* itd uses bluetooth low energy to communicate with InfiniTime devices
|
|
||||||
* Copyright (C) 2021 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"go.arsenm.dev/itd/cmd/itctl/cmd"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
var client *api.Client
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
|
||||||
|
app := cli.App{
|
||||||
|
Name: "itctl",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "socket-path",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Value: api.DefaultAddr,
|
||||||
|
Usage: "Path to itd socket",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "filesystem",
|
||||||
|
Aliases: []string{"fs"},
|
||||||
|
Usage: "Perform filesystem operations on the PineTime",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "list",
|
||||||
|
ArgsUsage: "[dir]",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List a directory",
|
||||||
|
Action: fsList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "mkdir",
|
||||||
|
ArgsUsage: "<paths...>",
|
||||||
|
Usage: "Create new directories",
|
||||||
|
Action: fsMkdir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "move",
|
||||||
|
ArgsUsage: "<old> <new>",
|
||||||
|
Aliases: []string{"mv"},
|
||||||
|
Usage: "Move a file or directory",
|
||||||
|
Action: fsMove,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "read",
|
||||||
|
ArgsUsage: `<remote path> <local path>`,
|
||||||
|
Usage: "Read a file from InfiniTime.",
|
||||||
|
Description: `Read is used to read files from InfiniTime's filesystem. A "-" can be used to signify stdout`,
|
||||||
|
Action: fsRead,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "remove",
|
||||||
|
ArgsUsage: "<paths...>",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Remove a file from InfiniTime",
|
||||||
|
Action: fsRemove,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "write",
|
||||||
|
ArgsUsage: `<local path> <remote path>`,
|
||||||
|
Usage: "Write a file to InfiniTime",
|
||||||
|
Description: `Write is used to write files to InfiniTime's filesystem. A "-" can be used to signify stdin`,
|
||||||
|
Action: fsWrite,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "firmware",
|
||||||
|
Aliases: []string{"fw"},
|
||||||
|
Usage: "Manage InfiniTime firmware",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.PathFlag{
|
||||||
|
Name: "init-packet",
|
||||||
|
Aliases: []string{"i"},
|
||||||
|
Usage: "Path to init packet (.dat file)",
|
||||||
|
},
|
||||||
|
&cli.PathFlag{
|
||||||
|
Name: "firmware",
|
||||||
|
Aliases: []string{"f"},
|
||||||
|
Usage: "Path to firmware image (.bin file)",
|
||||||
|
},
|
||||||
|
&cli.PathFlag{
|
||||||
|
Name: "archive",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "Path to firmware archive (.zip file)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Name: "upgrade",
|
||||||
|
Aliases: []string{"upg"},
|
||||||
|
Usage: "Upgrade InfiniTime firmware using files or archive",
|
||||||
|
Action: fwUpgrade,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version",
|
||||||
|
Aliases: []string{"ver"},
|
||||||
|
Usage: "Get firmware version of InfiniTime",
|
||||||
|
Action: fwVersion,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "get",
|
||||||
|
Usage: "Get information from InfiniTime",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "address",
|
||||||
|
Aliases: []string{"addr"},
|
||||||
|
Usage: "Get InfiniTime's bluetooth address",
|
||||||
|
Action: getAddress,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "battery",
|
||||||
|
Aliases: []string{"batt"},
|
||||||
|
Usage: "Get InfiniTime's battery percentage",
|
||||||
|
Action: getBattery,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "heart",
|
||||||
|
Usage: "Get heart rate from InfiniTime",
|
||||||
|
Action: getHeart,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{Name: "shell"},
|
||||||
|
},
|
||||||
|
Name: "motion",
|
||||||
|
Usage: "Get motion values from InfiniTime",
|
||||||
|
Action: getMotion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "steps",
|
||||||
|
Usage: "Get step count from InfiniTime",
|
||||||
|
Action: getSteps,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "notify",
|
||||||
|
Usage: "Send notification to InfiniTime",
|
||||||
|
Action: notify,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "set",
|
||||||
|
Usage: "Set information on InfiniTime",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
ArgsUsage: `<ISO8601|"now">`,
|
||||||
|
Usage: "Set InfiniTime's clock to specified time",
|
||||||
|
Action: setTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "update",
|
||||||
|
Usage: "Update information on InfiniTime",
|
||||||
|
Aliases: []string{"upd"},
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "weather",
|
||||||
|
Usage: "Force an immediate update of weather data",
|
||||||
|
Action: updateWeather,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Before: func(c *cli.Context) error {
|
||||||
|
newClient, err := api.New(c.String("socket-path"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client = newClient
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
After: func(*cli.Context) error {
|
||||||
|
if client != nil {
|
||||||
|
client.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Error while running app")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
cmd/itctl/notify.go
Normal file
17
cmd/itctl/notify.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
func notify(c *cli.Context) error {
|
||||||
|
// Ensure required arguments
|
||||||
|
if c.Args().Len() != 2 {
|
||||||
|
return cli.Exit("Command notify requires two arguments", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Notify(c.Args().Get(0), c.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
24
cmd/itctl/set.go
Normal file
24
cmd/itctl/set.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setTime(c *cli.Context) error {
|
||||||
|
// Ensure required arguments
|
||||||
|
if c.Args().Len() < 1 {
|
||||||
|
return cli.Exit("Command time requires one argument", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Args().Get(0) == "now" {
|
||||||
|
return client.SetTimeNow()
|
||||||
|
} else {
|
||||||
|
parsed, err := time.Parse(time.RFC3339, c.Args().Get(0))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.SetTime(parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
cmd/itctl/update.go
Normal file
7
cmd/itctl/update.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
func updateWeather(c *cli.Context) error {
|
||||||
|
return client.UpdateWeather()
|
||||||
|
}
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"net"
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"go.arsenm.dev/itd/internal/types"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func infoTab(parent fyne.Window) *fyne.Container {
|
func infoTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
||||||
infoLayout := container.NewVBox(
|
infoLayout := container.NewVBox(
|
||||||
// Add rectangle for a bit of padding
|
// Add rectangle for a bit of padding
|
||||||
canvas.NewRectangle(color.Transparent),
|
canvas.NewRectangle(color.Transparent),
|
||||||
@@ -25,20 +20,50 @@ func infoTab(parent fyne.Window) *fyne.Container {
|
|||||||
// Create label for heart rate
|
// Create label for heart rate
|
||||||
heartRateLbl := newText("0 BPM", 24)
|
heartRateLbl := newText("0 BPM", 24)
|
||||||
// Creae container to store heart rate section
|
// Creae container to store heart rate section
|
||||||
heartRate := container.NewVBox(
|
heartRateSect := container.NewVBox(
|
||||||
newText("Heart Rate", 12),
|
newText("Heart Rate", 12),
|
||||||
heartRateLbl,
|
heartRateLbl,
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
canvas.NewLine(theme.ShadowColor()),
|
||||||
)
|
)
|
||||||
infoLayout.Add(heartRate)
|
infoLayout.Add(heartRateSect)
|
||||||
|
|
||||||
// Watch for heart rate updates
|
heartRateCh, cancel, err := client.WatchHeartRate()
|
||||||
go watch(types.ReqTypeWatchHeartRate, func(data interface{}) {
|
if err != nil {
|
||||||
// Change text of heart rate label
|
guiErr(err, "Error getting heart rate channel", true, parent)
|
||||||
heartRateLbl.Text = fmt.Sprintf("%d BPM", int(data.(float64)))
|
}
|
||||||
// Refresh label
|
onClose = append(onClose, cancel)
|
||||||
heartRateLbl.Refresh()
|
go func() {
|
||||||
}, parent)
|
for heartRate := range heartRateCh {
|
||||||
|
// Change text of heart rate label
|
||||||
|
heartRateLbl.Text = fmt.Sprintf("%d BPM", heartRate)
|
||||||
|
// Refresh label
|
||||||
|
heartRateLbl.Refresh()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create label for heart rate
|
||||||
|
stepCountLbl := newText("0 Steps", 24)
|
||||||
|
// Creae container to store heart rate section
|
||||||
|
stepCountSect := container.NewVBox(
|
||||||
|
newText("Step Count", 12),
|
||||||
|
stepCountLbl,
|
||||||
|
canvas.NewLine(theme.ShadowColor()),
|
||||||
|
)
|
||||||
|
infoLayout.Add(stepCountSect)
|
||||||
|
|
||||||
|
stepCountCh, cancel, err := client.WatchStepCount()
|
||||||
|
if err != nil {
|
||||||
|
guiErr(err, "Error getting step count channel", true, parent)
|
||||||
|
}
|
||||||
|
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
|
// Create label for battery level
|
||||||
battLevelLbl := newText("0%", 24)
|
battLevelLbl := newText("0%", 24)
|
||||||
@@ -50,32 +75,40 @@ func infoTab(parent fyne.Window) *fyne.Container {
|
|||||||
)
|
)
|
||||||
infoLayout.Add(battLevel)
|
infoLayout.Add(battLevel)
|
||||||
|
|
||||||
// Watch for changes in battery level
|
battLevelCh, cancel, err := client.WatchBatteryLevel()
|
||||||
go watch(types.ReqTypeWatchBattLevel, func(data interface{}) {
|
if err != nil {
|
||||||
battLevelLbl.Text = fmt.Sprintf("%d%%", int(data.(float64)))
|
guiErr(err, "Error getting battery level channel", true, parent)
|
||||||
battLevelLbl.Refresh()
|
}
|
||||||
}, parent)
|
onClose = append(onClose, cancel)
|
||||||
|
go func() {
|
||||||
|
for battLevel := range battLevelCh {
|
||||||
|
// Change text of battery level label
|
||||||
|
battLevelLbl.Text = fmt.Sprintf("%d%%", battLevel)
|
||||||
|
// Refresh label
|
||||||
|
battLevelLbl.Refresh()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
fwVerString, err := get(types.ReqTypeFwVersion)
|
fwVerString, err := client.Version()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error getting firmware string", true, parent)
|
guiErr(err, "Error getting firmware string", true, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fwVer := container.NewVBox(
|
fwVer := container.NewVBox(
|
||||||
newText("Firmware Version", 12),
|
newText("Firmware Version", 12),
|
||||||
newText(fwVerString.(string), 24),
|
newText(fwVerString, 24),
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
canvas.NewLine(theme.ShadowColor()),
|
||||||
)
|
)
|
||||||
infoLayout.Add(fwVer)
|
infoLayout.Add(fwVer)
|
||||||
|
|
||||||
btAddrString, err := get(types.ReqTypeBtAddress)
|
btAddrString, err := client.Address()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
btAddr := container.NewVBox(
|
btAddr := container.NewVBox(
|
||||||
newText("Bluetooth Address", 12),
|
newText("Bluetooth Address", 12),
|
||||||
newText(btAddrString.(string), 24),
|
newText(btAddrString, 24),
|
||||||
canvas.NewLine(theme.ShadowColor()),
|
canvas.NewLine(theme.ShadowColor()),
|
||||||
)
|
)
|
||||||
infoLayout.Add(btAddr)
|
infoLayout.Add(btAddr)
|
||||||
@@ -83,65 +116,6 @@ func infoTab(parent fyne.Window) *fyne.Container {
|
|||||||
return infoLayout
|
return infoLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
func watch(req int, onRecv func(data interface{}), parent fyne.Window) error {
|
|
||||||
conn, err := net.Dial("unix", SockPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: req,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
scanner := bufio.NewScanner(conn)
|
|
||||||
for scanner.Scan() {
|
|
||||||
res, err := getResp(scanner.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
guiErr(err, "Error getting response from connection", false, parent)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
onRecv(res.Value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(req int) (interface{}, error) {
|
|
||||||
conn, err := net.Dial("unix", SockPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: req,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
line, _, err := bufio.NewReader(conn).ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res, err := getResp(line)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res.Value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResp(line []byte) (*types.Response, error) {
|
|
||||||
var res types.Response
|
|
||||||
err := json.Unmarshal(line, &res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if res.Error {
|
|
||||||
return nil, errors.New(res.Message)
|
|
||||||
}
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newText(t string, size float32) *canvas.Text {
|
func newText(t string, size float32) *canvas.Text {
|
||||||
text := canvas.NewText(t, theme.ForegroundColor())
|
text := canvas.NewText(t, theme.ForegroundColor())
|
||||||
text.TextSize = size
|
text.TextSize = size
|
||||||
|
|||||||
@@ -1,31 +1,39 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SockPath = "/tmp/itd/socket"
|
var onClose []func()
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create new app
|
// Create new app
|
||||||
a := app.New()
|
a := app.New()
|
||||||
// Create new window with title "itgui"
|
// Create new window with title "itgui"
|
||||||
window := a.NewWindow("itgui")
|
window := a.NewWindow("itgui")
|
||||||
|
window.SetOnClosed(func() {
|
||||||
|
for _, closeFn := range onClose {
|
||||||
|
closeFn()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
_, err := net.Dial("unix", SockPath)
|
client, err := api.New(api.DefaultAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error dialing itd socket", true, window)
|
guiErr(err, "Error connecting to itd", true, window)
|
||||||
}
|
}
|
||||||
|
onClose = append(onClose, func() {
|
||||||
|
client.Close()
|
||||||
|
})
|
||||||
|
|
||||||
// Create new app tabs container
|
// Create new app tabs container
|
||||||
tabs := container.NewAppTabs(
|
tabs := container.NewAppTabs(
|
||||||
container.NewTabItem("Info", infoTab(window)),
|
container.NewTabItem("Info", infoTab(window, client)),
|
||||||
container.NewTabItem("Notify", notifyTab(window)),
|
container.NewTabItem("Motion", motionTab(window, client)),
|
||||||
container.NewTabItem("Set Time", timeTab(window)),
|
container.NewTabItem("Notify", notifyTab(window, client)),
|
||||||
container.NewTabItem("Upgrade", upgradeTab(window)),
|
container.NewTabItem("Set Time", timeTab(window, client)),
|
||||||
|
container.NewTabItem("Upgrade", upgradeTab(window, client)),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set tabs as window content
|
// Set tabs as window content
|
||||||
|
|||||||
105
cmd/itgui/motion.go
Normal file
105
cmd/itgui/motion.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/canvas"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/theme"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"go.arsenm.dev/itd/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func motionTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
||||||
|
// Create label for heart rate
|
||||||
|
xCoordLbl := newText("0", 24)
|
||||||
|
// Creae container to store heart rate section
|
||||||
|
xCoordSect := container.NewVBox(
|
||||||
|
newText("X Coordinate", 12),
|
||||||
|
xCoordLbl,
|
||||||
|
canvas.NewLine(theme.ShadowColor()),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create label for heart rate
|
||||||
|
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
|
||||||
|
started := false
|
||||||
|
|
||||||
|
// Create button to stop motion
|
||||||
|
stopBtn := widget.NewButton("Stop", nil)
|
||||||
|
// Create button to start motion
|
||||||
|
startBtn := widget.NewButton("Start", func() {
|
||||||
|
// if motion is started
|
||||||
|
if started {
|
||||||
|
// Do nothing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Set motion started
|
||||||
|
started = true
|
||||||
|
// Watch motion values
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Create stop function
|
||||||
|
stopBtn.OnTapped = func() {
|
||||||
|
done <- struct{}{}
|
||||||
|
started = false
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
// Run stop button function on close if possible
|
||||||
|
onClose = append(onClose, func() {
|
||||||
|
if stopBtn.OnTapped != nil {
|
||||||
|
stopBtn.OnTapped()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 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,17 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"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"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
"go.arsenm.dev/itd/internal/types"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func notifyTab(parent fyne.Window) *fyne.Container {
|
func notifyTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
||||||
// Create new entry for notification title
|
// Create new entry for notification title
|
||||||
titleEntry := widget.NewEntry()
|
titleEntry := widget.NewEntry()
|
||||||
titleEntry.SetPlaceHolder("Title")
|
titleEntry.SetPlaceHolder("Title")
|
||||||
@@ -22,20 +19,11 @@ func notifyTab(parent fyne.Window) *fyne.Container {
|
|||||||
|
|
||||||
// Create new button to send notification
|
// Create new button to send notification
|
||||||
sendBtn := widget.NewButton("Send", func() {
|
sendBtn := widget.NewButton("Send", func() {
|
||||||
// Dial itd UNIX socket
|
err := client.Notify(titleEntry.Text, bodyEntry.Text)
|
||||||
conn, err := net.Dial("unix", SockPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error dialing socket", false, parent)
|
guiErr(err, "Error sending notification", false, parent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Encode notify request on connection
|
|
||||||
json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeNotify,
|
|
||||||
Data: types.ReqDataNotify{
|
|
||||||
Title: titleEntry.Text,
|
|
||||||
Body: bodyEntry.Text,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Return new container containing all elements
|
// Return new container containing all elements
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
"go.arsenm.dev/itd/internal/types"
|
"go.arsenm.dev/itd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func timeTab(parent fyne.Window) *fyne.Container {
|
func timeTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
||||||
// Create new entry for time string
|
// Create new entry for time string
|
||||||
timeEntry := widget.NewEntry()
|
timeEntry := widget.NewEntry()
|
||||||
// Set text to current time formatter properly
|
// Set text to current time formatter properly
|
||||||
@@ -21,7 +19,7 @@ func timeTab(parent fyne.Window) *fyne.Container {
|
|||||||
// Create button to set current time
|
// Create button to set current time
|
||||||
currentBtn := widget.NewButton("Set Current", func() {
|
currentBtn := widget.NewButton("Set Current", func() {
|
||||||
timeEntry.SetText(time.Now().Format(time.RFC1123))
|
timeEntry.SetText(time.Now().Format(time.RFC1123))
|
||||||
setTime(true)
|
setTime(client, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create button to set time inside entry
|
// Create button to set time inside entry
|
||||||
@@ -33,7 +31,7 @@ func timeTab(parent fyne.Window) *fyne.Container {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Set time to parsed time
|
// Set time to parsed time
|
||||||
setTime(false, parsedTime)
|
setTime(client, false, parsedTime)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Return new container with all elements centered
|
// Return new container with all elements centered
|
||||||
@@ -48,30 +46,13 @@ func timeTab(parent fyne.Window) *fyne.Container {
|
|||||||
|
|
||||||
// setTime sets the first element in the variadic parameter
|
// setTime sets the first element in the variadic parameter
|
||||||
// if current is false, otherwise, it sets the current time.
|
// if current is false, otherwise, it sets the current time.
|
||||||
func setTime(current bool, t ...time.Time) error {
|
func setTime(client *api.Client, current bool, t ...time.Time) error {
|
||||||
// Dial UNIX socket
|
var err error
|
||||||
conn, err := net.Dial("unix", SockPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
var data string
|
|
||||||
// If current is true, use the string "now"
|
|
||||||
// otherwise, use the formatted time from the
|
|
||||||
// first element in the variadic parameter.
|
|
||||||
// "now" is more accurate than formatting
|
|
||||||
// current time as only seconds are preserved
|
|
||||||
// in that case.
|
|
||||||
if current {
|
if current {
|
||||||
data = "now"
|
err = client.SetTimeNow()
|
||||||
} else {
|
} else {
|
||||||
data = t[0].Format(time.RFC3339)
|
err = client.SetTime(t[0])
|
||||||
}
|
}
|
||||||
// Encode SetTime request with above data
|
|
||||||
err = json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeSetTime,
|
|
||||||
Data: data,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"path/filepath"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -12,17 +10,18 @@ import (
|
|||||||
"fyne.io/fyne/v2/layout"
|
"fyne.io/fyne/v2/layout"
|
||||||
"fyne.io/fyne/v2/storage"
|
"fyne.io/fyne/v2/storage"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
"github.com/mitchellh/mapstructure"
|
"go.arsenm.dev/itd/api"
|
||||||
"go.arsenm.dev/itd/internal/types"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func upgradeTab(parent fyne.Window) *fyne.Container {
|
func upgradeTab(parent fyne.Window, client *api.Client) *fyne.Container {
|
||||||
var (
|
var (
|
||||||
archivePath string
|
archivePath string
|
||||||
fiwmarePath string
|
firmwarePath string
|
||||||
initPktPath string
|
initPktPath string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var archiveBtn *widget.Button
|
||||||
// Create archive selection dialog
|
// Create archive selection dialog
|
||||||
archiveDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
archiveDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
||||||
if e != nil || uc == nil {
|
if e != nil || uc == nil {
|
||||||
@@ -30,25 +29,29 @@ func upgradeTab(parent fyne.Window) *fyne.Container {
|
|||||||
}
|
}
|
||||||
uc.Close()
|
uc.Close()
|
||||||
archivePath = uc.URI().Path()
|
archivePath = uc.URI().Path()
|
||||||
|
archiveBtn.SetText(fmt.Sprintf("Select archive (.zip) [%s]", filepath.Base(archivePath)))
|
||||||
}, parent)
|
}, parent)
|
||||||
// Limit dialog to .zip files
|
// Limit dialog to .zip files
|
||||||
archiveDialog.SetFilter(storage.NewExtensionFileFilter([]string{".zip"}))
|
archiveDialog.SetFilter(storage.NewExtensionFileFilter([]string{".zip"}))
|
||||||
// Create button to show dialog
|
// Create button to show dialog
|
||||||
archiveBtn := widget.NewButton("Select archive (.zip)", archiveDialog.Show)
|
archiveBtn = widget.NewButton("Select archive (.zip)", archiveDialog.Show)
|
||||||
|
|
||||||
|
var firmwareBtn *widget.Button
|
||||||
// Create firmware selection dialog
|
// Create firmware selection dialog
|
||||||
firmwareDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
firmwareDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
||||||
if e != nil || uc == nil {
|
if e != nil || uc == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
uc.Close()
|
uc.Close()
|
||||||
fiwmarePath = uc.URI().Path()
|
firmwarePath = uc.URI().Path()
|
||||||
|
firmwareBtn.SetText(fmt.Sprintf("Select firmware (.bin) [%s]", filepath.Base(firmwarePath)))
|
||||||
}, parent)
|
}, parent)
|
||||||
// Limit dialog to .bin files
|
// Limit dialog to .bin files
|
||||||
firmwareDialog.SetFilter(storage.NewExtensionFileFilter([]string{".bin"}))
|
firmwareDialog.SetFilter(storage.NewExtensionFileFilter([]string{".bin"}))
|
||||||
// Create button to show dialog
|
// Create button to show dialog
|
||||||
firmwareBtn := widget.NewButton("Select init packet (.bin)", firmwareDialog.Show)
|
firmwareBtn = widget.NewButton("Select firmware (.bin)", firmwareDialog.Show)
|
||||||
|
|
||||||
|
var initPktBtn *widget.Button
|
||||||
// Create init packet selection dialog
|
// Create init packet selection dialog
|
||||||
initPktDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
initPktDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
|
||||||
if e != nil || uc == nil {
|
if e != nil || uc == nil {
|
||||||
@@ -56,11 +59,12 @@ func upgradeTab(parent fyne.Window) *fyne.Container {
|
|||||||
}
|
}
|
||||||
uc.Close()
|
uc.Close()
|
||||||
initPktPath = uc.URI().Path()
|
initPktPath = uc.URI().Path()
|
||||||
|
initPktBtn.SetText(fmt.Sprintf("Select init packet (.dat) [%s]", filepath.Base(initPktPath)))
|
||||||
}, parent)
|
}, parent)
|
||||||
// Limit dialog to .dat files
|
// Limit dialog to .dat files
|
||||||
initPktDialog.SetFilter(storage.NewExtensionFileFilter([]string{".dat"}))
|
initPktDialog.SetFilter(storage.NewExtensionFileFilter([]string{".dat"}))
|
||||||
// Create button to show dialog
|
// Create button to show dialog
|
||||||
initPktBtn := widget.NewButton("Select init packet (.dat)", initPktDialog.Show)
|
initPktBtn = widget.NewButton("Select init packet (.dat)", initPktDialog.Show)
|
||||||
|
|
||||||
// Hide init packet and firmware buttons
|
// Hide init packet and firmware buttons
|
||||||
initPktBtn.Hide()
|
initPktBtn.Hide()
|
||||||
@@ -91,7 +95,7 @@ func upgradeTab(parent fyne.Window) *fyne.Container {
|
|||||||
startBtn := widget.NewButton("Start", func() {
|
startBtn := widget.NewButton("Start", func() {
|
||||||
// If archive path does not exist and both init packet and firmware paths
|
// If archive path does not exist and both init packet and firmware paths
|
||||||
// also do not exist, return error
|
// also do not exist, return error
|
||||||
if archivePath == "" && (initPktPath == "" && fiwmarePath == "") {
|
if archivePath == "" && (initPktPath == "" && firmwarePath == "") {
|
||||||
guiErr(nil, "Upgrade requires archive or files selected", false, parent)
|
guiErr(nil, "Upgrade requires archive or files selected", false, parent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -110,7 +114,7 @@ func upgradeTab(parent fyne.Window) *fyne.Container {
|
|||||||
// Resize modal to 300x100
|
// Resize modal to 300x100
|
||||||
progressDlg.Resize(fyne.NewSize(300, 100))
|
progressDlg.Resize(fyne.NewSize(300, 100))
|
||||||
|
|
||||||
var fwUpgType int
|
var fwUpgType api.UpgradeType
|
||||||
var files []string
|
var files []string
|
||||||
// Get appropriate upgrade type and file paths
|
// Get appropriate upgrade type and file paths
|
||||||
switch upgradeTypeSelect.Selected {
|
switch upgradeTypeSelect.Selected {
|
||||||
@@ -119,51 +123,19 @@ func upgradeTab(parent fyne.Window) *fyne.Container {
|
|||||||
files = append(files, archivePath)
|
files = append(files, archivePath)
|
||||||
case "Files":
|
case "Files":
|
||||||
fwUpgType = types.UpgradeTypeFiles
|
fwUpgType = types.UpgradeTypeFiles
|
||||||
files = append(files, initPktPath, fiwmarePath)
|
files = append(files, initPktPath, firmwarePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial itd UNIX socket
|
progress, err := client.FirmwareUpgrade(fwUpgType, files...)
|
||||||
conn, err := net.Dial("unix", SockPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guiErr(err, "Error dialing socket", false, parent)
|
guiErr(err, "Error initiating DFU", false, parent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Encode firmware upgrade request to connection
|
|
||||||
json.NewEncoder(conn).Encode(types.Request{
|
|
||||||
Type: types.ReqTypeFwUpgrade,
|
|
||||||
Data: types.ReqDataFwUpgrade{
|
|
||||||
Type: fwUpgType,
|
|
||||||
Files: files,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Show progress dialog
|
// Show progress dialog
|
||||||
progressDlg.Show()
|
progressDlg.Show()
|
||||||
// Hide progress dialog after completion
|
|
||||||
defer progressDlg.Hide()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(conn)
|
for event := range progress {
|
||||||
for scanner.Scan() {
|
|
||||||
var res types.Response
|
|
||||||
// Decode scanned line into response struct
|
|
||||||
err = json.Unmarshal(scanner.Bytes(), &res)
|
|
||||||
if err != nil {
|
|
||||||
guiErr(err, "Error decoding response", false, parent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if res.Error {
|
|
||||||
guiErr(err, "Error returned in response", false, parent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var event types.DFUProgress
|
|
||||||
// Decode response data into progress struct
|
|
||||||
err = mapstructure.Decode(res.Value, &event)
|
|
||||||
if err != nil {
|
|
||||||
guiErr(err, "Error decoding response value", false, parent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Set label text to received / total B
|
// Set label text to received / total B
|
||||||
progressLbl.SetText(fmt.Sprintf("%d / %d B", event.Received, event.Total))
|
progressLbl.SetText(fmt.Sprintf("%d / %d B", event.Received, event.Total))
|
||||||
// Set progress bar values
|
// Set progress bar values
|
||||||
@@ -172,10 +144,28 @@ func upgradeTab(parent fyne.Window) *fyne.Container {
|
|||||||
// Refresh progress bar
|
// Refresh progress bar
|
||||||
progressBar.Refresh()
|
progressBar.Refresh()
|
||||||
// If transfer finished, break
|
// If transfer finished, break
|
||||||
if event.Received == event.Total {
|
if event.Sent == event.Total {
|
||||||
break
|
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 containing all elements
|
||||||
|
|||||||
76
config.go
76
config.go
@@ -2,46 +2,78 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/knadh/koanf/parsers/toml"
|
||||||
|
"github.com/knadh/koanf/providers/confmap"
|
||||||
|
"github.com/knadh/koanf/providers/env"
|
||||||
|
"github.com/knadh/koanf/providers/file"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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})
|
||||||
|
|
||||||
// Set config settings
|
// Get user's configuration directory
|
||||||
|
cfgDir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set config defaults
|
||||||
setCfgDefaults()
|
setCfgDefaults()
|
||||||
viper.AddConfigPath("$HOME/.config")
|
|
||||||
viper.AddConfigPath("/etc")
|
// Load config files
|
||||||
viper.SetConfigName("itd")
|
etcProvider := file.Provider("/etc/itd.toml")
|
||||||
viper.SetConfigType("toml")
|
cfgProvider := file.Provider(filepath.Join(cfgDir, "itd.toml"))
|
||||||
viper.WatchConfig()
|
k.Load(etcProvider, toml.Parser())
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
k.Load(cfgProvider, toml.Parser())
|
||||||
viper.SetEnvPrefix("itd")
|
|
||||||
// Ignore error because defaults set
|
// Watch configs for changes
|
||||||
viper.ReadInConfig()
|
cfgWatch(etcProvider)
|
||||||
viper.AutomaticEnv()
|
cfgWatch(cfgProvider)
|
||||||
|
|
||||||
|
// Load envireonment variables
|
||||||
|
k.Load(env.Provider("ITD_", "_", func(s string) string {
|
||||||
|
return strings.ToLower(strings.TrimPrefix(s, "ITD_"))
|
||||||
|
}), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cfgWatch(provider *file.File) {
|
||||||
|
// Watch for changes and reload when detected
|
||||||
|
provider.Watch(func(_ interface{}, err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
k.Load(provider, toml.Parser())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func setCfgDefaults() {
|
func setCfgDefaults() {
|
||||||
viper.SetDefault("cfg.version", 2)
|
k.Load(confmap.Provider(map[string]interface{}{
|
||||||
|
"socket.path": "/tmp/itd/socket",
|
||||||
|
|
||||||
viper.SetDefault("socket.path", "/tmp/itd/socket")
|
"conn.reconnect": true,
|
||||||
|
|
||||||
viper.SetDefault("conn.reconnect", true)
|
"conn.whitelist.enabled": false,
|
||||||
|
"conn.whitelist.devices": []string{},
|
||||||
|
|
||||||
viper.SetDefault("on.connect.notify", true)
|
"on.connect.notify": true,
|
||||||
|
|
||||||
viper.SetDefault("on.reconnect.notify", true)
|
"on.reconnect.notify": true,
|
||||||
viper.SetDefault("on.reconnect.setTime", true)
|
"on.reconnect.setTime": true,
|
||||||
|
|
||||||
viper.SetDefault("notifs.ignore.sender", []string{})
|
"notifs.translit.use": []string{"eASCII"},
|
||||||
viper.SetDefault("notifs.ignore.summary", []string{"InfiniTime"})
|
"notifs.translit.custom": []string{},
|
||||||
viper.SetDefault("notifs.ignore.body", []string{})
|
|
||||||
|
|
||||||
viper.SetDefault("music.vol.interval", 5)
|
"notifs.ignore.sender": []string{},
|
||||||
|
"notifs.ignore.summary": []string{"InfiniTime"},
|
||||||
|
"notifs.ignore.body": []string{},
|
||||||
|
|
||||||
|
"music.vol.interval": 5,
|
||||||
|
}, "."), nil)
|
||||||
}
|
}
|
||||||
|
|||||||
37
dbus.go
Normal file
37
dbus.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/godbus/dbus/v5"
|
||||||
|
|
||||||
|
func newSystemBusConn() (*dbus.Conn, error) {
|
||||||
|
// Connect to dbus session bus
|
||||||
|
conn, err := dbus.SystemBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Auth(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Hello()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSessionBusConn() (*dbus.Conn, error) {
|
||||||
|
// Connect to dbus session bus
|
||||||
|
conn, err := dbus.SessionBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Auth(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = conn.Hello()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
42
go.mod
42
go.mod
@@ -3,25 +3,35 @@ module go.arsenm.dev/itd
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/fyne/v2 v2.0.4
|
fyne.io/fyne/v2 v2.1.2
|
||||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible
|
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8
|
github.com/cheggaaa/pb/v3 v3.0.8
|
||||||
github.com/fatih/color v1.12.0 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.0 // indirect
|
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.0.4
|
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b
|
||||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
|
||||||
|
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/mattn/go-colorable v0.1.12 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.14
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.1
|
github.com/mitchellh/mapstructure v1.4.3
|
||||||
github.com/mozillazg/go-pinyin v0.18.0
|
github.com/mozillazg/go-pinyin v0.19.0
|
||||||
github.com/rs/zerolog v1.23.0
|
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||||
|
github.com/rs/zerolog v1.26.0
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/srwiley/oksvg v0.0.0-20211120171407-1837d6608d8c // indirect
|
||||||
github.com/spf13/cobra v1.2.1
|
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 // indirect
|
||||||
github.com/spf13/viper v1.8.1
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
go.arsenm.dev/infinitime v0.0.0-20210825051734-745b4bd37cf4
|
github.com/yuin/goldmark v1.4.4 // indirect
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // 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
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
636
go.sum
636
go.sum
@@ -1,668 +1,296 @@
|
|||||||
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=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
fyne.io/fyne/v2 v2.1.2 h1:avp9CvLAUdvE7fDMtH1tVKyjxEWHWcpow6aI6L7Kvvw=
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
fyne.io/fyne/v2 v2.1.2/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
|
||||||
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.0.4 h1:eDGaPGzeR4qNqWuAp9Li1kY4eVIHldCkf42KMakKIK4=
|
|
||||||
fyne.io/fyne/v2 v2.0.4/go.mod h1:nNpgL7sZkDVLraGtQII2ArNRnnl6kHup/KfQRxIhbvs=
|
|
||||||
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/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
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/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 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
|
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
|
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
|
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
|
|
||||||
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/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/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=
|
||||||
|
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/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 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
|
||||||
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 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
|
||||||
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 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/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/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/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
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/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
|
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
|
||||||
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.0 h1:NO5hkcB+srp1x6QmwvNZLeaOgbM8cmBTN32THzjvu2k=
|
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||||
github.com/fsnotify/fsnotify v1.5.0/go.mod h1:BX0DCEr5pT4jm2CnQdVP1lFV521fcCNcyEeNp4DQQDk=
|
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||||
github.com/fyne-io/mobile v0.1.3-0.20210412090810-650a3139866a h1:3TAJhl8vXyli0tooKB0vd6gLCyBdWL4QEYbDoJpHEZk=
|
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||||
github.com/fyne-io/mobile v0.1.3-0.20210412090810-650a3139866a/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
|
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b h1:M0/hjawi9ur15zpqL/h66ga87jlYA7iAuZ4HC6ak08k=
|
||||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
|
github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU=
|
||||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
|
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be/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-20211204153444-caad923f49f4 h1:KgfIc81yNEUKNAsF+Mt3C1Cl+iQqKF1r7nWEKzL0c2Y=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb h1:T6gaWBvRzJjuOrdCtg8fXXjKai2xSDqWTcKFUPuw8Tw=
|
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-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||||
|
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/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 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
|
||||||
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/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
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/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
||||||
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/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/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
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/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/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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
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/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.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.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-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||||
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.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
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/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
|
||||||
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/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/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
|
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/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/knadh/koanf v1.4.0 h1:/k0Bh49SqLyLNfte9r6cvuZWrApOQhglOmhIU3L/zDw=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/knadh/koanf v1.4.0/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
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/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-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
|
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
||||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
|
||||||
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.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
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/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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
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/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
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 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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
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 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
|
||||||
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mozillazg/go-pinyin v0.18.0 h1:hQompXO23/0ohH8YNjvfsAITnCQImCiR/Fny8EhIeW0=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mozillazg/go-pinyin v0.18.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20210812063148-b6c83362e27d h1:EG/xyWjHT19rkUpwsWSkyiCCmyqNwFovr9m10rhyOxU=
|
github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20210812063148-b6c83362e27d/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
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-20220219050759-674a63b8741a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
||||||
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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
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/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.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||||
|
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
|
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/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/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
|
||||||
github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g=
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo=
|
|
||||||
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/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/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
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/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/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 h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
|
||||||
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 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
|
||||||
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/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
|
||||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
|
||||||
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
|
|
||||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
|
||||||
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 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
|
|
||||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
|
||||||
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
|
|
||||||
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/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
|
github.com/srwiley/oksvg v0.0.0-20211120171407-1837d6608d8c h1:+e9myEHblxwU1r2Jb5PKzepMcsuig7+NUz+K53lBNaQ=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20211120171407-1837d6608d8c/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
||||||
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/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 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/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/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
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/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
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=
|
||||||
go.arsenm.dev/infinitime v0.0.0-20210825051734-745b4bd37cf4 h1:XZyynxrvGxP0mwyhdiuMrvj5SkiK6N+MDiC6DiGzgWU=
|
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.arsenm.dev/infinitime v0.0.0-20210825051734-745b4bd37cf4/go.mod h1:gaepaueUz4J5FfxuV19B4w5pi+V3mD0LTef50ryxr/Q=
|
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
go.arsenm.dev/infinitime v0.0.0-20220416112421-b7a50271bece h1:ns/GMc4E8ZUZ9TEXhXgU4M+5sRaOLTFFoBWEJ67p3YM=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.arsenm.dev/infinitime v0.0.0-20220416112421-b7a50271bece/go.mod h1:Prvwx7Y2y8HsNRA1tPptduW9jzuw/JffmocvoHcDbYo=
|
||||||
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/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-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 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
|
||||||
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/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/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/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-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 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
|
||||||
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-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
||||||
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
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-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-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-20190916202348-b4ddaad3f8a3/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-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-20200720211630-cb9d2d5c5666/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-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-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-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/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-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
|
||||||
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/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/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.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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/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-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
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-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
|
||||||
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.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
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/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/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-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
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.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.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||||
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/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/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
|
||||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
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=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
@@ -672,12 +300,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
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=
|
||||||
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=
|
|
||||||
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,5 +1,10 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ReqTypeHeartRate = iota
|
ReqTypeHeartRate = iota
|
||||||
ReqTypeBattLevel
|
ReqTypeBattLevel
|
||||||
@@ -10,6 +15,13 @@ const (
|
|||||||
ReqTypeSetTime
|
ReqTypeSetTime
|
||||||
ReqTypeWatchHeartRate
|
ReqTypeWatchHeartRate
|
||||||
ReqTypeWatchBattLevel
|
ReqTypeWatchBattLevel
|
||||||
|
ReqTypeMotion
|
||||||
|
ReqTypeWatchMotion
|
||||||
|
ReqTypeStepCount
|
||||||
|
ReqTypeWatchStepCount
|
||||||
|
ReqTypeCancel
|
||||||
|
ReqTypeFS
|
||||||
|
ReqTypeWeatherUpdate
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -17,14 +29,31 @@ const (
|
|||||||
UpgradeTypeFiles
|
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 ReqDataFwUpgrade struct {
|
||||||
Type int
|
Type int
|
||||||
Files []string
|
Files []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
|
Type int `json:"type"`
|
||||||
Value interface{} `json:"value,omitempty"`
|
Value interface{} `json:"value,omitempty"`
|
||||||
Message string `json:"msg,omitempty"`
|
Message string `json:"msg,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
Error bool `json:"error"`
|
Error bool `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,4 +70,81 @@ type ReqDataNotify struct {
|
|||||||
type DFUProgress struct {
|
type DFUProgress struct {
|
||||||
Received int64 `mapstructure:"recvd"`
|
Received int64 `mapstructure:"recvd"`
|
||||||
Total int64 `mapstructure:"total"`
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
11
itd.toml
11
itd.toml
@@ -1,13 +1,13 @@
|
|||||||
# This is temporary, it is to show a notice
|
|
||||||
# to people still using the old config
|
|
||||||
cfg.version = 2
|
|
||||||
|
|
||||||
[socket]
|
[socket]
|
||||||
path = "/tmp/itd/socket"
|
path = "/tmp/itd/socket"
|
||||||
|
|
||||||
[conn]
|
[conn]
|
||||||
reconnect = true
|
reconnect = true
|
||||||
|
|
||||||
|
[conn.whitelist]
|
||||||
|
enabled = false
|
||||||
|
devices = []
|
||||||
|
|
||||||
[on.connect]
|
[on.connect]
|
||||||
notify = true
|
notify = true
|
||||||
|
|
||||||
@@ -26,3 +26,6 @@ cfg.version = 2
|
|||||||
[music]
|
[music]
|
||||||
vol.interval = 5
|
vol.interval = 5
|
||||||
|
|
||||||
|
[weather]
|
||||||
|
enabled = true
|
||||||
|
location = "Los Angeles, CA"
|
||||||
100
main.go
100
main.go
@@ -19,50 +19,86 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gen2brain/dlgs"
|
||||||
|
"github.com/knadh/koanf"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var firmwareUpdating = false
|
var k = koanf.New(".")
|
||||||
|
|
||||||
|
//go:embed version.txt
|
||||||
|
var version string
|
||||||
|
|
||||||
|
var (
|
||||||
|
firmwareUpdating = false
|
||||||
|
// The FS must be updated when the watch is reconnected
|
||||||
|
updateFS = false
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if viper.GetInt("cfg.version") != 2 {
|
showVer := flag.Bool("version", false, "Show version number and exit")
|
||||||
log.Fatal().Msg("Please update your config to the newest format, only v2 configs supported.")
|
flag.Parse()
|
||||||
|
// If version requested, print and exit
|
||||||
|
if *showVer {
|
||||||
|
fmt.Println(version)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize infinitime library
|
||||||
|
infinitime.Init()
|
||||||
// Cleanly exit after function
|
// Cleanly exit after function
|
||||||
defer infinitime.Exit()
|
defer infinitime.Exit()
|
||||||
|
|
||||||
|
// Create infinitime options struct
|
||||||
|
opts := &infinitime.Options{
|
||||||
|
AttemptReconnect: k.Bool("conn.reconnect"),
|
||||||
|
WhitelistEnabled: k.Bool("conn.whitelist.enabled"),
|
||||||
|
Whitelist: k.Strings("conn.whitelist.devices"),
|
||||||
|
OnReqPasskey: onReqPasskey,
|
||||||
|
Logger: log.Logger,
|
||||||
|
LogLevel: zerolog.WarnLevel,
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to InfiniTime with default options
|
// Connect to InfiniTime with default options
|
||||||
dev, err := infinitime.Connect(&infinitime.Options{
|
dev, err := infinitime.Connect(opts)
|
||||||
AttemptReconnect: viper.GetBool("conn.reconnect"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Error connecting to InfiniTime")
|
log.Fatal().Err(err).Msg("Error connecting to InfiniTime")
|
||||||
}
|
}
|
||||||
|
|
||||||
// When InfiniTime reconnects
|
// When InfiniTime reconnects
|
||||||
dev.OnReconnect(func() {
|
opts.OnReconnect = func() {
|
||||||
if viper.GetBool("on.reconnect.setTime") {
|
if k.Bool("on.reconnect.setTime") {
|
||||||
// Set time to current time
|
// Set time to current time
|
||||||
err = dev.SetTime(time.Now())
|
err = dev.SetTime(time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Error setting current time on connected InfiniTime")
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If config specifies to notify on reconnect
|
// If config specifies to notify on reconnect
|
||||||
if viper.GetBool("on.reconnect.notify") {
|
if k.Bool("on.reconnect.notify") {
|
||||||
// Send notification to InfiniTime
|
// Send notification to InfiniTime
|
||||||
err = dev.Notify("itd", "Successfully reconnected")
|
err = dev.Notify("itd", "Successfully reconnected")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Error sending notification to InfiniTime")
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// FS must be updated on reconnect
|
||||||
|
updateFS = true
|
||||||
|
// Resend weather on reconnect
|
||||||
|
sendWeatherCh <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
// Get firmware version
|
// Get firmware version
|
||||||
ver, err := dev.Version()
|
ver, err := dev.Version()
|
||||||
@@ -74,7 +110,7 @@ func main() {
|
|||||||
log.Info().Str("version", ver).Msg("Connected to InfiniTime")
|
log.Info().Str("version", ver).Msg("Connected to InfiniTime")
|
||||||
|
|
||||||
// If config specifies to notify on connect
|
// If config specifies to notify on connect
|
||||||
if viper.GetBool("on.connect.notify") {
|
if k.Bool("on.connect.notify") {
|
||||||
// Send notification to InfiniTime
|
// Send notification to InfiniTime
|
||||||
err = dev.Notify("itd", "Successfully connected")
|
err = dev.Notify("itd", "Successfully connected")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -94,12 +130,24 @@ func main() {
|
|||||||
log.Error().Err(err).Msg("Error initializing music control")
|
log.Error().Err(err).Msg("Error initializing music control")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start control socket
|
||||||
|
err = initCallNotifs(dev)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error initializing call notifications")
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize notification relay
|
// Initialize notification relay
|
||||||
err = initNotifRelay(dev)
|
err = initNotifRelay(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
|
||||||
|
err = initWeather(dev)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error initializing weather")
|
||||||
|
}
|
||||||
|
|
||||||
// Start control socket
|
// Start control socket
|
||||||
err = startSocket(dev)
|
err = startSocket(dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,3 +157,25 @@ func main() {
|
|||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onReqPasskey() (uint32, error) {
|
||||||
|
var out uint32
|
||||||
|
if isatty.IsTerminal(os.Stdin.Fd()) {
|
||||||
|
fmt.Print("Passkey: ")
|
||||||
|
_, err := fmt.Scanln(&out)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
passkey, ok, err := dlgs.Entry("Pairing", "Enter the passkey displayed on your watch.", "")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
passkeyInt, err := strconv.Atoi(passkey)
|
||||||
|
return uint32(passkeyInt), err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|||||||
55
music.go
55
music.go
@@ -20,51 +20,32 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
"go.arsenm.dev/infinitime/pkg/player"
|
"go.arsenm.dev/infinitime/pkg/player"
|
||||||
|
"go.arsenm.dev/itd/translit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initMusicCtrl(dev *infinitime.Device) error {
|
func initMusicCtrl(dev *infinitime.Device) error {
|
||||||
// On player status change, set status
|
player.Init()
|
||||||
err := player.Status(func(newStatus bool) {
|
|
||||||
if !firmwareUpdating {
|
|
||||||
dev.Music.SetStatus(newStatus)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// On player title change, set track
|
maps := k.Strings("notifs.translit.use")
|
||||||
err = player.Metadata("title", func(newTitle string) {
|
translit.Transliterators["custom"] = translit.Map(k.Strings("notifs.translit.custom"))
|
||||||
if !firmwareUpdating {
|
|
||||||
dev.Music.SetTrack(newTitle)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// On player album change, set album
|
player.OnChange(func(ct player.ChangeType, val string) {
|
||||||
err = player.Metadata("album", func(newAlbum string) {
|
newVal := translit.Transliterate(val, maps...)
|
||||||
if !firmwareUpdating {
|
if !firmwareUpdating {
|
||||||
dev.Music.SetAlbum(newAlbum)
|
switch ct {
|
||||||
|
case player.ChangeTypeStatus:
|
||||||
|
dev.Music.SetStatus(val == "Playing")
|
||||||
|
case player.ChangeTypeTitle:
|
||||||
|
dev.Music.SetTrack(newVal)
|
||||||
|
case player.ChangeTypeAlbum:
|
||||||
|
dev.Music.SetAlbum(newVal)
|
||||||
|
case player.ChangeTypeArtist:
|
||||||
|
dev.Music.SetArtist(newVal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// On player artist change, set artist
|
|
||||||
err = player.Metadata("artist", func(newArtist string) {
|
|
||||||
if !firmwareUpdating {
|
|
||||||
dev.Music.SetArtist(newArtist)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch for music events
|
// Watch for music events
|
||||||
musicEvtCh, err := dev.Music.WatchEvents()
|
musicEvtCh, err := dev.Music.WatchEvents()
|
||||||
@@ -85,9 +66,9 @@ func initMusicCtrl(dev *infinitime.Device) error {
|
|||||||
case infinitime.MusicEventPrev:
|
case infinitime.MusicEventPrev:
|
||||||
player.Prev()
|
player.Prev()
|
||||||
case infinitime.MusicEventVolUp:
|
case infinitime.MusicEventVolUp:
|
||||||
player.VolUp(viper.GetUint("music.vol.interval"))
|
player.VolUp(uint(k.Int("music.vol.interval")))
|
||||||
case infinitime.MusicEventVolDown:
|
case infinitime.MusicEventVolDown:
|
||||||
player.VolDown(viper.GetUint("music.vol.interval"))
|
player.VolDown(uint(k.Int("music.vol.interval")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
13
notifs.go
13
notifs.go
@@ -23,14 +23,13 @@ import (
|
|||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
"go.arsenm.dev/itd/translit"
|
"go.arsenm.dev/itd/translit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initNotifRelay(dev *infinitime.Device) error {
|
func initNotifRelay(dev *infinitime.Device) error {
|
||||||
// Connect to dbus session bus
|
// Connect to dbus session bus
|
||||||
bus, err := dbus.SessionBus()
|
bus, err := newSessionBusConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -72,8 +71,8 @@ func initNotifRelay(dev *infinitime.Device) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
maps := viper.GetStringSlice("notifs.translit.use")
|
maps := k.Strings("notifs.translit.use")
|
||||||
translit.Transliterators["custom"] = translit.Map(viper.GetStringSlice("notifs.translit.custom"))
|
translit.Transliterators["custom"] = translit.Map(k.Strings("notifs.translit.custom"))
|
||||||
sender = translit.Transliterate(sender, maps...)
|
sender = translit.Transliterate(sender, maps...)
|
||||||
summary = translit.Transliterate(summary, maps...)
|
summary = translit.Transliterate(summary, maps...)
|
||||||
body = translit.Transliterate(body, maps...)
|
body = translit.Transliterate(body, maps...)
|
||||||
@@ -97,9 +96,9 @@ func initNotifRelay(dev *infinitime.Device) error {
|
|||||||
|
|
||||||
// ignored checks whether any fields were ignored in the config
|
// ignored checks whether any fields were ignored in the config
|
||||||
func ignored(sender, summary, body string) bool {
|
func ignored(sender, summary, body string) bool {
|
||||||
ignoreSender := viper.GetStringSlice("notifs.ignore.sender")
|
ignoreSender := k.Strings("notifs.ignore.sender")
|
||||||
ignoreSummary := viper.GetStringSlice("notifs.ignore.summary")
|
ignoreSummary := k.Strings("notifs.ignore.summary")
|
||||||
ignoreBody := viper.GetStringSlice("notifs.ignore.body")
|
ignoreBody := k.Strings("notifs.ignore.body")
|
||||||
return strSlcContains(ignoreSender, sender) ||
|
return strSlcContains(ignoreSender, sender) ||
|
||||||
strSlcContains(ignoreSummary, summary) ||
|
strSlcContains(ignoreSummary, summary) ||
|
||||||
strSlcContains(ignoreBody, body)
|
strSlcContains(ignoreBody, body)
|
||||||
|
|||||||
493
socket.go
493
socket.go
@@ -22,38 +22,69 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
|
"go.arsenm.dev/infinitime/blefs"
|
||||||
"go.arsenm.dev/itd/internal/types"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
"go.arsenm.dev/itd/translit"
|
"go.arsenm.dev/itd/translit"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DoneMap map[string]chan struct{}
|
||||||
|
|
||||||
|
func (dm DoneMap) Exists(key string) bool {
|
||||||
|
_, ok := dm[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm DoneMap) Done(key string) {
|
||||||
|
ch := dm[key]
|
||||||
|
ch <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm DoneMap) Create(key string) {
|
||||||
|
dm[key] = make(chan struct{}, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm DoneMap) Remove(key string) {
|
||||||
|
close(dm[key])
|
||||||
|
delete(dm, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
var done = DoneMap{}
|
||||||
|
|
||||||
func startSocket(dev *infinitime.Device) error {
|
func startSocket(dev *infinitime.Device) error {
|
||||||
// Make socket directory if non-existent
|
// Make socket directory if non-existant
|
||||||
err := os.MkdirAll(filepath.Dir(viper.GetString("socket.path")), 0755)
|
err := os.MkdirAll(filepath.Dir(k.String("socket.path")), 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old socket if it exists
|
// Remove old socket if it exists
|
||||||
err = os.RemoveAll(viper.GetString("socket.path"))
|
err = os.RemoveAll(k.String("socket.path"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen on socket path
|
// Listen on socket path
|
||||||
ln, err := net.Listen("unix", viper.GetString("socket.path"))
|
ln, err := net.Listen("unix", k.String("socket.path"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs, err := dev.FS()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Error getting BLE filesystem")
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
// Accept socket connection
|
// Accept socket connection
|
||||||
@@ -63,22 +94,31 @@ func startSocket(dev *infinitime.Device) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Concurrently handle connection
|
// Concurrently handle connection
|
||||||
go handleConnection(conn, dev)
|
go handleConnection(conn, dev, fs)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Log socket start
|
// Log socket start
|
||||||
log.Info().Str("path", viper.GetString("socket.path")).Msg("Started control socket")
|
log.Info().Str("path", k.String("socket.path")).Msg("Started control socket")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
func handleConnection(conn net.Conn, dev *infinitime.Device, fs *blefs.FS) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
// If firmware is updating, return error
|
|
||||||
if firmwareUpdating {
|
// If an FS update is required (reconnect ocurred)
|
||||||
connErr(conn, nil, "Firmware update in progress")
|
if updateFS {
|
||||||
return
|
// Get new FS
|
||||||
|
newFS, err := dev.FS()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Error updating BLE filesystem")
|
||||||
|
} else {
|
||||||
|
// Set FS pointer to new FS
|
||||||
|
*fs = *newFS
|
||||||
|
// Reset updateFS
|
||||||
|
updateFS = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new scanner on connection
|
// Create new scanner on connection
|
||||||
@@ -88,110 +128,227 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
// Decode scanned message into types.Request
|
// Decode scanned message into types.Request
|
||||||
err := json.Unmarshal(scanner.Bytes(), &req)
|
err := json.Unmarshal(scanner.Bytes(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error decoding JSON input")
|
connErr(conn, req.Type, err, "Error decoding JSON input")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If firmware is updating, return error
|
||||||
|
if firmwareUpdating {
|
||||||
|
connErr(conn, req.Type, nil, "Firmware update in progress")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch req.Type {
|
switch req.Type {
|
||||||
case types.ReqTypeHeartRate:
|
case types.ReqTypeHeartRate:
|
||||||
// Get heart rate from watch
|
// Get heart rate from watch
|
||||||
heartRate, err := dev.HeartRate()
|
heartRate, err := dev.HeartRate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error getting heart rate")
|
connErr(conn, req.Type, err, "Error getting heart rate")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Encode heart rate to connection
|
// Encode heart rate to connection
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
Value: heartRate,
|
Value: heartRate,
|
||||||
})
|
})
|
||||||
case types.ReqTypeWatchHeartRate:
|
case types.ReqTypeWatchHeartRate:
|
||||||
heartRateCh, err := dev.WatchHeartRate()
|
heartRateCh, cancel, err := dev.WatchHeartRate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error getting heart rate channel")
|
connErr(conn, req.Type, err, "Error getting heart rate channel")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
reqID := uuid.New().String()
|
||||||
go func() {
|
go func() {
|
||||||
|
done.Create(reqID)
|
||||||
|
// For every heart rate value
|
||||||
for heartRate := range heartRateCh {
|
for heartRate := range heartRateCh {
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
select {
|
||||||
Value: heartRate,
|
case <-done[reqID]:
|
||||||
})
|
// Stop notifications if done signal received
|
||||||
}
|
cancel()
|
||||||
}()
|
done.Remove(reqID)
|
||||||
case types.ReqTypeWatchBattLevel:
|
return
|
||||||
battLevelCh, err := dev.WatchBatteryLevel()
|
default:
|
||||||
if err != nil {
|
// Encode response to connection if no done signal received
|
||||||
connErr(conn, err, "Error getting heart rate channel")
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
break
|
Type: req.Type,
|
||||||
}
|
ID: reqID,
|
||||||
go func() {
|
Value: heartRate,
|
||||||
for battLevel := range battLevelCh {
|
})
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
}
|
||||||
Value: battLevel,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
case types.ReqTypeBattLevel:
|
case types.ReqTypeBattLevel:
|
||||||
// Get battery level from watch
|
// Get battery level from watch
|
||||||
battLevel, err := dev.BatteryLevel()
|
battLevel, err := dev.BatteryLevel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error getting battery level")
|
connErr(conn, req.Type, err, "Error getting battery level")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Encode battery level to connection
|
// Encode battery level to connection
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
Value: battLevel,
|
Value: battLevel,
|
||||||
})
|
})
|
||||||
|
case types.ReqTypeWatchBattLevel:
|
||||||
|
battLevelCh, cancel, err := dev.WatchBatteryLevel()
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error getting battery level channel")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
reqID := uuid.New().String()
|
||||||
|
go func() {
|
||||||
|
done.Create(reqID)
|
||||||
|
// For every battery level value
|
||||||
|
for battLevel := range battLevelCh {
|
||||||
|
select {
|
||||||
|
case <-done[reqID]:
|
||||||
|
// Stop notifications if done signal received
|
||||||
|
cancel()
|
||||||
|
done.Remove(reqID)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// Encode response to connection if no done signal received
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
ID: reqID,
|
||||||
|
Value: battLevel,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case types.ReqTypeMotion:
|
||||||
|
// Get battery level from watch
|
||||||
|
motionVals, err := dev.Motion()
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error getting motion values")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Encode battery level to connection
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
Value: motionVals,
|
||||||
|
})
|
||||||
|
case types.ReqTypeWatchMotion:
|
||||||
|
motionValCh, cancel, err := dev.WatchMotion()
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error getting heart rate channel")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
reqID := uuid.New().String()
|
||||||
|
go func() {
|
||||||
|
done.Create(reqID)
|
||||||
|
// For every motion event
|
||||||
|
for motionVals := range motionValCh {
|
||||||
|
select {
|
||||||
|
case <-done[reqID]:
|
||||||
|
// Stop notifications if done signal received
|
||||||
|
cancel()
|
||||||
|
done.Remove(reqID)
|
||||||
|
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// Encode response to connection if no done signal received
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
ID: reqID,
|
||||||
|
Value: motionVals,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case types.ReqTypeStepCount:
|
||||||
|
// Get battery level from watch
|
||||||
|
stepCount, err := dev.StepCount()
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error getting step count")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Encode battery level to connection
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
Value: stepCount,
|
||||||
|
})
|
||||||
|
case types.ReqTypeWatchStepCount:
|
||||||
|
stepCountCh, cancel, err := dev.WatchStepCount()
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error getting heart rate channel")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
reqID := uuid.New().String()
|
||||||
|
go func() {
|
||||||
|
done.Create(reqID)
|
||||||
|
// For every step count value
|
||||||
|
for stepCount := range stepCountCh {
|
||||||
|
select {
|
||||||
|
case <-done[reqID]:
|
||||||
|
// Stop notifications if done signal received
|
||||||
|
cancel()
|
||||||
|
done.Remove(reqID)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// Encode response to connection if no done signal received
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
ID: reqID,
|
||||||
|
Value: stepCount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
case types.ReqTypeFwVersion:
|
case types.ReqTypeFwVersion:
|
||||||
// Get firmware version from watch
|
// Get firmware version from watch
|
||||||
version, err := dev.Version()
|
version, err := dev.Version()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error getting battery level")
|
connErr(conn, req.Type, err, "Error getting firmware version")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Encode version to connection
|
// Encode version to connection
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
Value: version,
|
Value: version,
|
||||||
})
|
})
|
||||||
case types.ReqTypeBtAddress:
|
case types.ReqTypeBtAddress:
|
||||||
// Encode bluetooth address to connection
|
// Encode bluetooth address to connection
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
Value: dev.Address(),
|
Value: dev.Address(),
|
||||||
})
|
})
|
||||||
case types.ReqTypeNotify:
|
case types.ReqTypeNotify:
|
||||||
// If no data, return error
|
// If no data, return error
|
||||||
if req.Data == nil {
|
if req.Data == nil {
|
||||||
connErr(conn, nil, "Data required for notify request")
|
connErr(conn, req.Type, nil, "Data required for notify request")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
var reqData types.ReqDataNotify
|
var reqData types.ReqDataNotify
|
||||||
// Decode data map to notify request data
|
// Decode data map to notify request data
|
||||||
err = mapstructure.Decode(req.Data, &reqData)
|
err = mapstructure.Decode(req.Data, &reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error decoding request data")
|
connErr(conn, req.Type, err, "Error decoding request data")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
maps := viper.GetStringSlice("notifs.translit.use")
|
maps := k.Strings("notifs.translit.use")
|
||||||
translit.Transliterators["custom"] = translit.Map(viper.GetStringSlice("notifs.translit.custom"))
|
translit.Transliterators["custom"] = translit.Map(k.Strings("notifs.translit.custom"))
|
||||||
title := translit.Transliterate(reqData.Title, maps...)
|
title := translit.Transliterate(reqData.Title, maps...)
|
||||||
body := translit.Transliterate(reqData.Body, maps...)
|
body := translit.Transliterate(reqData.Body, maps...)
|
||||||
// Send notification to watch
|
// Send notification to watch
|
||||||
err = dev.Notify(title, body)
|
err = dev.Notify(title, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error sending notification")
|
connErr(conn, req.Type, err, "Error sending notification")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Encode empty types.Response to connection
|
// Encode empty types.Response to connection
|
||||||
json.NewEncoder(conn).Encode(types.Response{})
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
case types.ReqTypeSetTime:
|
case types.ReqTypeSetTime:
|
||||||
// If no data, return error
|
// If no data, return error
|
||||||
if req.Data == nil {
|
if req.Data == nil {
|
||||||
connErr(conn, nil, "Data required for settime request")
|
connErr(conn, req.Type, nil, "Data required for settime request")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Get string from data or return error
|
// Get string from data or return error
|
||||||
reqTimeStr, ok := req.Data.(string)
|
reqTimeStr, ok := req.Data.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
connErr(conn, nil, "Data for settime request must be RFC3339 formatted time string")
|
connErr(conn, req.Type, nil, "Data for settime request must be RFC3339 formatted time string")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,69 +359,71 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
// Parse time as RFC3339/ISO8601
|
// Parse time as RFC3339/ISO8601
|
||||||
reqTime, err = time.Parse(time.RFC3339, reqTimeStr)
|
reqTime, err = time.Parse(time.RFC3339, reqTimeStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Invalid time format. Time string must be formatted as ISO8601 or the word `now`")
|
connErr(conn, req.Type, err, "Invalid time format. Time string must be formatted as ISO8601 or the word `now`")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set time on watch
|
// Set time on watch
|
||||||
err = dev.SetTime(reqTime)
|
err = dev.SetTime(reqTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error setting device time")
|
connErr(conn, req.Type, err, "Error setting device time")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Encode empty types.Response to connection
|
// Encode empty types.Response to connection
|
||||||
json.NewEncoder(conn).Encode(types.Response{})
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
case types.ReqTypeFwUpgrade:
|
case types.ReqTypeFwUpgrade:
|
||||||
// If no data, return error
|
// If no data, return error
|
||||||
if req.Data == nil {
|
if req.Data == nil {
|
||||||
connErr(conn, nil, "Data required for firmware upgrade request")
|
connErr(conn, req.Type, nil, "Data required for firmware upgrade request")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
var reqData types.ReqDataFwUpgrade
|
var reqData types.ReqDataFwUpgrade
|
||||||
// Decode data map to firmware upgrade request data
|
// Decode data map to firmware upgrade request data
|
||||||
err = mapstructure.Decode(req.Data, &reqData)
|
err = mapstructure.Decode(req.Data, &reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error decoding request data")
|
connErr(conn, req.Type, err, "Error decoding request data")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// Reset DFU to prepare for next update
|
||||||
|
dev.DFU.Reset()
|
||||||
switch reqData.Type {
|
switch reqData.Type {
|
||||||
case types.UpgradeTypeArchive:
|
case types.UpgradeTypeArchive:
|
||||||
// If less than one file, return error
|
// If less than one file, return error
|
||||||
if len(reqData.Files) < 1 {
|
if len(reqData.Files) < 1 {
|
||||||
connErr(conn, nil, "Archive upgrade requires one file with .zip extension")
|
connErr(conn, req.Type, nil, "Archive upgrade requires one file with .zip extension")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// If file is not zip archive, return error
|
// If file is not zip archive, return error
|
||||||
if filepath.Ext(reqData.Files[0]) != ".zip" {
|
if filepath.Ext(reqData.Files[0]) != ".zip" {
|
||||||
connErr(conn, nil, "Archive upgrade file must be a zip archive")
|
connErr(conn, req.Type, nil, "Archive upgrade file must be a zip archive")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Load DFU archive
|
// Load DFU archive
|
||||||
err := dev.DFU.LoadArchive(reqData.Files[0])
|
err := dev.DFU.LoadArchive(reqData.Files[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error loading archive file")
|
connErr(conn, req.Type, err, "Error loading archive file")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case types.UpgradeTypeFiles:
|
case types.UpgradeTypeFiles:
|
||||||
// If less than two files, return error
|
// If less than two files, return error
|
||||||
if len(reqData.Files) < 2 {
|
if len(reqData.Files) < 2 {
|
||||||
connErr(conn, nil, "Files upgrade requires two files. First with .dat and second with .bin extension.")
|
connErr(conn, req.Type, nil, "Files upgrade requires two files. First with .dat and second with .bin extension.")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// If first file is not init packet, return error
|
// If first file is not init packet, return error
|
||||||
if filepath.Ext(reqData.Files[0]) != ".dat" {
|
if filepath.Ext(reqData.Files[0]) != ".dat" {
|
||||||
connErr(conn, nil, "First file must be a .dat file")
|
connErr(conn, req.Type, nil, "First file must be a .dat file")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// If second file is not firmware image, return error
|
// If second file is not firmware image, return error
|
||||||
if filepath.Ext(reqData.Files[1]) != ".bin" {
|
if filepath.Ext(reqData.Files[1]) != ".bin" {
|
||||||
connErr(conn, nil, "Second file must be a .bin file")
|
connErr(conn, req.Type, nil, "Second file must be a .bin file")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Load individual DFU files
|
// Load individual DFU files
|
||||||
err := dev.DFU.LoadFiles(reqData.Files[0], reqData.Files[1])
|
err := dev.DFU.LoadFiles(reqData.Files[0], reqData.Files[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error loading firmware files")
|
connErr(conn, req.Type, err, "Error loading firmware files")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,9 +435,11 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
for event := range progress {
|
for event := range progress {
|
||||||
// Encode event on connection
|
// Encode event on connection
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
Value: event,
|
Value: event,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
firmwareUpdating = false
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Set firmwareUpdating
|
// Set firmwareUpdating
|
||||||
@@ -286,16 +447,224 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
// Start DFU
|
// Start DFU
|
||||||
err = dev.DFU.Start()
|
err = dev.DFU.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connErr(conn, err, "Error performing upgrade")
|
connErr(conn, req.Type, err, "Error performing upgrade")
|
||||||
firmwareUpdating = false
|
firmwareUpdating = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
firmwareUpdating = false
|
firmwareUpdating = false
|
||||||
|
case types.ReqTypeFS:
|
||||||
|
if fs == nil {
|
||||||
|
connErr(conn, req.Type, nil, "BLE filesystem is not available")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no data, return error
|
||||||
|
if req.Data == nil {
|
||||||
|
connErr(conn, req.Type, nil, "Data required for filesystem operations")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqData types.ReqDataFS
|
||||||
|
// Decode data map to firmware upgrade request data
|
||||||
|
err = mapstructure.Decode(req.Data, &reqData)
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error decoding request data")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean input filepaths
|
||||||
|
reqData.Files = cleanPaths(reqData.Files)
|
||||||
|
|
||||||
|
switch reqData.Type {
|
||||||
|
case types.FSTypeDelete:
|
||||||
|
if len(reqData.Files) == 0 {
|
||||||
|
connErr(conn, req.Type, nil, "Remove FS command requires at least one file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, file := range reqData.Files {
|
||||||
|
err := fs.Remove(file)
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error removing file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
|
case types.FSTypeMove:
|
||||||
|
if len(reqData.Files) != 2 {
|
||||||
|
connErr(conn, req.Type, nil, "Move FS command requires an old path and new path in the files list")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err := fs.Rename(reqData.Files[0], reqData.Files[1])
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error moving file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
|
case types.FSTypeMkdir:
|
||||||
|
if len(reqData.Files) == 0 {
|
||||||
|
connErr(conn, req.Type, nil, "Mkdir FS command requires at least one file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, file := range reqData.Files {
|
||||||
|
err := fs.Mkdir(file)
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error creating directory")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
|
case types.FSTypeList:
|
||||||
|
if len(reqData.Files) != 1 {
|
||||||
|
connErr(conn, req.Type, nil, "List FS command requires a path to list in the files list")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
entries, err := fs.ReadDir(reqData.Files[0])
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error reading directory")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var out []types.FileInfo
|
||||||
|
for _, entry := range entries {
|
||||||
|
info, err := entry.Info()
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error getting file info")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out = append(out, types.FileInfo{
|
||||||
|
Name: info.Name(),
|
||||||
|
Size: info.Size(),
|
||||||
|
IsDir: info.IsDir(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
Value: out,
|
||||||
|
})
|
||||||
|
case types.FSTypeWrite:
|
||||||
|
if len(reqData.Files) != 2 {
|
||||||
|
connErr(conn, req.Type, nil, "Write FS command requires a path to the file to write")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
localFile, err := os.Open(reqData.Files[1])
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error opening local file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer localFile.Close()
|
||||||
|
|
||||||
|
localInfo, err := localFile.Stat()
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error getting local file information")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteFile, err := fs.Create(reqData.Files[0], uint32(localInfo.Size()))
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error creating remote file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer remoteFile.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// For every progress event
|
||||||
|
for sent := range remoteFile.Progress() {
|
||||||
|
// Encode event on connection
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
Value: types.FSTransferProgress{
|
||||||
|
Type: types.FSTypeWrite,
|
||||||
|
Total: remoteFile.Size(),
|
||||||
|
Sent: sent,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
|
|
||||||
|
io.Copy(remoteFile, localFile)
|
||||||
|
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
Value: types.FSTransferProgress{
|
||||||
|
Type: types.FSTypeWrite,
|
||||||
|
Total: remoteFile.Size(),
|
||||||
|
Sent: remoteFile.Size(),
|
||||||
|
Done: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case types.FSTypeRead:
|
||||||
|
if len(reqData.Files) != 2 {
|
||||||
|
connErr(conn, req.Type, nil, "Read FS command requires a path to the file to read")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
localFile, err := os.Create(reqData.Files[0])
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error creating local file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer localFile.Close()
|
||||||
|
|
||||||
|
remoteFile, err := fs.Open(reqData.Files[1])
|
||||||
|
if err != nil {
|
||||||
|
connErr(conn, req.Type, err, "Error opening remote file")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer remoteFile.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// For every progress event
|
||||||
|
for rcvd := range remoteFile.Progress() {
|
||||||
|
// Encode event on connection
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
Value: types.FSTransferProgress{
|
||||||
|
Type: types.FSTypeRead,
|
||||||
|
Total: remoteFile.Size(),
|
||||||
|
Sent: rcvd,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
|
|
||||||
|
io.Copy(localFile, remoteFile)
|
||||||
|
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
|
Type: req.Type,
|
||||||
|
Value: types.FSTransferProgress{
|
||||||
|
Type: types.FSTypeRead,
|
||||||
|
Total: remoteFile.Size(),
|
||||||
|
Sent: remoteFile.Size(),
|
||||||
|
Done: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case types.ReqTypeWeatherUpdate:
|
||||||
|
// Send weather update signal
|
||||||
|
sendWeatherCh <- struct{}{}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
|
case types.ReqTypeCancel:
|
||||||
|
if req.Data == nil {
|
||||||
|
connErr(conn, req.Type, nil, "No data provided. Cancel request requires request ID string as data.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
reqID, ok := req.Data.(string)
|
||||||
|
if !ok {
|
||||||
|
connErr(conn, req.Type, nil, "Invalid data. Cancel request required request ID string as data.")
|
||||||
|
}
|
||||||
|
// Stop notifications
|
||||||
|
done.Done(reqID)
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{Type: req.Type})
|
||||||
|
default:
|
||||||
|
connErr(conn, req.Type, nil, fmt.Sprintf("Unknown request type %d", req.Type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func connErr(conn net.Conn, err error, msg string) {
|
func connErr(conn net.Conn, resType int, err error, msg string) {
|
||||||
var res types.Response
|
var res types.Response
|
||||||
// If error exists, add to types.Response, otherwise don't
|
// If error exists, add to types.Response, otherwise don't
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -303,10 +672,20 @@ func connErr(conn net.Conn, err error, msg string) {
|
|||||||
res = types.Response{Message: fmt.Sprintf("%s: %s", msg, err)}
|
res = types.Response{Message: fmt.Sprintf("%s: %s", msg, err)}
|
||||||
} else {
|
} else {
|
||||||
log.Error().Msg(msg)
|
log.Error().Msg(msg)
|
||||||
res = types.Response{Message: msg}
|
res = types.Response{Message: msg, Type: resType}
|
||||||
}
|
}
|
||||||
res.Error = true
|
res.Error = true
|
||||||
|
|
||||||
// Encode error to connection
|
// Encode error to connection
|
||||||
json.NewEncoder(conn).Encode(res)
|
json.NewEncoder(conn).Encode(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanPaths runs strings.TrimSpace and filepath.Clean
|
||||||
|
// on all inputs, and returns the updated slice
|
||||||
|
func cleanPaths(paths []string) []string {
|
||||||
|
for index, path := range paths {
|
||||||
|
newPath := strings.TrimSpace(path)
|
||||||
|
paths[index] = filepath.Clean(newPath)
|
||||||
|
}
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ArmenianTranslit struct{}
|
type ArmenianTranslit struct {
|
||||||
|
initComplete bool
|
||||||
|
}
|
||||||
|
|
||||||
var armenianMap = []string{
|
var armenianMap = []string{
|
||||||
"աու", "au",
|
"աու", "au",
|
||||||
@@ -120,16 +122,26 @@ var armenianMap = []string{
|
|||||||
"ւ", "",
|
"ւ", "",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func (at *ArmenianTranslit) Init() {
|
||||||
lower := armenianMap
|
if !at.initComplete {
|
||||||
for i, val := range lower {
|
// Copy map as original will be changed
|
||||||
if i%2 == 1 {
|
lower := armenianMap
|
||||||
continue
|
// For every value in copied map
|
||||||
}
|
for i, val := range lower {
|
||||||
capital := strings.Title(val)
|
// If index is odd, skip
|
||||||
if capital != val {
|
if i%2 == 1 {
|
||||||
armenianMap = append(armenianMap, capital, strings.Title(armenianMap[i+1]))
|
continue
|
||||||
|
}
|
||||||
|
// Capitalize first letter
|
||||||
|
capital := strings.Title(val)
|
||||||
|
// If capital is not the same as lowercase
|
||||||
|
if capital != val {
|
||||||
|
// Add capital to map
|
||||||
|
armenianMap = append(armenianMap, capital, strings.Title(armenianMap[i+1]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Set init complete to true so it is not run again
|
||||||
|
at.initComplete = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
// conversion library.
|
// conversion library.
|
||||||
type ChineseTranslit struct{}
|
type ChineseTranslit struct{}
|
||||||
|
|
||||||
|
func (ChineseTranslit) Init() {}
|
||||||
|
|
||||||
func (ct *ChineseTranslit) Transliterate(s string) string {
|
func (ct *ChineseTranslit) Transliterate(s string) string {
|
||||||
// Create buffer for final output
|
// Create buffer for final output
|
||||||
outBuf := &bytes.Buffer{}
|
outBuf := &bytes.Buffer{}
|
||||||
@@ -37,6 +39,15 @@ func (ct *ChineseTranslit) Transliterate(s string) string {
|
|||||||
outBuf.WriteRune(char)
|
outBuf.WriteRune(char)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If buffer contains characters
|
||||||
|
if tmpBuf.Len() > 0 {
|
||||||
|
// Convert to pinyin (without tones)
|
||||||
|
out := pinyin.LazyConvert(tmpBuf.String(), nil)
|
||||||
|
// Write space-separated string to output
|
||||||
|
outBuf.WriteString(strings.Join(out, " "))
|
||||||
|
// Reset temporary buffer
|
||||||
|
tmpBuf.Reset()
|
||||||
|
}
|
||||||
// Return output string
|
// Return output string
|
||||||
return outBuf.String()
|
return outBuf.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ var compatJamoBlock = &unicode.RangeTable{
|
|||||||
// This was translated to Go from the code in https://codeberg.org/Freeyourgadget/Gadgetbridge
|
// This was translated to Go from the code in https://codeberg.org/Freeyourgadget/Gadgetbridge
|
||||||
type KoreanTranslit struct{}
|
type KoreanTranslit struct{}
|
||||||
|
|
||||||
|
func (KoreanTranslit) Init() {}
|
||||||
|
|
||||||
// User input consisting of isolated jamo is usually mapped to the KS X 1001 compatibility
|
// User input consisting of isolated jamo is usually mapped to the KS X 1001 compatibility
|
||||||
// block, but jamo resulting from decomposed syllables are mapped to the modern one. This
|
// block, but jamo resulting from decomposed syllables are mapped to the modern one. This
|
||||||
// function maps compat jamo to modern ones where possible and returns all other characters
|
// function maps compat jamo to modern ones where possible and returns all other characters
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ func Transliterate(s string, useMaps ...string) string {
|
|||||||
// Create variable to store modified string
|
// Create variable to store modified string
|
||||||
out := s
|
out := s
|
||||||
// If custom map exists
|
// If custom map exists
|
||||||
if customMap, ok := Transliterators["custom"]; ok {
|
if custom, ok := Transliterators["custom"]; ok {
|
||||||
// Perform transliteration with it
|
// Perform transliteration with it
|
||||||
out = customMap.Transliterate(out)
|
out = custom.Transliterate(out)
|
||||||
}
|
}
|
||||||
// For every map to use
|
// For every map to use
|
||||||
for _, useMap := range useMaps {
|
for _, useMap := range useMaps {
|
||||||
@@ -20,12 +20,13 @@ func Transliterate(s string, useMaps ...string) string {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Get requested map
|
// Get requested map
|
||||||
translitMap, ok := Transliterators[useMap]
|
transliterator, ok := Transliterators[useMap]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
transliterator.Init()
|
||||||
// Perform transliteration
|
// Perform transliteration
|
||||||
out = translitMap.Transliterate(out)
|
out = transliterator.Transliterate(out)
|
||||||
}
|
}
|
||||||
// Return result
|
// Return result
|
||||||
return out
|
return out
|
||||||
@@ -36,10 +37,11 @@ func Transliterate(s string, useMaps ...string) string {
|
|||||||
// and returns the resulting string.
|
// and returns the resulting string.
|
||||||
type Transliterator interface {
|
type Transliterator interface {
|
||||||
Transliterate(string) string
|
Transliterate(string) string
|
||||||
|
Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map implements Transliterator using a slice where
|
// Map implements Transliterator using a slice where
|
||||||
// every odd element is a key and every even one is a value
|
// every even element is a key and every odd one is a value
|
||||||
// which replaces the key.
|
// which replaces the key.
|
||||||
type Map []string
|
type Map []string
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ func (mt Map) Transliterate(s string) string {
|
|||||||
return strings.NewReplacer(mt...).Replace(s)
|
return strings.NewReplacer(mt...).Replace(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (Map) Init() {}
|
||||||
|
|
||||||
// Transliterators stores transliterator implementations for each supported language.
|
// Transliterators stores transliterator implementations for each supported language.
|
||||||
// Some of these were sourced from https://codeberg.org/Freeyourgadget/Gadgetbridge
|
// Some of these were sourced from https://codeberg.org/Freeyourgadget/Gadgetbridge
|
||||||
var Transliterators = map[string]Transliterator{
|
var Transliterators = map[string]Transliterator{
|
||||||
@@ -151,7 +155,7 @@ var Transliterators = map[string]Transliterator{
|
|||||||
"Ζ", "Z",
|
"Ζ", "Z",
|
||||||
"Η", "I",
|
"Η", "I",
|
||||||
"Ή", "I",
|
"Ή", "I",
|
||||||
"Θ", "TH",
|
"Θ", "Th",
|
||||||
"Ι", "I",
|
"Ι", "I",
|
||||||
"Ί", "I",
|
"Ί", "I",
|
||||||
"Ϊ", "I",
|
"Ϊ", "I",
|
||||||
@@ -159,7 +163,7 @@ var Transliterators = map[string]Transliterator{
|
|||||||
"Λ", "L",
|
"Λ", "L",
|
||||||
"Μ", "M",
|
"Μ", "M",
|
||||||
"Ν", "N",
|
"Ν", "N",
|
||||||
"Ξ", "KS",
|
"Ξ", "Ks",
|
||||||
"Ο", "O",
|
"Ο", "O",
|
||||||
"Ό", "O",
|
"Ό", "O",
|
||||||
"Π", "P",
|
"Π", "P",
|
||||||
@@ -170,8 +174,8 @@ var Transliterators = map[string]Transliterator{
|
|||||||
"Ύ", "Y",
|
"Ύ", "Y",
|
||||||
"Ϋ", "Y",
|
"Ϋ", "Y",
|
||||||
"Φ", "F",
|
"Φ", "F",
|
||||||
"Χ", "CH",
|
"Χ", "Ch",
|
||||||
"Ψ", "PS",
|
"Ψ", "Ps",
|
||||||
"Ω", "O",
|
"Ω", "O",
|
||||||
"Ώ", "O",
|
"Ώ", "O",
|
||||||
},
|
},
|
||||||
@@ -184,8 +188,8 @@ var Transliterators = map[string]Transliterator{
|
|||||||
"є", "je",
|
"є", "je",
|
||||||
"і", "i",
|
"і", "i",
|
||||||
"ї", "ji",
|
"ї", "ji",
|
||||||
"Ґ", "GH",
|
"Ґ", "Gh",
|
||||||
"Є", "JE",
|
"Є", "Je",
|
||||||
"І", "I",
|
"І", "I",
|
||||||
"Ї", "JI",
|
"Ї", "JI",
|
||||||
},
|
},
|
||||||
@@ -323,11 +327,40 @@ var Transliterators = map[string]Transliterator{
|
|||||||
"ÿ", "y",
|
"ÿ", "y",
|
||||||
"ç", "c",
|
"ç", "c",
|
||||||
},
|
},
|
||||||
|
"Romanian": Map{
|
||||||
|
"ă", "a",
|
||||||
|
"Ă", "A",
|
||||||
|
"â", "a",
|
||||||
|
"Â", "A",
|
||||||
|
"î", "i",
|
||||||
|
"Î", "I",
|
||||||
|
"ș", "s",
|
||||||
|
"Ș", "S",
|
||||||
|
"ț", "t",
|
||||||
|
"Ț", "T",
|
||||||
|
"ş", "s",
|
||||||
|
"Ş", "S",
|
||||||
|
"ţ", "t",
|
||||||
|
"Ţ", "T",
|
||||||
|
"„", "\"",
|
||||||
|
"”", "\"",
|
||||||
|
},
|
||||||
"Emoji": Map{
|
"Emoji": Map{
|
||||||
"😂", ":')",
|
"😂", "XD",
|
||||||
|
"🤣", "XD",
|
||||||
"😊", ":)",
|
"😊", ":)",
|
||||||
"😃", ":)",
|
"☺️", ":)",
|
||||||
|
"😌", ":)",
|
||||||
|
"😃", ":D",
|
||||||
|
"😁", ":D",
|
||||||
|
"😋", ":P",
|
||||||
|
"😛", ":P",
|
||||||
|
"😜", ";P",
|
||||||
|
"🙃", "(:",
|
||||||
|
"😎", "8)",
|
||||||
|
"😶", ":#",
|
||||||
"😩", "-_-",
|
"😩", "-_-",
|
||||||
|
"😕", ":(",
|
||||||
"😏", ":‑J",
|
"😏", ":‑J",
|
||||||
"💜", "<3",
|
"💜", "<3",
|
||||||
"💖", "<3",
|
"💖", "<3",
|
||||||
@@ -339,12 +372,37 @@ var Transliterators = map[string]Transliterator{
|
|||||||
"💓", "<3",
|
"💓", "<3",
|
||||||
"💚", "<3",
|
"💚", "<3",
|
||||||
"💙", "<3",
|
"💙", "<3",
|
||||||
|
"💟", "<3",
|
||||||
|
"❣️", "<3!",
|
||||||
"💔", "</3",
|
"💔", "</3",
|
||||||
"😱", "D:",
|
"😱", "D:",
|
||||||
"😮", ":O",
|
"😮", ":O",
|
||||||
"😝", ":P",
|
"😯", ":O",
|
||||||
"😍", ":x",
|
"😝", "xP",
|
||||||
"😢", ":(",
|
"🤔", "',:-|",
|
||||||
|
"😔", ":|",
|
||||||
|
"😍", ":*",
|
||||||
|
"😘", ":*",
|
||||||
|
"😚", ":*",
|
||||||
|
"😙", ":*",
|
||||||
|
"👍", ":thumbsup:",
|
||||||
|
"👌", ":ok_hand:",
|
||||||
|
"🤞", ":crossed_fingers:",
|
||||||
|
"✌️", ":victory_hand:",
|
||||||
|
"🌄", ":sunrise_over_mountains:",
|
||||||
|
"🌞", ":sun_with_face:",
|
||||||
|
"🤗", ":hugging_face:",
|
||||||
|
"🌻", ":sunflower:",
|
||||||
|
"🥱", ":yawning_face:",
|
||||||
|
"🙄", ":face_with_rolling_eyes:",
|
||||||
|
"🔫", ":gun:",
|
||||||
|
"🥔", ":potato:",
|
||||||
|
"😬", ":E",
|
||||||
|
"✨", "***",
|
||||||
|
"🌌", "***",
|
||||||
|
"💀", "8-X",
|
||||||
|
"😅", "':D",
|
||||||
|
"😢", ":'(",
|
||||||
"💯", ":100:",
|
"💯", ":100:",
|
||||||
"🔥", ":fire:",
|
"🔥", ":fire:",
|
||||||
"😉", ";)",
|
"😉", ";)",
|
||||||
|
|||||||
1
version.txt
Normal file
1
version.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
unknown
|
||||||
277
weather.go
Normal file
277
weather.go
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"go.arsenm.dev/infinitime"
|
||||||
|
"go.arsenm.dev/infinitime/weather"
|
||||||
|
)
|
||||||
|
|
||||||
|
// METResponse represents a response from
|
||||||
|
// the MET Norway API
|
||||||
|
type METResponse struct {
|
||||||
|
Properties struct {
|
||||||
|
Timeseries []struct {
|
||||||
|
Time time.Time
|
||||||
|
Data METData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// METData represents data in a METResponse
|
||||||
|
type METData struct {
|
||||||
|
Instant struct {
|
||||||
|
Details struct {
|
||||||
|
AirPressure float32 `json:"air_pressure_at_sea_level"`
|
||||||
|
AirTemperature float32 `json:"air_temperature"`
|
||||||
|
DewPoint float32 `json:"dew_point_temperature"`
|
||||||
|
CloudAreaFraction float32 `json:"cloud_area_fraction"`
|
||||||
|
FogAreaFraction float32 `json:"fog_area_fraction"`
|
||||||
|
RelativeHumidity float32 `json:"relative_humidity"`
|
||||||
|
UVIndex float32 `json:"ultraviolet_index_clear_sky"`
|
||||||
|
WindDirection float32 `json:"wind_from_direction"`
|
||||||
|
WindSpeed float32 `json:"wind_speed"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NextHour struct {
|
||||||
|
Summary struct {
|
||||||
|
SymbolCode string `json:"symbol_code"`
|
||||||
|
}
|
||||||
|
Details struct {
|
||||||
|
PrecipitationAmount float32 `json:"precipitation_amount"`
|
||||||
|
}
|
||||||
|
} `json:"next_1_hours"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OSMData represents lat/long data from
|
||||||
|
// OpenStreetMap Nominatim
|
||||||
|
type OSMData []struct {
|
||||||
|
Lat string `json:"lat"`
|
||||||
|
Lon string `json:"lon"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var sendWeatherCh = make(chan struct{}, 1)
|
||||||
|
|
||||||
|
func initWeather(dev *infinitime.Device) error {
|
||||||
|
if !k.Bool("weather.enabled") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get location based on string in config
|
||||||
|
lat, lon, err := getLocation(k.String("weather.location"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
timer := time.NewTimer(time.Hour)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
// Attempt to get weather
|
||||||
|
data, err := getWeather(lat, lon)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Error getting weather data")
|
||||||
|
// Wait 15 minutes before retrying
|
||||||
|
time.Sleep(15 * time.Minute)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current data
|
||||||
|
current := data.Properties.Timeseries[0]
|
||||||
|
currentData := current.Data.Instant.Details
|
||||||
|
|
||||||
|
// Add temperature event
|
||||||
|
err = dev.AddWeatherEvent(weather.TemperatureEvent{
|
||||||
|
TimelineHeader: weather.NewHeader(
|
||||||
|
weather.EventTypeTemperature,
|
||||||
|
time.Hour,
|
||||||
|
),
|
||||||
|
Temperature: int16(round(currentData.AirTemperature * 100)),
|
||||||
|
DewPoint: int16(round(currentData.DewPoint)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error adding temperature event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add precipitation event
|
||||||
|
err = dev.AddWeatherEvent(weather.PrecipitationEvent{
|
||||||
|
TimelineHeader: weather.NewHeader(
|
||||||
|
weather.EventTypePrecipitation,
|
||||||
|
time.Hour,
|
||||||
|
),
|
||||||
|
Type: parseSymbol(current.Data.NextHour.Summary.SymbolCode),
|
||||||
|
Amount: uint8(round(current.Data.NextHour.Details.PrecipitationAmount)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error adding precipitation event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add wind event
|
||||||
|
err = dev.AddWeatherEvent(weather.WindEvent{
|
||||||
|
TimelineHeader: weather.NewHeader(
|
||||||
|
weather.EventTypeWind,
|
||||||
|
time.Hour,
|
||||||
|
),
|
||||||
|
SpeedMin: uint8(round(currentData.WindSpeed)),
|
||||||
|
SpeedMax: uint8(round(currentData.WindSpeed)),
|
||||||
|
DirectionMin: uint8(round(currentData.WindDirection)),
|
||||||
|
DirectionMax: uint8(round(currentData.WindDirection)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error adding wind event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add cloud event
|
||||||
|
err = dev.AddWeatherEvent(weather.CloudsEvent{
|
||||||
|
TimelineHeader: weather.NewHeader(
|
||||||
|
weather.EventTypeClouds,
|
||||||
|
time.Hour,
|
||||||
|
),
|
||||||
|
Amount: uint8(round(currentData.CloudAreaFraction)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error adding clouds event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add humidity event
|
||||||
|
err = dev.AddWeatherEvent(weather.HumidityEvent{
|
||||||
|
TimelineHeader: weather.NewHeader(
|
||||||
|
weather.EventTypeHumidity,
|
||||||
|
time.Hour,
|
||||||
|
),
|
||||||
|
Humidity: uint8(round(currentData.RelativeHumidity)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error adding humidity event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pressure event
|
||||||
|
err = dev.AddWeatherEvent(weather.PressureEvent{
|
||||||
|
TimelineHeader: weather.NewHeader(
|
||||||
|
weather.EventTypePressure,
|
||||||
|
time.Hour,
|
||||||
|
),
|
||||||
|
Pressure: int16(round(currentData.AirPressure)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error adding pressure event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset timer to 1 hour
|
||||||
|
timer.Stop()
|
||||||
|
timer.Reset(time.Hour)
|
||||||
|
|
||||||
|
// Wait for timer to fire or manual update signal
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
case <-sendWeatherCh:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLocation returns the latitude and longitude
|
||||||
|
// given a location
|
||||||
|
func getLocation(loc string) (lat, lon float64, err error) {
|
||||||
|
// Create request URL and perform GET request
|
||||||
|
reqURL := fmt.Sprintf("https://nominatim.openstreetmap.org/search.php?q=%s&format=jsonv2", url.QueryEscape(loc))
|
||||||
|
res, err := http.Get(reqURL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode JSON from response into OSMData
|
||||||
|
data := OSMData{}
|
||||||
|
err = json.NewDecoder(res.Body).Decode(&data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If no data points
|
||||||
|
if len(data) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get first data point
|
||||||
|
out := data[0]
|
||||||
|
|
||||||
|
// Attempt to parse latitude
|
||||||
|
lat, err = strconv.ParseFloat(out.Lat, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Attempt to parse longitude
|
||||||
|
lon, err = strconv.ParseFloat(out.Lon, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWeather gets weather data given a latitude and longitude
|
||||||
|
func getWeather(lat, lon float64) (*METResponse, error) {
|
||||||
|
// Create new GET request
|
||||||
|
req, err := http.NewRequest(
|
||||||
|
http.MethodGet,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=%.2f&lon=%.2f",
|
||||||
|
lat,
|
||||||
|
lon,
|
||||||
|
),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set identifying user agent as per NMI requirements
|
||||||
|
req.Header.Set("User-Agent", fmt.Sprintf("ITD/%s gitea.arsenm.dev/Arsen6331/itd", version))
|
||||||
|
|
||||||
|
// Perform request
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode JSON from response to METResponse struct
|
||||||
|
out := &METResponse{}
|
||||||
|
err = json.NewDecoder(res.Body).Decode(out)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSymbol determines what type of precipitation a symbol code
|
||||||
|
// codes for.
|
||||||
|
func parseSymbol(symCode string) weather.PrecipitationType {
|
||||||
|
switch {
|
||||||
|
case strings.Contains(symCode, "lightrain"):
|
||||||
|
return weather.PrecipitationTypeRain
|
||||||
|
case strings.Contains(symCode, "rain"):
|
||||||
|
return weather.PrecipitationTypeRain
|
||||||
|
case strings.Contains(symCode, "snow"):
|
||||||
|
return weather.PrecipitationTypeSnow
|
||||||
|
case strings.Contains(symCode, "sleet"):
|
||||||
|
return weather.PrecipitationTypeSleet
|
||||||
|
case strings.Contains(symCode, "snow"):
|
||||||
|
return weather.PrecipitationTypeSnow
|
||||||
|
default:
|
||||||
|
return weather.PrecipitationTypeNone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// round rounds 32-bit floats to 32-bit integers
|
||||||
|
func round(f float32) int32 {
|
||||||
|
return int32(math.Round(float64(f)))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user