Send response types in socket responses and create api package

This commit is contained in:
Elara 2021-10-22 20:47:57 -07:00
parent b63960ed88
commit a235903583
6 changed files with 317 additions and 4 deletions

102
api/client.go Normal file
View File

@ -0,0 +1,102 @@
package api
import (
"bufio"
"encoding/json"
"errors"
"net"
"github.com/mitchellh/mapstructure"
"go.arsenm.dev/infinitime"
"go.arsenm.dev/itd/internal/types"
)
const DefaultAddr = "/tmp/itd/socket"
type Client struct {
conn net.Conn
respCh chan types.Response
heartRateCh chan uint8
battLevelCh chan uint8
stepCountCh chan uint32
motionCh chan infinitime.MotionValues
dfuProgressCh chan DFUProgress
}
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) 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
}
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
}
func (c *Client) handleResp(res types.Response) error {
switch res.Type {
case types.ResTypeWatchHeartRate:
c.heartRateCh <- uint8(res.Value.(float64))
case types.ResTypeWatchBattLevel:
c.battLevelCh <- uint8(res.Value.(float64))
case types.ResTypeWatchStepCount:
c.stepCountCh <- uint32(res.Value.(float64))
case types.ResTypeWatchMotion:
out := infinitime.MotionValues{}
err := mapstructure.Decode(res.Value, &out)
if err != nil {
return err
}
c.motionCh <- out
case types.ResTypeDFUProgress:
out := DFUProgress{}
err := mapstructure.Decode(res.Value, &out)
if err != nil {
return err
}
c.dfuProgressCh <- out
default:
c.respCh <- res
}
return nil
}

121
api/info.go Normal file
View File

@ -0,0 +1,121 @@
package api
import (
"github.com/mitchellh/mapstructure"
"go.arsenm.dev/infinitime"
"go.arsenm.dev/itd/internal/types"
)
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
}
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
}
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
}
func (c *Client) WatchBatteryLevel() (<-chan uint8, error) {
c.battLevelCh = make(chan uint8, 2)
err := c.requestNoRes(types.Request{
Type: types.ReqTypeBattLevel,
})
if err != nil {
return nil, err
}
return c.battLevelCh, nil
}
func (c *Client) HeartRate() (uint8, error) {
res, err := c.request(types.Request{
Type: types.ReqTypeHeartRate,
})
if err != nil {
return 0, err
}
return uint8(res.Value.(float64)), nil
}
func (c *Client) WatchHeartRate() (<-chan uint8, error) {
c.heartRateCh = make(chan uint8, 2)
err := c.requestNoRes(types.Request{
Type: types.ReqTypeWatchHeartRate,
})
if err != nil {
return nil, err
}
return c.heartRateCh, nil
}
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
}
func (c *Client) WatchStepCount() (<-chan uint32, error) {
c.stepCountCh = make(chan uint32, 2)
err := c.requestNoRes(types.Request{
Type: types.ReqTypeWatchStepCount,
})
if err != nil {
return nil, err
}
return c.stepCountCh, nil
}
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
}
func (c *Client) WatchMotion() (<-chan infinitime.MotionValues, error) {
c.motionCh = make(chan infinitime.MotionValues, 2)
err := c.requestNoRes(types.Request{
Type: types.ReqTypeWatchMotion,
})
if err != nil {
return nil, err
}
return c.motionCh, nil
}

29
api/time.go Normal file
View File

@ -0,0 +1,29 @@
package api
import (
"time"
"go.arsenm.dev/itd/internal/types"
)
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
}
func (c *Client) SetTimeNow() error {
_, err := c.request(types.Request{
Type: types.ReqTypeSetTime,
Data: "now",
})
if err != nil {
return err
}
return nil
}

33
api/upgrade.go Normal file
View File

@ -0,0 +1,33 @@
package api
import (
"encoding/json"
"go.arsenm.dev/itd/internal/types"
)
type DFUProgress types.DFUProgress
type UpgradeType uint8
const (
UpgradeTypeArchive UpgradeType = iota
UpgradeTypeFiles
)
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 DFUProgress, 5)
return c.dfuProgressCh, nil
}

View File

@ -16,6 +16,22 @@ const (
ReqTypeWatchStepCount
)
const (
ResTypeHeartRate = iota
ResTypeBattLevel
ResTypeFwVersion
ResTypeDFUProgress
ResTypeBtAddress
ResTypeNotify
ResTypeSetTime
ResTypeWatchHeartRate
ResTypeWatchBattLevel
ResTypeMotion
ResTypeWatchMotion
ResTypeStepCount
ResTypeWatchStepCount
)
const (
UpgradeTypeArchive = iota
UpgradeTypeFiles
@ -27,6 +43,7 @@ type ReqDataFwUpgrade struct {
}
type Response struct {
Type int
Value interface{} `json:"value,omitempty"`
Message string `json:"msg,omitempty"`
Error bool `json:"error"`

View File

@ -102,6 +102,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
}
// Encode heart rate to connection
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeHeartRate,
Value: heartRate,
})
case types.ReqTypeWatchHeartRate:
@ -113,6 +114,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
go func() {
for heartRate := range heartRateCh {
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeWatchHeartRate,
Value: heartRate,
})
}
@ -126,17 +128,19 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
}
// Encode battery level to connection
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeBattLevel,
Value: battLevel,
})
case types.ReqTypeWatchBattLevel:
battLevelCh, err := dev.WatchBatteryLevel()
if err != nil {
connErr(conn, err, "Error getting heart rate channel")
connErr(conn, err, "Error getting battery level channel")
break
}
go func() {
for battLevel := range battLevelCh {
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeWatchBattLevel,
Value: battLevel,
})
}
@ -150,6 +154,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
}
// Encode battery level to connection
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeMotion,
Value: motionVals,
})
case types.ReqTypeWatchMotion:
@ -161,6 +166,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
go func() {
for motionVals := range motionValCh {
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeWatchMotion,
Value: motionVals,
})
}
@ -174,6 +180,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
}
// Encode battery level to connection
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeStepCount,
Value: stepCount,
})
case types.ReqTypeWatchStepCount:
@ -185,6 +192,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
go func() {
for stepCount := range stepCountCh {
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeWatchStepCount,
Value: stepCount,
})
}
@ -193,16 +201,18 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
// Get firmware version from watch
version, err := dev.Version()
if err != nil {
connErr(conn, err, "Error getting battery level")
connErr(conn, err, "Error getting firmware version")
break
}
// Encode version to connection
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeFwVersion,
Value: version,
})
case types.ReqTypeBtAddress:
// Encode bluetooth address to connection
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeBtAddress,
Value: dev.Address(),
})
case types.ReqTypeNotify:
@ -229,7 +239,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
break
}
// Encode empty types.Response to connection
json.NewEncoder(conn).Encode(types.Response{})
json.NewEncoder(conn).Encode(types.Response{Type: types.ResTypeNotify})
case types.ReqTypeSetTime:
// If no data, return error
if req.Data == nil {
@ -261,7 +271,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
break
}
// Encode empty types.Response to connection
json.NewEncoder(conn).Encode(types.Response{})
json.NewEncoder(conn).Encode(types.Response{Type: types.ResTypeSetTime})
case types.ReqTypeFwUpgrade:
// If no data, return error
if req.Data == nil {
@ -326,6 +336,7 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
for event := range progress {
// Encode event on connection
json.NewEncoder(conn).Encode(types.Response{
Type: types.ResTypeDFUProgress,
Value: event,
})
}