forked from Elara6331/itd
Switch from custom socket API to rpcx
This commit is contained in:
117
api/api.go
Normal file
117
api/api.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/smallnest/rpcx/client"
|
||||
"github.com/smallnest/rpcx/protocol"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
"go.arsenm.dev/infinitime"
|
||||
)
|
||||
|
||||
const DefaultAddr = "/tmp/itd/socket"
|
||||
|
||||
type Client struct {
|
||||
itdClient client.XClient
|
||||
itdCh chan *protocol.Message
|
||||
fsClient client.XClient
|
||||
fsCh chan *protocol.Message
|
||||
srvVals map[string]chan interface{}
|
||||
}
|
||||
|
||||
func New(sockPath string) (*Client, error) {
|
||||
d, err := client.NewPeer2PeerDiscovery("unix@"+sockPath, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := &Client{}
|
||||
|
||||
out.itdCh = make(chan *protocol.Message, 5)
|
||||
out.itdClient = client.NewBidirectionalXClient(
|
||||
"ITD",
|
||||
client.Failtry,
|
||||
client.RandomSelect,
|
||||
d,
|
||||
client.DefaultOption,
|
||||
out.itdCh,
|
||||
)
|
||||
|
||||
out.fsCh = make(chan *protocol.Message, 5)
|
||||
out.fsClient = client.NewBidirectionalXClient(
|
||||
"FS",
|
||||
client.Failtry,
|
||||
client.RandomSelect,
|
||||
d,
|
||||
client.DefaultOption,
|
||||
out.fsCh,
|
||||
)
|
||||
|
||||
out.srvVals = map[string]chan interface{}{}
|
||||
|
||||
go out.handleMessages(out.itdCh)
|
||||
go out.handleMessages(out.fsCh)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Client) handleMessages(msgCh chan *protocol.Message) {
|
||||
for msg := range msgCh {
|
||||
_, ok := c.srvVals[msg.ServicePath]
|
||||
if !ok {
|
||||
c.srvVals[msg.ServicePath] = make(chan interface{}, 5)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v\n", msg)
|
||||
|
||||
ch := c.srvVals[msg.ServicePath]
|
||||
|
||||
switch msg.ServiceMethod {
|
||||
case "FSProgress":
|
||||
var progress FSTransferProgress
|
||||
msgpack.Unmarshal(msg.Payload, &progress)
|
||||
ch <- progress
|
||||
case "DFUProgress":
|
||||
var progress infinitime.DFUProgress
|
||||
msgpack.Unmarshal(msg.Payload, &progress)
|
||||
ch <- progress
|
||||
case "MotionSample":
|
||||
var motionVals infinitime.MotionValues
|
||||
msgpack.Unmarshal(msg.Payload, &motionVals)
|
||||
ch <- motionVals
|
||||
case "Done":
|
||||
close(c.srvVals[msg.ServicePath])
|
||||
delete(c.srvVals, msg.ServicePath)
|
||||
default:
|
||||
var value interface{}
|
||||
msgpack.Unmarshal(msg.Payload, &value)
|
||||
ch <- value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) done(id string) error {
|
||||
return c.itdClient.Call(
|
||||
context.Background(),
|
||||
"Done",
|
||||
id,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
err := c.itdClient.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.fsClient.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
close(c.itdCh)
|
||||
close(c.fsCh)
|
||||
|
||||
return nil
|
||||
}
|
153
api/client.go
153
api/client.go
@@ -1,153 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"go.arsenm.dev/infinitime"
|
||||
"go.arsenm.dev/itd/internal/types"
|
||||
)
|
||||
|
||||
// Default socket address
|
||||
const DefaultAddr = "/tmp/itd/socket"
|
||||
|
||||
// Client is the socket API client
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
respCh chan types.Response
|
||||
heartRateCh chan types.Response
|
||||
battLevelCh chan types.Response
|
||||
stepCountCh chan types.Response
|
||||
motionCh chan types.Response
|
||||
dfuProgressCh chan types.Response
|
||||
readProgressCh chan types.FSTransferProgress
|
||||
writeProgressCh chan types.FSTransferProgress
|
||||
}
|
||||
|
||||
// New creates a new client and sets it up
|
||||
func New(addr string) (*Client, error) {
|
||||
conn, err := net.Dial("unix", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := &Client{
|
||||
conn: conn,
|
||||
respCh: make(chan types.Response, 5),
|
||||
}
|
||||
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(conn)
|
||||
for scanner.Scan() {
|
||||
var res types.Response
|
||||
err = json.Unmarshal(scanner.Bytes(), &res)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out.handleResp(res)
|
||||
}
|
||||
}()
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
err := c.conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
close(c.respCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
// request sends a request to itd and waits for and returns the response
|
||||
func (c *Client) request(req types.Request) (types.Response, error) {
|
||||
// Encode request into connection
|
||||
err := json.NewEncoder(c.conn).Encode(req)
|
||||
if err != nil {
|
||||
return types.Response{}, err
|
||||
}
|
||||
|
||||
res := <-c.respCh
|
||||
|
||||
if res.Error {
|
||||
return res, errors.New(res.Message)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// requestNoRes sends a request to itd and does not wait for the response
|
||||
func (c *Client) requestNoRes(req types.Request) error {
|
||||
// Encode request into connection
|
||||
err := json.NewEncoder(c.conn).Encode(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleResp handles the received response as needed
|
||||
func (c *Client) handleResp(res types.Response) error {
|
||||
switch res.Type {
|
||||
case types.ReqTypeWatchHeartRate:
|
||||
c.heartRateCh <- res
|
||||
case types.ReqTypeWatchBattLevel:
|
||||
c.battLevelCh <- res
|
||||
case types.ReqTypeWatchStepCount:
|
||||
c.stepCountCh <- res
|
||||
case types.ReqTypeWatchMotion:
|
||||
c.motionCh <- res
|
||||
case types.ReqTypeFwUpgrade:
|
||||
c.dfuProgressCh <- res
|
||||
case types.ReqTypeFS:
|
||||
if res.Value == nil {
|
||||
c.respCh <- res
|
||||
break
|
||||
}
|
||||
var progress types.FSTransferProgress
|
||||
if err := mapstructure.Decode(res.Value, &progress); err != nil {
|
||||
c.respCh <- res
|
||||
break
|
||||
}
|
||||
switch progress.Type {
|
||||
case types.FSTypeRead:
|
||||
c.readProgressCh <- progress
|
||||
case types.FSTypeWrite:
|
||||
c.writeProgressCh <- progress
|
||||
default:
|
||||
c.respCh <- res
|
||||
}
|
||||
default:
|
||||
c.respCh <- res
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeUint8(val interface{}) uint8 {
|
||||
return uint8(val.(float64))
|
||||
}
|
||||
|
||||
func decodeUint32(val interface{}) uint32 {
|
||||
return uint32(val.(float64))
|
||||
}
|
||||
|
||||
func decodeMotion(val interface{}) (infinitime.MotionValues, error) {
|
||||
out := infinitime.MotionValues{}
|
||||
err := mapstructure.Decode(val, &out)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func decodeDFUProgress(val interface{}) (DFUProgress, error) {
|
||||
out := DFUProgress{}
|
||||
err := mapstructure.Decode(val, &out)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
40
api/firmware.go
Normal file
40
api/firmware.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.arsenm.dev/infinitime"
|
||||
)
|
||||
|
||||
func (c *Client) FirmwareUpgrade(upgType UpgradeType, files ...string) (chan infinitime.DFUProgress, error) {
|
||||
var id string
|
||||
err := c.itdClient.Call(
|
||||
context.Background(),
|
||||
"FirmwareUpgrade",
|
||||
FwUpgradeData{
|
||||
Type: upgType,
|
||||
Files: files,
|
||||
},
|
||||
&id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
progressCh := make(chan infinitime.DFUProgress, 5)
|
||||
go func() {
|
||||
srvValCh, ok := c.srvVals[id]
|
||||
for !ok {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srvValCh, ok = c.srvVals[id]
|
||||
}
|
||||
|
||||
for val := range srvValCh {
|
||||
progressCh <- val.(infinitime.DFUProgress)
|
||||
}
|
||||
close(progressCh)
|
||||
}()
|
||||
|
||||
return progressCh, nil
|
||||
}
|
153
api/fs.go
153
api/fs.go
@@ -1,102 +1,101 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"go.arsenm.dev/itd/internal/types"
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 {
|
||||
return c.fsClient.Call(
|
||||
context.Background(),
|
||||
"Remove",
|
||||
paths,
|
||||
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) Rename(old, new string) error {
|
||||
return c.fsClient.Call(
|
||||
context.Background(),
|
||||
"Remove",
|
||||
[2]string{old, new},
|
||||
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
|
||||
return c.fsClient.Call(
|
||||
context.Background(),
|
||||
"Mkdir",
|
||||
paths,
|
||||
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) ReadDir(dir string) (out []FileInfo, err error) {
|
||||
err = c.fsClient.Call(
|
||||
context.Background(),
|
||||
"ReadDir",
|
||||
dir,
|
||||
&out,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
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},
|
||||
},
|
||||
})
|
||||
|
||||
func (c *Client) Upload(dst, src string) (chan FSTransferProgress, error) {
|
||||
var id string
|
||||
err := c.fsClient.Call(
|
||||
context.Background(),
|
||||
"Upload",
|
||||
[2]string{dst, src},
|
||||
&id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.readProgressCh, nil
|
||||
progressCh := make(chan FSTransferProgress, 5)
|
||||
go func() {
|
||||
srvValCh, ok := c.srvVals[id]
|
||||
for !ok {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srvValCh, ok = c.srvVals[id]
|
||||
}
|
||||
|
||||
for val := range srvValCh {
|
||||
progressCh <- val.(FSTransferProgress)
|
||||
}
|
||||
close(progressCh)
|
||||
}()
|
||||
|
||||
return progressCh, 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},
|
||||
},
|
||||
})
|
||||
func (c *Client) Download(dst, src string) (chan FSTransferProgress, error) {
|
||||
var id string
|
||||
err := c.fsClient.Call(
|
||||
context.Background(),
|
||||
"Download",
|
||||
[2]string{dst, src},
|
||||
&id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.writeProgressCh, nil
|
||||
progressCh := make(chan FSTransferProgress, 5)
|
||||
go func() {
|
||||
srvValCh, ok := c.srvVals[id]
|
||||
for !ok {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srvValCh, ok = c.srvVals[id]
|
||||
}
|
||||
|
||||
for val := range srvValCh {
|
||||
progressCh <- val.(FSTransferProgress)
|
||||
}
|
||||
close(progressCh)
|
||||
}()
|
||||
|
||||
return progressCh, nil
|
||||
}
|
||||
|
67
api/get.go
Normal file
67
api/get.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.arsenm.dev/infinitime"
|
||||
)
|
||||
|
||||
func (c *Client) HeartRate() (out uint8, err error) {
|
||||
err = c.itdClient.Call(
|
||||
context.Background(),
|
||||
"HeartRate",
|
||||
nil,
|
||||
&out,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) BatteryLevel() (out uint8, err error) {
|
||||
err = c.itdClient.Call(
|
||||
context.Background(),
|
||||
"BatteryLevel",
|
||||
nil,
|
||||
&out,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) Motion() (out infinitime.MotionValues, err error) {
|
||||
err = c.itdClient.Call(
|
||||
context.Background(),
|
||||
"Motion",
|
||||
nil,
|
||||
&out,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) StepCount() (out uint32, err error) {
|
||||
err = c.itdClient.Call(
|
||||
context.Background(),
|
||||
"StepCount",
|
||||
nil,
|
||||
&out,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) Version() (out string, err error) {
|
||||
err = c.itdClient.Call(
|
||||
context.Background(),
|
||||
"Version",
|
||||
nil,
|
||||
&out,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) Address() (out string, err error) {
|
||||
err = c.itdClient.Call(
|
||||
context.Background(),
|
||||
"Address",
|
||||
nil,
|
||||
&out,
|
||||
)
|
||||
return
|
||||
}
|
209
api/info.go
209
api/info.go
@@ -1,209 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"go.arsenm.dev/infinitime"
|
||||
"go.arsenm.dev/itd/internal/types"
|
||||
)
|
||||
|
||||
// Address gets the bluetooth address of the connected device
|
||||
func (c *Client) Address() (string, error) {
|
||||
res, err := c.request(types.Request{
|
||||
Type: types.ReqTypeBtAddress,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.Value.(string), nil
|
||||
}
|
||||
|
||||
// Version gets the firmware version of the connected device
|
||||
func (c *Client) Version() (string, error) {
|
||||
res, err := c.request(types.Request{
|
||||
Type: types.ReqTypeFwVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.Value.(string), nil
|
||||
}
|
||||
|
||||
// BatteryLevel gets the battery level of the connected device
|
||||
func (c *Client) BatteryLevel() (uint8, error) {
|
||||
res, err := c.request(types.Request{
|
||||
Type: types.ReqTypeBattLevel,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint8(res.Value.(float64)), nil
|
||||
}
|
||||
|
||||
// WatchBatteryLevel returns a channel which will contain
|
||||
// new battery level values as they update. Do not use after
|
||||
// calling cancellation function
|
||||
func (c *Client) WatchBatteryLevel() (<-chan uint8, func(), error) {
|
||||
c.battLevelCh = make(chan types.Response, 2)
|
||||
err := c.requestNoRes(types.Request{
|
||||
Type: types.ReqTypeWatchBattLevel,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
res := <-c.battLevelCh
|
||||
done, cancel := c.cancelFn(res.ID, c.battLevelCh)
|
||||
out := make(chan uint8, 2)
|
||||
go func() {
|
||||
for res := range c.battLevelCh {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
out <- decodeUint8(res.Value)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out, cancel, nil
|
||||
}
|
||||
|
||||
// HeartRate gets the heart rate from the connected device
|
||||
func (c *Client) HeartRate() (uint8, error) {
|
||||
res, err := c.request(types.Request{
|
||||
Type: types.ReqTypeHeartRate,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return decodeUint8(res.Value), nil
|
||||
}
|
||||
|
||||
// WatchHeartRate returns a channel which will contain
|
||||
// new heart rate values as they update. Do not use after
|
||||
// calling cancellation function
|
||||
func (c *Client) WatchHeartRate() (<-chan uint8, func(), error) {
|
||||
c.heartRateCh = make(chan types.Response, 2)
|
||||
err := c.requestNoRes(types.Request{
|
||||
Type: types.ReqTypeWatchHeartRate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
res := <-c.heartRateCh
|
||||
done, cancel := c.cancelFn(res.ID, c.heartRateCh)
|
||||
out := make(chan uint8, 2)
|
||||
go func() {
|
||||
for res := range c.heartRateCh {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
out <- decodeUint8(res.Value)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out, cancel, nil
|
||||
}
|
||||
|
||||
// cancelFn generates a cancellation function for the given
|
||||
// request type and channel
|
||||
func (c *Client) cancelFn(reqID string, ch chan types.Response) (chan struct{}, func()) {
|
||||
done := make(chan struct{}, 1)
|
||||
return done, func() {
|
||||
done <- struct{}{}
|
||||
close(ch)
|
||||
c.requestNoRes(types.Request{
|
||||
Type: types.ReqTypeCancel,
|
||||
Data: reqID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// StepCount gets the step count from the connected device
|
||||
func (c *Client) StepCount() (uint32, error) {
|
||||
res, err := c.request(types.Request{
|
||||
Type: types.ReqTypeStepCount,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(res.Value.(float64)), nil
|
||||
}
|
||||
|
||||
// WatchStepCount returns a channel which will contain
|
||||
// new step count values as they update. Do not use after
|
||||
// calling cancellation function
|
||||
func (c *Client) WatchStepCount() (<-chan uint32, func(), error) {
|
||||
c.stepCountCh = make(chan types.Response, 2)
|
||||
err := c.requestNoRes(types.Request{
|
||||
Type: types.ReqTypeWatchStepCount,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
res := <-c.stepCountCh
|
||||
done, cancel := c.cancelFn(res.ID, c.stepCountCh)
|
||||
out := make(chan uint32, 2)
|
||||
go func() {
|
||||
for res := range c.stepCountCh {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
out <- decodeUint32(res.Value)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out, cancel, nil
|
||||
}
|
||||
|
||||
// Motion gets the motion values from the connected device
|
||||
func (c *Client) Motion() (infinitime.MotionValues, error) {
|
||||
out := infinitime.MotionValues{}
|
||||
res, err := c.request(types.Request{
|
||||
Type: types.ReqTypeMotion,
|
||||
})
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
err = mapstructure.Decode(res.Value, &out)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// WatchMotion returns a channel which will contain
|
||||
// new motion values as they update. Do not use after
|
||||
// calling cancellation function
|
||||
func (c *Client) WatchMotion() (<-chan infinitime.MotionValues, func(), error) {
|
||||
c.motionCh = make(chan types.Response, 5)
|
||||
err := c.requestNoRes(types.Request{
|
||||
Type: types.ReqTypeWatchMotion,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
res := <-c.motionCh
|
||||
done, cancel := c.cancelFn(res.ID, c.motionCh)
|
||||
out := make(chan infinitime.MotionValues, 5)
|
||||
go func() {
|
||||
for res := range c.motionCh {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
motion, err := decodeMotion(res.Value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out <- motion
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out, cancel, nil
|
||||
}
|
@@ -1,14 +1,17 @@
|
||||
package api
|
||||
|
||||
import "go.arsenm.dev/itd/internal/types"
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func (c *Client) Notify(title string, body string) error {
|
||||
_, err := c.request(types.Request{
|
||||
Type: types.ReqTypeNotify,
|
||||
Data: types.ReqDataNotify{
|
||||
func (c *Client) Notify(title, body string) error {
|
||||
return c.itdClient.Call(
|
||||
context.Background(),
|
||||
"Notify",
|
||||
NotifyData{
|
||||
Title: title,
|
||||
Body: body,
|
||||
},
|
||||
})
|
||||
return err
|
||||
nil,
|
||||
)
|
||||
}
|
15
api/set.go
Normal file
15
api/set.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Client) SetTime(t time.Time) error {
|
||||
return c.itdClient.Call(
|
||||
context.Background(),
|
||||
"SetTime",
|
||||
t,
|
||||
nil,
|
||||
)
|
||||
}
|
33
api/time.go
33
api/time.go
@@ -1,33 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.arsenm.dev/itd/internal/types"
|
||||
)
|
||||
|
||||
// SetTime sets the given time on the connected device
|
||||
func (c *Client) SetTime(t time.Time) error {
|
||||
_, err := c.request(types.Request{
|
||||
Type: types.ReqTypeSetTime,
|
||||
Data: t.Format(time.RFC3339),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTimeNow sets the time on the connected device to
|
||||
// the current time. This is more accurate than
|
||||
// SetTime(time.Now()) due to RFC3339 formatting
|
||||
func (c *Client) SetTimeNow() error {
|
||||
_, err := c.request(types.Request{
|
||||
Type: types.ReqTypeSetTime,
|
||||
Data: "now",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
96
api/types.go
Normal file
96
api/types.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type UpgradeType uint8
|
||||
|
||||
const (
|
||||
UpgradeTypeArchive UpgradeType = iota
|
||||
UpgradeTypeFiles
|
||||
)
|
||||
|
||||
type FSData struct {
|
||||
Files []string
|
||||
Data string
|
||||
}
|
||||
|
||||
type FwUpgradeData struct {
|
||||
Type UpgradeType
|
||||
Files []string
|
||||
}
|
||||
|
||||
type NotifyData struct {
|
||||
Title string
|
||||
Body string
|
||||
}
|
||||
|
||||
type FSTransferProgress struct {
|
||||
Total uint32
|
||||
Sent uint32
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Size int64
|
||||
IsDir bool
|
||||
}
|
||||
|
||||
func (fi FileInfo) String() string {
|
||||
var isDirChar rune
|
||||
if fi.IsDir {
|
||||
isDirChar = 'd'
|
||||
} else {
|
||||
isDirChar = '-'
|
||||
}
|
||||
|
||||
// Get human-readable value for file size
|
||||
val, unit := bytesHuman(fi.Size)
|
||||
prec := 0
|
||||
// If value is less than 10, set precision to 1
|
||||
if val < 10 {
|
||||
prec = 1
|
||||
}
|
||||
// Convert float to string
|
||||
valStr := strconv.FormatFloat(val, 'f', prec, 64)
|
||||
|
||||
// Return string formatted like so:
|
||||
// - 10 kB file
|
||||
// or:
|
||||
// d 0 B .
|
||||
return fmt.Sprintf(
|
||||
"%c %3s %-2s %s",
|
||||
isDirChar,
|
||||
valStr,
|
||||
unit,
|
||||
fi.Name,
|
||||
)
|
||||
}
|
||||
|
||||
// bytesHuman returns a human-readable string for
|
||||
// the amount of bytes inputted.
|
||||
func bytesHuman(b int64) (float64, string) {
|
||||
const unit = 1000
|
||||
// Set possible units prefixes (PineTime flash is 4MB)
|
||||
units := [2]rune{'k', 'M'}
|
||||
// If amount of bytes is less than smallest unit
|
||||
if b < unit {
|
||||
// Return unchanged with unit "B"
|
||||
return float64(b), "B"
|
||||
}
|
||||
|
||||
div, exp := int64(unit), 0
|
||||
// Get decimal values and unit prefix index
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
|
||||
// Create string for full unit
|
||||
unitStr := string([]rune{units[exp], 'B'})
|
||||
|
||||
// Return decimal with unit string
|
||||
return float64(b) / float64(div), unitStr
|
||||
}
|
12
api/update.go
Normal file
12
api/update.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Client) WeatherUpdate() error {
|
||||
return c.itdClient.Call(
|
||||
context.Background(),
|
||||
"WeatherUpdate",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"go.arsenm.dev/itd/internal/types"
|
||||
)
|
||||
|
||||
// DFUProgress stores the progress of a DFU upfate
|
||||
type DFUProgress types.DFUProgress
|
||||
|
||||
// UpgradeType indicates the type of upgrade to be performed
|
||||
type UpgradeType uint8
|
||||
|
||||
// Type of DFU upgrade
|
||||
const (
|
||||
UpgradeTypeArchive UpgradeType = iota
|
||||
UpgradeTypeFiles
|
||||
)
|
||||
|
||||
// FirmwareUpgrade initiates a DFU update and returns the progress channel
|
||||
func (c *Client) FirmwareUpgrade(upgType UpgradeType, files ...string) (<-chan DFUProgress, error) {
|
||||
err := json.NewEncoder(c.conn).Encode(types.Request{
|
||||
Type: types.ReqTypeFwUpgrade,
|
||||
Data: types.ReqDataFwUpgrade{
|
||||
Type: int(upgType),
|
||||
Files: files,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.dfuProgressCh = make(chan types.Response, 5)
|
||||
|
||||
out := make(chan DFUProgress, 5)
|
||||
go func() {
|
||||
for res := range c.dfuProgressCh {
|
||||
progress, err := decodeDFUProgress(res.Value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out <- progress
|
||||
}
|
||||
}()
|
||||
|
||||
return out, nil
|
||||
}
|
144
api/watch.go
Normal file
144
api/watch.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.arsenm.dev/infinitime"
|
||||
)
|
||||
|
||||
func (c *Client) WatchHeartRate() (<-chan uint8, func(), error) {
|
||||
var id string
|
||||
err := c.itdClient.Call(
|
||||
context.Background(),
|
||||
"WatchHeartRate",
|
||||
nil,
|
||||
&id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
outCh := make(chan uint8, 2)
|
||||
go func() {
|
||||
srvValCh, ok := c.srvVals[id]
|
||||
for !ok {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srvValCh, ok = c.srvVals[id]
|
||||
}
|
||||
|
||||
for val := range srvValCh {
|
||||
outCh <- val.(uint8)
|
||||
}
|
||||
}()
|
||||
|
||||
doneFn := func() {
|
||||
c.done(id)
|
||||
close(c.srvVals[id])
|
||||
delete(c.srvVals, id)
|
||||
}
|
||||
|
||||
return outCh, doneFn, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchBatteryLevel() (<-chan uint8, func(), error) {
|
||||
var id string
|
||||
err := c.itdClient.Call(
|
||||
context.Background(),
|
||||
"WatchBatteryLevel",
|
||||
nil,
|
||||
&id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
outCh := make(chan uint8, 2)
|
||||
go func() {
|
||||
srvValCh, ok := c.srvVals[id]
|
||||
for !ok {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srvValCh, ok = c.srvVals[id]
|
||||
}
|
||||
|
||||
for val := range srvValCh {
|
||||
outCh <- val.(uint8)
|
||||
}
|
||||
}()
|
||||
|
||||
doneFn := func() {
|
||||
c.done(id)
|
||||
close(c.srvVals[id])
|
||||
delete(c.srvVals, id)
|
||||
}
|
||||
|
||||
return outCh, doneFn, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchStepCount() (<-chan uint32, func(), error) {
|
||||
var id string
|
||||
err := c.itdClient.Call(
|
||||
context.Background(),
|
||||
"WatchStepCount",
|
||||
nil,
|
||||
&id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
outCh := make(chan uint32, 2)
|
||||
go func() {
|
||||
srvValCh, ok := c.srvVals[id]
|
||||
for !ok {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srvValCh, ok = c.srvVals[id]
|
||||
}
|
||||
|
||||
for val := range srvValCh {
|
||||
outCh <- val.(uint32)
|
||||
}
|
||||
}()
|
||||
|
||||
doneFn := func() {
|
||||
c.done(id)
|
||||
close(c.srvVals[id])
|
||||
delete(c.srvVals, id)
|
||||
}
|
||||
|
||||
return outCh, doneFn, nil
|
||||
}
|
||||
|
||||
func (c *Client) WatchMotion() (<-chan infinitime.MotionValues, func(), error) {
|
||||
var id string
|
||||
err := c.itdClient.Call(
|
||||
context.Background(),
|
||||
"WatchMotion",
|
||||
nil,
|
||||
&id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
outCh := make(chan infinitime.MotionValues, 2)
|
||||
go func() {
|
||||
srvValCh, ok := c.srvVals[id]
|
||||
for !ok {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
srvValCh, ok = c.srvVals[id]
|
||||
}
|
||||
|
||||
for val := range srvValCh {
|
||||
outCh <- val.(infinitime.MotionValues)
|
||||
}
|
||||
}()
|
||||
|
||||
doneFn := func() {
|
||||
c.done(id)
|
||||
close(c.srvVals[id])
|
||||
delete(c.srvVals, id)
|
||||
}
|
||||
|
||||
return outCh, doneFn, nil
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"go.arsenm.dev/itd/internal/types"
|
||||
)
|
||||
|
||||
// UpdateWeather sends the update weather signal,
|
||||
// immediately sending current weather data
|
||||
func (c *Client) UpdateWeather() error {
|
||||
_, err := c.request(types.Request{
|
||||
Type: types.ReqTypeWeatherUpdate,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user