Compare commits
No commits in common. "2ea9f99db6c6e3167ef79eb3b52604ed2890bdc2" and "8cf2b47733c410e1d064a5849b462743d53d4764" have entirely different histories.
2ea9f99db6
...
8cf2b47733
@ -18,11 +18,11 @@ const DefaultAddr = "/tmp/itd/socket"
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
respCh chan types.Response
|
respCh chan types.Response
|
||||||
heartRateCh chan types.Response
|
heartRateCh chan uint8
|
||||||
battLevelCh chan types.Response
|
battLevelCh chan uint8
|
||||||
stepCountCh chan types.Response
|
stepCountCh chan uint32
|
||||||
motionCh chan types.Response
|
motionCh chan infinitime.MotionValues
|
||||||
dfuProgressCh chan types.Response
|
dfuProgressCh chan DFUProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new client and sets it up
|
// New creates a new client and sets it up
|
||||||
@ -91,43 +91,27 @@ func (c *Client) requestNoRes(req types.Request) error {
|
|||||||
func (c *Client) handleResp(res types.Response) error {
|
func (c *Client) handleResp(res types.Response) error {
|
||||||
switch res.Type {
|
switch res.Type {
|
||||||
case types.ResTypeWatchHeartRate:
|
case types.ResTypeWatchHeartRate:
|
||||||
c.heartRateCh <- res
|
c.heartRateCh <- uint8(res.Value.(float64))
|
||||||
case types.ResTypeWatchBattLevel:
|
case types.ResTypeWatchBattLevel:
|
||||||
c.battLevelCh <- res
|
c.battLevelCh <- uint8(res.Value.(float64))
|
||||||
case types.ResTypeWatchStepCount:
|
case types.ResTypeWatchStepCount:
|
||||||
c.stepCountCh <- res
|
c.stepCountCh <- uint32(res.Value.(float64))
|
||||||
case types.ResTypeWatchMotion:
|
case types.ResTypeWatchMotion:
|
||||||
c.motionCh <- res
|
out := infinitime.MotionValues{}
|
||||||
|
err := mapstructure.Decode(res.Value, &out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.motionCh <- out
|
||||||
case types.ResTypeDFUProgress:
|
case types.ResTypeDFUProgress:
|
||||||
c.dfuProgressCh <- res
|
out := DFUProgress{}
|
||||||
|
err := mapstructure.Decode(res.Value, &out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.dfuProgressCh <- out
|
||||||
default:
|
default:
|
||||||
c.respCh <- res
|
c.respCh <- res
|
||||||
}
|
}
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
93
api/info.go
93
api/info.go
@ -1,6 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"go.arsenm.dev/infinitime"
|
"go.arsenm.dev/infinitime"
|
||||||
"go.arsenm.dev/itd/internal/types"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
@ -46,27 +48,15 @@ func (c *Client) BatteryLevel() (uint8, error) {
|
|||||||
// new battery level values as they update. Do not use after
|
// new battery level values as they update. Do not use after
|
||||||
// calling cancellation function
|
// calling cancellation function
|
||||||
func (c *Client) WatchBatteryLevel() (<-chan uint8, func(), error) {
|
func (c *Client) WatchBatteryLevel() (<-chan uint8, func(), error) {
|
||||||
c.battLevelCh = make(chan types.Response, 2)
|
c.battLevelCh = make(chan uint8, 2)
|
||||||
err := c.requestNoRes(types.Request{
|
err := c.requestNoRes(types.Request{
|
||||||
Type: types.ReqTypeBattLevel,
|
Type: types.ReqTypeBattLevel,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
res := <-c.battLevelCh
|
cancel := c.cancelFn(types.ReqTypeCancelBattLevel, c.battLevelCh)
|
||||||
done, cancel := c.cancelFn(res.ID, c.battLevelCh)
|
return c.battLevelCh, cancel, nil
|
||||||
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
|
// HeartRate gets the heart rate from the connected device
|
||||||
@ -78,46 +68,33 @@ func (c *Client) HeartRate() (uint8, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodeUint8(res.Value), nil
|
return uint8(res.Value.(float64)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchHeartRate returns a channel which will contain
|
// WatchHeartRate returns a channel which will contain
|
||||||
// new heart rate values as they update. Do not use after
|
// new heart rate values as they update. Do not use after
|
||||||
// calling cancellation function
|
// calling cancellation function
|
||||||
func (c *Client) WatchHeartRate() (<-chan uint8, func(), error) {
|
func (c *Client) WatchHeartRate() (<-chan uint8, func(), error) {
|
||||||
c.heartRateCh = make(chan types.Response, 2)
|
c.heartRateCh = make(chan uint8, 2)
|
||||||
err := c.requestNoRes(types.Request{
|
err := c.requestNoRes(types.Request{
|
||||||
Type: types.ReqTypeWatchHeartRate,
|
Type: types.ReqTypeWatchHeartRate,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
res := <-c.heartRateCh
|
cancel := c.cancelFn(types.ReqTypeCancelHeartRate, c.heartRateCh)
|
||||||
done, cancel := c.cancelFn(res.ID, c.heartRateCh)
|
return c.heartRateCh, cancel, nil
|
||||||
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
|
// cancelFn generates a cancellation function for the given
|
||||||
// request type and channel
|
// request type and channel
|
||||||
func (c *Client) cancelFn(reqID string, ch chan types.Response) (chan struct{}, func()) {
|
func (c *Client) cancelFn(reqType int, ch interface{}) func() {
|
||||||
done := make(chan struct{}, 1)
|
return func() {
|
||||||
return done, func() {
|
reflectCh := reflect.ValueOf(ch)
|
||||||
done <- struct{}{}
|
reflectCh.Close()
|
||||||
close(ch)
|
reflectCh.Set(reflect.Zero(reflectCh.Type()))
|
||||||
c.requestNoRes(types.Request{
|
c.requestNoRes(types.Request{
|
||||||
Type: types.ReqTypeCancel,
|
Type: reqType,
|
||||||
Data: reqID,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,27 +115,15 @@ func (c *Client) StepCount() (uint32, error) {
|
|||||||
// new step count values as they update. Do not use after
|
// new step count values as they update. Do not use after
|
||||||
// calling cancellation function
|
// calling cancellation function
|
||||||
func (c *Client) WatchStepCount() (<-chan uint32, func(), error) {
|
func (c *Client) WatchStepCount() (<-chan uint32, func(), error) {
|
||||||
c.stepCountCh = make(chan types.Response, 2)
|
c.stepCountCh = make(chan uint32, 2)
|
||||||
err := c.requestNoRes(types.Request{
|
err := c.requestNoRes(types.Request{
|
||||||
Type: types.ReqTypeWatchStepCount,
|
Type: types.ReqTypeWatchStepCount,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
res := <-c.stepCountCh
|
cancel := c.cancelFn(types.ReqTypeCancelStepCount, c.stepCountCh)
|
||||||
done, cancel := c.cancelFn(res.ID, c.stepCountCh)
|
return c.stepCountCh, cancel, nil
|
||||||
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
|
// Motion gets the motion values from the connected device
|
||||||
@ -181,29 +146,13 @@ func (c *Client) Motion() (infinitime.MotionValues, error) {
|
|||||||
// new motion values as they update. Do not use after
|
// new motion values as they update. Do not use after
|
||||||
// calling cancellation function
|
// calling cancellation function
|
||||||
func (c *Client) WatchMotion() (<-chan infinitime.MotionValues, func(), error) {
|
func (c *Client) WatchMotion() (<-chan infinitime.MotionValues, func(), error) {
|
||||||
c.motionCh = make(chan types.Response, 5)
|
c.motionCh = make(chan infinitime.MotionValues, 2)
|
||||||
err := c.requestNoRes(types.Request{
|
err := c.requestNoRes(types.Request{
|
||||||
Type: types.ReqTypeWatchMotion,
|
Type: types.ReqTypeWatchMotion,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
res := <-c.motionCh
|
cancel := c.cancelFn(types.ReqTypeCancelMotion, c.motionCh)
|
||||||
done, cancel := c.cancelFn(res.ID, c.motionCh)
|
return c.motionCh, cancel, nil
|
||||||
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 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -31,18 +31,7 @@ func (c *Client) FirmwareUpgrade(upgType UpgradeType, files ...string) (<-chan D
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.dfuProgressCh = make(chan types.Response, 5)
|
c.dfuProgressCh = make(chan DFUProgress, 5)
|
||||||
|
|
||||||
out := make(chan DFUProgress, 5)
|
return c.dfuProgressCh, nil
|
||||||
go func() {
|
|
||||||
for res := range c.dfuProgressCh {
|
|
||||||
progress, err := decodeDFUProgress(res.Value)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out <- progress
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,18 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package get
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// addressCmd represents the address command
|
// addressCmd represents the address command
|
||||||
@ -33,14 +36,40 @@ var addressCmd = &cobra.Command{
|
|||||||
Aliases: []string{"addr"},
|
Aliases: []string{"addr"},
|
||||||
Short: "Get InfiniTime's bluetooth address",
|
Short: "Get InfiniTime's bluetooth address",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
client := viper.Get("client").(*api.Client)
|
// Connect to itd UNIX socket
|
||||||
|
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
||||||
address, err := client.Address()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error getting bluetooth address")
|
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(address)
|
// 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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -16,15 +16,18 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package get
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// batteryCmd represents the batt command
|
// batteryCmd represents the batt command
|
||||||
@ -33,15 +36,40 @@ var batteryCmd = &cobra.Command{
|
|||||||
Aliases: []string{"batt"},
|
Aliases: []string{"batt"},
|
||||||
Short: "Get battery level from InfiniTime",
|
Short: "Get battery level from InfiniTime",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
client := viper.Get("client").(*api.Client)
|
// Connect to itd UNIX socket
|
||||||
|
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
||||||
battLevel, err := client.BatteryLevel()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error getting battery level")
|
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
|
// Print returned percentage
|
||||||
fmt.Printf("%d%%\n", battLevel)
|
fmt.Printf("%d%%\n", int(res.Value.(float64)))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -16,34 +16,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package get
|
package cmd
|
||||||
|
|
||||||
import (
|
type DFUProgress struct {
|
||||||
"fmt"
|
Received int64 `mapstructure:"recvd"`
|
||||||
|
Total int64 `mapstructure:"total"`
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.arsenm.dev/itd/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
// heartCmd represents the heart command
|
|
||||||
var heartCmd = &cobra.Command{
|
|
||||||
Use: "heart",
|
|
||||||
Short: "Get heart rate from InfiniTime",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
client := viper.Get("client").(*api.Client)
|
|
||||||
|
|
||||||
heartRate, err := client.HeartRate()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Error getting heart rate")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print returned BPM
|
|
||||||
fmt.Printf("%d BPM\n", heartRate)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
getCmd.AddCommand(heartCmd)
|
|
||||||
}
|
}
|
@ -16,11 +16,10 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package firmware
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.arsenm.dev/itd/cmd/itctl/root"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// firmwareCmd represents the firmware command
|
// firmwareCmd represents the firmware command
|
||||||
@ -31,5 +30,5 @@ var firmwareCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
root.RootCmd.AddCommand(firmwareCmd)
|
rootCmd.AddCommand(firmwareCmd)
|
||||||
}
|
}
|
@ -16,11 +16,10 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package get
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.arsenm.dev/itd/cmd/itctl/root"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// getCmd represents the get command
|
// getCmd represents the get command
|
||||||
@ -30,5 +29,5 @@ var getCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
root.RootCmd.AddCommand(getCmd)
|
rootCmd.AddCommand(getCmd)
|
||||||
}
|
}
|
77
cmd/itctl/cmd/heart.go
Normal file
77
cmd/itctl/cmd/heart.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
}
|
@ -16,15 +16,19 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package get
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// steps.goCmd represents the steps.go command
|
// steps.goCmd represents the steps.go command
|
||||||
@ -32,11 +36,42 @@ var motionCmd = &cobra.Command{
|
|||||||
Use: "motion",
|
Use: "motion",
|
||||||
Short: "Get motion values from InfiniTime",
|
Short: "Get motion values from InfiniTime",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
client := viper.Get("client").(*api.Client)
|
// Connect to itd UNIX socket
|
||||||
|
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
||||||
motionVals, err := client.Motion()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error getting motion values")
|
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.ReqTypeMotion,
|
||||||
|
})
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
var motionVals types.MotionValues
|
||||||
|
err = mapstructure.Decode(res.Value, &motionVals)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Error decoding motion values")
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Error {
|
||||||
|
log.Fatal().Msg(res.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if viper.GetBool("shell") {
|
if viper.GetBool("shell") {
|
@ -16,14 +16,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package notify
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
"go.arsenm.dev/itd/cmd/itctl/root"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// notifyCmd represents the notify command
|
// notifyCmd represents the notify command
|
||||||
@ -37,15 +40,44 @@ var notifyCmd = &cobra.Command{
|
|||||||
log.Fatal().Msg("Command notify requires two arguments")
|
log.Fatal().Msg("Command notify requires two arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
client := viper.Get("client").(*api.Client)
|
// Connect to itd UNIX socket
|
||||||
|
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
||||||
err := client.Notify(args[0], args[1])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error sending notification")
|
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() {
|
func init() {
|
||||||
root.RootCmd.AddCommand(notifyCmd)
|
rootCmd.AddCommand(notifyCmd)
|
||||||
}
|
}
|
@ -16,18 +16,16 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package root
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/abiosoft/ishell"
|
"github.com/abiosoft/ishell"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RootCmd represents the base command when called without any subcommands
|
// rootCmd represents the base command when called without any subcommands
|
||||||
var RootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "itctl",
|
Use: "itctl",
|
||||||
Short: "Control the itd daemon for InfiniTime smartwatches",
|
Short: "Control the itd daemon for InfiniTime smartwatches",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
@ -63,25 +61,18 @@ var RootCmd = &cobra.Command{
|
|||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// 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.
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
func Execute() {
|
func Execute() {
|
||||||
client, err := api.New(viper.GetString("sockPath"))
|
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||||
if err != nil {
|
cobra.CheckErr(rootCmd.Execute())
|
||||||
log.Fatal().Err(err).Msg("Error connecting to socket. Is itd running?")
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
viper.Set("client", client)
|
|
||||||
RootCmd.CompletionOptions.DisableDefaultCmd = true
|
|
||||||
cobra.CheckErr(RootCmd.Execute())
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Register flag for socket path
|
// Register flag for socket path
|
||||||
RootCmd.Flags().StringP("socket-path", "s", api.DefaultAddr, "Path to itd socket")
|
rootCmd.Flags().StringP("socket-path", "s", "", "Path to itd socket")
|
||||||
|
|
||||||
// Bind flag and environment variable to viper key
|
// Bind flag and environment variable to viper key
|
||||||
viper.BindPFlag("sockPath", RootCmd.Flags().Lookup("socket-path"))
|
viper.BindPFlag("sockPath", rootCmd.Flags().Lookup("socket-path"))
|
||||||
viper.BindEnv("sockPath", "ITCTL_SOCKET_PATH")
|
viper.BindEnv("sockPath", "ITCTL_SOCKET_PATH")
|
||||||
|
|
||||||
// Set default value for socket path
|
// Set default value for socket path
|
||||||
viper.SetDefault("sockPath", api.DefaultAddr)
|
viper.SetDefault("sockPath", "/tmp/itd/socket")
|
||||||
}
|
}
|
@ -16,11 +16,10 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package set
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.arsenm.dev/itd/cmd/itctl/root"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// setCmd represents the set command
|
// setCmd represents the set command
|
||||||
@ -30,5 +29,5 @@ var setCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
root.RootCmd.AddCommand(setCmd)
|
rootCmd.AddCommand(setCmd)
|
||||||
}
|
}
|
@ -16,15 +16,18 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package get
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// steps.goCmd represents the steps.go command
|
// steps.goCmd represents the steps.go command
|
||||||
@ -32,15 +35,40 @@ var stepsCmd = &cobra.Command{
|
|||||||
Use: "steps",
|
Use: "steps",
|
||||||
Short: "Get step count from InfiniTime",
|
Short: "Get step count from InfiniTime",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
client := viper.Get("client").(*api.Client)
|
// Connect to itd UNIX socket
|
||||||
|
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
||||||
stepCount, err := client.StepCount()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error getting step count")
|
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.ReqTypeStepCount,
|
||||||
|
})
|
||||||
|
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
|
// Print returned BPM
|
||||||
fmt.Printf("%d Steps\n", stepCount)
|
fmt.Printf("%d Steps\n", int(res.Value.(float64)))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package set
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
@ -16,50 +16,61 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package firmware
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/cheggaaa/pb/v3"
|
"github.com/cheggaaa/pb/v3"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
|
||||||
"go.arsenm.dev/itd/internal/types"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DFUProgress struct {
|
|
||||||
Received int64 `mapstructure:"recvd"`
|
|
||||||
Total int64 `mapstructure:"total"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// upgradeCmd represents the upgrade command
|
// upgradeCmd represents the upgrade command
|
||||||
var upgradeCmd = &cobra.Command{
|
var upgradeCmd = &cobra.Command{
|
||||||
Use: "upgrade",
|
Use: "upgrade",
|
||||||
Short: "Upgrade InfiniTime firmware using files or archive",
|
Short: "Upgrade InfiniTime firmware using files or archive",
|
||||||
Aliases: []string{"upg"},
|
Aliases: []string{"upg"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
client := viper.Get("client").(*api.Client)
|
// 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 upgType api.UpgradeType
|
var data types.ReqDataFwUpgrade
|
||||||
var files []string
|
|
||||||
// Get relevant data struct
|
// Get relevant data struct
|
||||||
if viper.GetString("archive") != "" {
|
if viper.GetString("archive") != "" {
|
||||||
// Get archive data struct
|
// Get archive data struct
|
||||||
upgType = types.UpgradeTypeArchive
|
data = types.ReqDataFwUpgrade{
|
||||||
files = []string{viper.GetString("archive")}
|
Type: types.UpgradeTypeArchive,
|
||||||
|
Files: []string{viper.GetString("archive")},
|
||||||
|
}
|
||||||
} else if viper.GetString("initPkt") != "" && viper.GetString("firmware") != "" {
|
} else if viper.GetString("initPkt") != "" && viper.GetString("firmware") != "" {
|
||||||
// Get files data struct
|
// Get files data struct
|
||||||
upgType = types.UpgradeTypeFiles
|
data = types.ReqDataFwUpgrade{
|
||||||
files = []string{viper.GetString("initPkt"), viper.GetString("firmware")}
|
Type: types.UpgradeTypeFiles,
|
||||||
|
Files: []string{viper.GetString("initPkt"), viper.GetString("firmware")},
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
log.Warn().Msg("Upgrade command requires either archive or init packet and firmware.")
|
log.Warn().Msg("Upgrade command requires either archive or init packet and firmware.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
progress, err := client.FirmwareUpgrade(upgType, files...)
|
// Encode response into connection
|
||||||
|
err = json.NewEncoder(conn).Encode(types.Request{
|
||||||
|
Type: types.ReqTypeFwUpgrade,
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error initiating DFU")
|
log.Fatal().Err(err).Msg("Error making request")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create progress bar template
|
// Create progress bar template
|
||||||
@ -67,18 +78,37 @@ var upgradeCmd = &cobra.Command{
|
|||||||
// Start full bar at 0 total
|
// Start full bar at 0 total
|
||||||
bar := pb.ProgressBarTemplate(barTmpl).Start(0)
|
bar := pb.ProgressBarTemplate(barTmpl).Start(0)
|
||||||
// Create new scanner of connection
|
// Create new scanner of connection
|
||||||
for event := range progress {
|
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
|
// Set total bytes in progress bar
|
||||||
bar.SetTotal(event.Total)
|
bar.SetTotal(event.Total)
|
||||||
// Set amount of bytes received in progress bar
|
// Set amount of bytes received in progress bar
|
||||||
bar.SetCurrent(event.Received)
|
bar.SetCurrent(event.Received)
|
||||||
// If transfer finished, break
|
// If transfer finished, break
|
||||||
if event.Sent == event.Total {
|
if event.Received == event.Total {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finish progress bar
|
// Finish progress bar
|
||||||
bar.Finish()
|
bar.Finish()
|
||||||
|
if scanner.Err() != nil {
|
||||||
|
log.Fatal().Err(scanner.Err()).Msg("Error while scanning output")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -16,15 +16,18 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package firmware
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.arsenm.dev/itd/api"
|
"go.arsenm.dev/itd/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// versionCmd represents the version command
|
// versionCmd represents the version command
|
||||||
@ -33,14 +36,40 @@ var versionCmd = &cobra.Command{
|
|||||||
Aliases: []string{"ver"},
|
Aliases: []string{"ver"},
|
||||||
Short: "Get firmware version of InfiniTime",
|
Short: "Get firmware version of InfiniTime",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
client := viper.Get("client").(*api.Client)
|
// Connect to itd UNIX socket
|
||||||
|
conn, err := net.Dial("unix", viper.GetString("sockPath"))
|
||||||
version, err := client.Version()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error getting firmware version")
|
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(version)
|
// 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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -19,14 +19,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "go.arsenm.dev/itd/cmd/itctl/firmware"
|
|
||||||
_ "go.arsenm.dev/itd/cmd/itctl/get"
|
|
||||||
_ "go.arsenm.dev/itd/cmd/itctl/notify"
|
|
||||||
"go.arsenm.dev/itd/cmd/itctl/root"
|
|
||||||
_ "go.arsenm.dev/itd/cmd/itctl/set"
|
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@ -36,5 +32,5 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
root.Execute()
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -13,7 +13,6 @@ require (
|
|||||||
github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259 // indirect
|
github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259 // indirect
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect
|
||||||
github.com/godbus/dbus/v5 v5.0.5
|
github.com/godbus/dbus/v5 v5.0.5
|
||||||
github.com/google/uuid v1.1.2
|
|
||||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.2
|
github.com/mitchellh/mapstructure v1.4.2
|
||||||
|
1
go.sum
1
go.sum
@ -193,7 +193,6 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
|
|||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
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 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
@ -9,12 +9,15 @@ const (
|
|||||||
ReqTypeNotify
|
ReqTypeNotify
|
||||||
ReqTypeSetTime
|
ReqTypeSetTime
|
||||||
ReqTypeWatchHeartRate
|
ReqTypeWatchHeartRate
|
||||||
|
ReqTypeCancelHeartRate
|
||||||
ReqTypeWatchBattLevel
|
ReqTypeWatchBattLevel
|
||||||
|
ReqTypeCancelBattLevel
|
||||||
ReqTypeMotion
|
ReqTypeMotion
|
||||||
ReqTypeWatchMotion
|
ReqTypeWatchMotion
|
||||||
|
ReqTypeCancelMotion
|
||||||
ReqTypeStepCount
|
ReqTypeStepCount
|
||||||
ReqTypeWatchStepCount
|
ReqTypeWatchStepCount
|
||||||
ReqTypeCancel
|
ReqTypeCancelStepCount
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -26,12 +29,15 @@ const (
|
|||||||
ResTypeNotify
|
ResTypeNotify
|
||||||
ResTypeSetTime
|
ResTypeSetTime
|
||||||
ResTypeWatchHeartRate
|
ResTypeWatchHeartRate
|
||||||
|
ResTypeCancelHeartRate
|
||||||
ResTypeWatchBattLevel
|
ResTypeWatchBattLevel
|
||||||
|
ResTypeCancelBattLevel
|
||||||
ResTypeMotion
|
ResTypeMotion
|
||||||
ResTypeWatchMotion
|
ResTypeWatchMotion
|
||||||
|
ResTypeCancelMotion
|
||||||
ResTypeStepCount
|
ResTypeStepCount
|
||||||
ResTypeWatchStepCount
|
ResTypeWatchStepCount
|
||||||
ResTypeCancel
|
ResTypeCancelStepCount
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -48,7 +54,6 @@ type Response struct {
|
|||||||
Type int `json:"type"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +70,6 @@ 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 MotionValues struct {
|
type MotionValues struct {
|
||||||
|
83
socket.go
83
socket.go
@ -27,7 +27,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"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"
|
"github.com/spf13/viper"
|
||||||
@ -36,29 +35,6 @@ import (
|
|||||||
"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-existant
|
// Make socket directory if non-existant
|
||||||
err := os.MkdirAll(filepath.Dir(viper.GetString("socket.path")), 0755)
|
err := os.MkdirAll(filepath.Dir(viper.GetString("socket.path")), 0755)
|
||||||
@ -105,6 +81,11 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
heartRateDone := make(chan struct{})
|
||||||
|
battLevelDone := make(chan struct{})
|
||||||
|
stepCountDone := make(chan struct{})
|
||||||
|
motionDone := make(chan struct{})
|
||||||
|
|
||||||
// Create new scanner on connection
|
// Create new scanner on connection
|
||||||
scanner := bufio.NewScanner(conn)
|
scanner := bufio.NewScanner(conn)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
@ -135,27 +116,27 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
connErr(conn, err, "Error getting heart rate channel")
|
connErr(conn, 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 every heart rate value
|
||||||
for heartRate := range heartRateCh {
|
for heartRate := range heartRateCh {
|
||||||
select {
|
select {
|
||||||
case <-done[reqID]:
|
case <-heartRateDone:
|
||||||
// Stop notifications if done signal received
|
// Stop notifications if done signal received
|
||||||
cancel()
|
cancel()
|
||||||
done.Remove(reqID)
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// Encode response to connection if no done signal received
|
// Encode response to connection if no done signal received
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
Type: types.ResTypeWatchHeartRate,
|
Type: types.ResTypeWatchHeartRate,
|
||||||
ID: reqID,
|
|
||||||
Value: heartRate,
|
Value: heartRate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
case types.ReqTypeCancelHeartRate:
|
||||||
|
// Stop heart rate notifications
|
||||||
|
heartRateDone <- struct{}{}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{})
|
||||||
case types.ReqTypeBattLevel:
|
case types.ReqTypeBattLevel:
|
||||||
// Get battery level from watch
|
// Get battery level from watch
|
||||||
battLevel, err := dev.BatteryLevel()
|
battLevel, err := dev.BatteryLevel()
|
||||||
@ -174,27 +155,27 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
connErr(conn, err, "Error getting battery level channel")
|
connErr(conn, err, "Error getting battery level channel")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
reqID := uuid.New().String()
|
|
||||||
go func() {
|
go func() {
|
||||||
done.Create(reqID)
|
|
||||||
// For every battery level value
|
// For every battery level value
|
||||||
for battLevel := range battLevelCh {
|
for battLevel := range battLevelCh {
|
||||||
select {
|
select {
|
||||||
case <-done[reqID]:
|
case <-battLevelDone:
|
||||||
// Stop notifications if done signal received
|
// Stop notifications if done signal received
|
||||||
cancel()
|
cancel()
|
||||||
done.Remove(reqID)
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// Encode response to connection if no done signal received
|
// Encode response to connection if no done signal received
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
Type: types.ResTypeWatchBattLevel,
|
Type: types.ResTypeWatchBattLevel,
|
||||||
ID: reqID,
|
|
||||||
Value: battLevel,
|
Value: battLevel,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
case types.ReqTypeCancelBattLevel:
|
||||||
|
// Stop battery level notifications
|
||||||
|
battLevelDone <- struct{}{}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{})
|
||||||
case types.ReqTypeMotion:
|
case types.ReqTypeMotion:
|
||||||
// Get battery level from watch
|
// Get battery level from watch
|
||||||
motionVals, err := dev.Motion()
|
motionVals, err := dev.Motion()
|
||||||
@ -213,28 +194,27 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
connErr(conn, err, "Error getting heart rate channel")
|
connErr(conn, err, "Error getting heart rate channel")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
reqID := uuid.New().String()
|
|
||||||
go func() {
|
go func() {
|
||||||
done.Create(reqID)
|
|
||||||
// For every motion event
|
// For every motion event
|
||||||
for motionVals := range motionValCh {
|
for motionVals := range motionValCh {
|
||||||
select {
|
select {
|
||||||
case <-done[reqID]:
|
case <-motionDone:
|
||||||
// Stop notifications if done signal received
|
// Stop notifications if done signal received
|
||||||
cancel()
|
cancel()
|
||||||
done.Remove(reqID)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// Encode response to connection if no done signal received
|
// Encode response to connection if no done signal received
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
Type: types.ResTypeWatchMotion,
|
Type: types.ResTypeWatchMotion,
|
||||||
ID: reqID,
|
|
||||||
Value: motionVals,
|
Value: motionVals,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
case types.ReqTypeCancelMotion:
|
||||||
|
// Stop motion notifications
|
||||||
|
motionDone <- struct{}{}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{})
|
||||||
case types.ReqTypeStepCount:
|
case types.ReqTypeStepCount:
|
||||||
// Get battery level from watch
|
// Get battery level from watch
|
||||||
stepCount, err := dev.StepCount()
|
stepCount, err := dev.StepCount()
|
||||||
@ -253,27 +233,27 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
connErr(conn, err, "Error getting heart rate channel")
|
connErr(conn, err, "Error getting heart rate channel")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
reqID := uuid.New().String()
|
|
||||||
go func() {
|
go func() {
|
||||||
done.Create(reqID)
|
|
||||||
// For every step count value
|
// For every step count value
|
||||||
for stepCount := range stepCountCh {
|
for stepCount := range stepCountCh {
|
||||||
select {
|
select {
|
||||||
case <-done[reqID]:
|
case <-stepCountDone:
|
||||||
// Stop notifications if done signal received
|
// Stop notifications if done signal received
|
||||||
cancel()
|
cancel()
|
||||||
done.Remove(reqID)
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// Encode response to connection if no done signal received
|
// Encode response to connection if no done signal received
|
||||||
json.NewEncoder(conn).Encode(types.Response{
|
json.NewEncoder(conn).Encode(types.Response{
|
||||||
Type: types.ResTypeWatchStepCount,
|
Type: types.ResTypeWatchStepCount,
|
||||||
ID: reqID,
|
|
||||||
Value: stepCount,
|
Value: stepCount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
case types.ReqTypeCancelStepCount:
|
||||||
|
// Stop step count notifications
|
||||||
|
stepCountDone <- struct{}{}
|
||||||
|
json.NewEncoder(conn).Encode(types.Response{})
|
||||||
case types.ReqTypeFwVersion:
|
case types.ReqTypeFwVersion:
|
||||||
// Get firmware version from watch
|
// Get firmware version from watch
|
||||||
version, err := dev.Version()
|
version, err := dev.Version()
|
||||||
@ -417,7 +397,6 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
Value: event,
|
Value: event,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
firmwareUpdating = false
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Set firmwareUpdating
|
// Set firmwareUpdating
|
||||||
@ -430,18 +409,6 @@ func handleConnection(conn net.Conn, dev *infinitime.Device) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
firmwareUpdating = false
|
firmwareUpdating = false
|
||||||
case types.ReqTypeCancel:
|
|
||||||
if req.Data == nil {
|
|
||||||
connErr(conn, nil, "No data provided. Cancel request requires request ID string as data.")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
reqID, ok := req.Data.(string)
|
|
||||||
if !ok {
|
|
||||||
connErr(conn, nil, "Invalid data. Cancel request required request ID string as data.")
|
|
||||||
}
|
|
||||||
// Stop notifications
|
|
||||||
done.Done(reqID)
|
|
||||||
json.NewEncoder(conn).Encode(types.Response{Type: types.ResTypeCancel})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user