Skip to content

Commit d19d44a

Browse files
committed
Add ota upload command
1 parent 5deacfc commit d19d44a

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

cli/ota/ota.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package ota
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
func NewCommand() *cobra.Command {
8+
otaCommand := &cobra.Command{
9+
Use: "ota",
10+
Short: "Over The Air.",
11+
Long: "Over The Air firmware update.",
12+
}
13+
14+
otaCommand.AddCommand(initUploadCommand())
15+
16+
return otaCommand
17+
}

cli/ota/upload.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package ota
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/arduino/iot-cloud-cli/command/ota"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var uploadFlags struct {
11+
deviceID string
12+
file string
13+
}
14+
15+
func initUploadCommand() *cobra.Command {
16+
uploadCommand := &cobra.Command{
17+
Use: "upload",
18+
Short: "OTA upload",
19+
Long: "OTA upload on a device of Arduino IoT Cloud",
20+
RunE: runUploadCommand,
21+
}
22+
23+
uploadCommand.Flags().StringVarP(&uploadFlags.deviceID, "device-id", "d", "", "Device ID")
24+
uploadCommand.Flags().StringVarP(&uploadFlags.file, "file", "", "", "OTA file")
25+
26+
uploadCommand.MarkFlagRequired("device-id")
27+
uploadCommand.MarkFlagRequired("file")
28+
return uploadCommand
29+
}
30+
31+
func runUploadCommand(cmd *cobra.Command, args []string) error {
32+
fmt.Printf("Uploading binary %s to device %s\n", uploadFlags.file, uploadFlags.deviceID)
33+
34+
params := &ota.UploadParams{
35+
DeviceID: uploadFlags.deviceID,
36+
File: uploadFlags.file,
37+
}
38+
err := ota.Upload(params)
39+
if err != nil {
40+
return err
41+
}
42+
43+
fmt.Println("Upload successfully started")
44+
return nil
45+
}

cli/root.go

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/arduino/iot-cloud-cli/cli/config"
88
"github.com/arduino/iot-cloud-cli/cli/device"
9+
"github.com/arduino/iot-cloud-cli/cli/ota"
910
"github.com/arduino/iot-cloud-cli/cli/thing"
1011
"github.com/spf13/cobra"
1112
)
@@ -15,6 +16,7 @@ func Execute() {
1516
rootCmd.AddCommand(config.NewCommand())
1617
rootCmd.AddCommand(device.NewCommand())
1718
rootCmd.AddCommand(thing.NewCommand())
19+
rootCmd.AddCommand(ota.NewCommand())
1820

1921
if err := rootCmd.Execute(); err != nil {
2022
fmt.Fprintln(os.Stderr, err)

command/ota/upload.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package ota
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/arduino/iot-cloud-cli/internal/config"
10+
"github.com/arduino/iot-cloud-cli/internal/iot"
11+
)
12+
13+
// UploadParams contains the parameters needed to
14+
// perform an OTA upload.
15+
type UploadParams struct {
16+
DeviceID string
17+
File string
18+
}
19+
20+
// Upload command is used to upload a firmware OTA,
21+
// on a device of Arduino IoT Cloud.
22+
func Upload(params *UploadParams) error {
23+
conf, err := config.Retrieve()
24+
if err != nil {
25+
return err
26+
}
27+
iotClient, err := iot.NewClient(conf.Client, conf.Secret)
28+
if err != nil {
29+
return err
30+
}
31+
32+
dev, err := iotClient.DeviceShow(params.DeviceID)
33+
if err != nil {
34+
return err
35+
}
36+
37+
otaDir, err := ioutil.TempDir("", "")
38+
if err != nil {
39+
return fmt.Errorf("%s: %w", "cannot create temporary folder", err)
40+
}
41+
otaFile := filepath.Join(otaDir, "temp.ota")
42+
defer os.RemoveAll(otaDir)
43+
44+
err = Generate(params.File, otaFile, dev.Fqbn)
45+
if err != nil {
46+
return fmt.Errorf("%s: %w", "cannot generate .ota file", err)
47+
}
48+
49+
file, err := os.Open(otaFile)
50+
if err != nil {
51+
return fmt.Errorf("%s: %w", "cannot open ota file", err)
52+
}
53+
54+
err = iotClient.DeviceOTA(params.DeviceID, file)
55+
if err != nil {
56+
return err
57+
}
58+
59+
return nil
60+
}

internal/iot/client.go

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package iot
33
import (
44
"context"
55
"fmt"
6+
"os"
67

78
"github.com/antihax/optional"
89
iotclient "github.com/arduino/iot-client-go"
@@ -14,6 +15,7 @@ type Client interface {
1415
DeviceDelete(id string) error
1516
DeviceList() ([]iotclient.ArduinoDevicev2, error)
1617
DeviceShow(id string) (*iotclient.ArduinoDevicev2, error)
18+
DeviceOTA(id string, file *os.File) error
1719
CertificateCreate(id, csr string) (*iotclient.ArduinoCompressedv2, error)
1820
ThingCreate(thing *iotclient.Thing, force bool) (string, error)
1921
ThingUpdate(id string, thing *iotclient.Thing, force bool) error
@@ -89,6 +91,20 @@ func (cl *client) DeviceShow(id string) (*iotclient.ArduinoDevicev2, error) {
8991
return &dev, nil
9092
}
9193

94+
// DeviceOTA performs an OTA upload request to Arduino IoT Cloud, passing
95+
// the ID of the device to be updated and the actual file containing the OTA firmware.
96+
func (cl *client) DeviceOTA(id string, file *os.File) error {
97+
opt := &iotclient.DevicesV2OtaUploadOpts{
98+
ExpireInMins: optional.NewInt32(5),
99+
}
100+
_, err := cl.api.DevicesV2OtaApi.DevicesV2OtaUpload(cl.ctx, id, file, opt)
101+
if err != nil {
102+
err = fmt.Errorf("listing devices: %w", errorDetail(err))
103+
return err
104+
}
105+
return nil
106+
}
107+
92108
// CertificateCreate allows to upload a certificate on Arduino IoT Cloud.
93109
// It returns the certificate parameters populated by the cloud.
94110
func (cl *client) CertificateCreate(id, csr string) (*iotclient.ArduinoCompressedv2, error) {

0 commit comments

Comments
 (0)