forked from Elara6331/itd
		
	Send response types in socket responses and create api package
This commit is contained in:
		
							
								
								
									
										102
									
								
								api/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								api/client.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										121
									
								
								api/info.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										29
									
								
								api/time.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										33
									
								
								api/upgrade.go
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
| @@ -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"` | ||||
|   | ||||
							
								
								
									
										19
									
								
								socket.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								socket.go
									
									
									
									
									
								
							| @@ -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, | ||||
| 					}) | ||||
| 				} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user