Skip to content

Commit 08befc7

Browse files
Paolo Calaopolldo
Paolo Calao
authored andcommitted
Add device list-fqbn command (#78)
Add a command to retrieve the list of FQBN supported by arduino iot cloud
1 parent e777598 commit 08befc7

File tree

5 files changed

+229
-0
lines changed

5 files changed

+229
-0
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ An optional `--fqbn` flag can be passed to specify the fqbn of the device, other
8181

8282
`$ arduino-cloud-cli device create-generic --name <deviceName> --fqbn <fqbn>`
8383

84+
The list of supported FQBN can be retrieved with:
85+
86+
`$ arduino-cloud-cli device list-fqbn`
87+
8488
## Device commands
8589

8690
Devices can be deleted using the device delete command. This command accepts two mutually exclusive flags: `--id` and `--tags`. Only one of them must be passed. When the `--id` is passed, the device having such ID gets deleted:

cli/device/device.go

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func NewCommand() *cobra.Command {
3737
deviceCommand.AddCommand(initListFrequencyPlansCommand())
3838
deviceCommand.AddCommand(initCreateLoraCommand())
3939
deviceCommand.AddCommand(initCreateGenericCommand())
40+
deviceCommand.AddCommand(initListFQBNCommand())
4041

4142
return deviceCommand
4243
}

cli/device/listfqbn.go

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package device
19+
20+
import (
21+
"os"
22+
23+
"github.com/arduino/arduino-cli/cli/errorcodes"
24+
"github.com/arduino/arduino-cli/cli/feedback"
25+
"github.com/arduino/arduino-cli/table"
26+
"github.com/arduino/arduino-cloud-cli/command/device"
27+
"github.com/sirupsen/logrus"
28+
"github.com/spf13/cobra"
29+
)
30+
31+
func initListFQBNCommand() *cobra.Command {
32+
listFQBNCommand := &cobra.Command{
33+
Use: "list-fqbn",
34+
Short: "List supported FQBN",
35+
Long: "List all the FQBN supported by Arduino IoT Cloud",
36+
Run: runListFQBNCommand,
37+
}
38+
return listFQBNCommand
39+
}
40+
41+
func runListFQBNCommand(cmd *cobra.Command, args []string) {
42+
logrus.Info("Listing supported FQBN")
43+
44+
fqbn, err := device.ListFQBN()
45+
if err != nil {
46+
feedback.Errorf("Error during device list-fqbn: %v", err)
47+
os.Exit(errorcodes.ErrGeneric)
48+
}
49+
50+
feedback.PrintResult(listFQBNResult{fqbn})
51+
}
52+
53+
type listFQBNResult struct {
54+
fqbn []device.FQBNInfo
55+
}
56+
57+
func (r listFQBNResult) Data() interface{} {
58+
return r.fqbn
59+
}
60+
61+
func (r listFQBNResult) String() string {
62+
if len(r.fqbn) == 0 {
63+
return "No FQBN."
64+
}
65+
t := table.New()
66+
t.SetHeader("Name", "FQBN")
67+
for _, f := range r.fqbn {
68+
t.AddRow(
69+
f.Name,
70+
f.Value,
71+
)
72+
}
73+
return t.Render()
74+
}

command/device/listfqbn.go

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package device
19+
20+
import (
21+
"encoding/json"
22+
"fmt"
23+
"io/ioutil"
24+
"net/http"
25+
"time"
26+
)
27+
28+
const (
29+
arduinoPackage = "arduino"
30+
esp32Package = "esp32"
31+
esp8266Package = "esp8266"
32+
)
33+
34+
var (
35+
// this is temporary... it will be removed when
36+
// https://github.com/arduino/arduino-cloud-cli/pull/74/files#diff-d891696d5c17ea0eecc6b1c23802cbaf553379e701c5e0e1ff23ee0d26d2877cR27-R39
37+
// will be merged
38+
compatibleArduinoFQBN = []string{
39+
"arduino:samd:nano_33_iot",
40+
"arduino:samd:mkrwifi1010",
41+
"arduino:mbed_nano:nanorp2040connect",
42+
"arduino:mbed_portenta:envie_m7",
43+
"arduino:samd:mkr1000",
44+
"arduino:samd:mkrgsm1400",
45+
"arduino:samd:mkrnb1500",
46+
"arduino:samd:mkrwan1310",
47+
"arduino:samd:mkrwan1300",
48+
}
49+
)
50+
51+
// FQBNInfo contains the details of a FQBN.
52+
type FQBNInfo struct {
53+
Value string `json:"fqbn"`
54+
Name string `json:"name"`
55+
Package string `json:"package"`
56+
}
57+
58+
// ListFQBN command returns a list of the supported FQBN.
59+
func ListFQBN() ([]FQBNInfo, error) {
60+
h := &http.Client{Timeout: time.Second * 5}
61+
resp, err := h.Get("https://builder.arduino.cc/v3/boards/")
62+
if err != nil {
63+
return nil, fmt.Errorf("cannot retrieve boards from builder.arduino.cc: %w", err)
64+
}
65+
defer resp.Body.Close()
66+
67+
body, err := ioutil.ReadAll(resp.Body)
68+
if err != nil {
69+
return nil, fmt.Errorf("reading boards from builder.arduino.cc: cannot read response's body: %w", err)
70+
}
71+
72+
var fqbnList struct {
73+
Items []FQBNInfo `json:"items"`
74+
}
75+
if err = json.Unmarshal(body, &fqbnList); err != nil {
76+
return nil, fmt.Errorf("cannot parse boards retrieved from builder.arduino.cc: %w", err)
77+
}
78+
79+
return filterFQBN(fqbnList.Items), nil
80+
}
81+
82+
// filterFQBN takes a list of fqbn and returns only the
83+
// ones supported by iot cloud.
84+
func filterFQBN(ls []FQBNInfo) []FQBNInfo {
85+
filtered := make([]FQBNInfo, 0, len(ls))
86+
for _, fqbn := range ls {
87+
switch fqbn.Package {
88+
89+
case esp32Package, esp8266Package:
90+
filtered = append(filtered, fqbn)
91+
92+
case arduinoPackage:
93+
for _, b := range compatibleArduinoFQBN {
94+
if fqbn.Value == b {
95+
filtered = append(filtered, fqbn)
96+
break
97+
}
98+
}
99+
}
100+
}
101+
return filtered
102+
}

command/device/listfqbn_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package device
19+
20+
import (
21+
"testing"
22+
23+
"github.com/google/go-cmp/cmp"
24+
)
25+
26+
func TestFilterFQBN(t *testing.T) {
27+
var (
28+
wrong = []FQBNInfo{
29+
{Name: "Arduino Uno", Value: "arduino:avr:uno", Package: "arduino"},
30+
{Name: "Arduino Industrial 101", Value: "arduino:avr:chiwawa", Package: "arduino"},
31+
{Name: "SmartEverything Lion (Native USB Port)", Value: "Arrow:samd:SmartEverything_Lion_native", Package: "Arrow"},
32+
{Name: "Arduino/Genuino 101", Value: "Intel:arc32:arduino_101", Package: "Intel"},
33+
{Name: "Atmel atmega328pb Xplained mini", Value: "atmel-avr-xminis:avr:atmega328pb_xplained_mini", Package: "atmel-avr-xminis"},
34+
}
35+
good = []FQBNInfo{
36+
{Name: "Arduino Nano RP2040 Connect", Value: "arduino:mbed_nano:nanorp2040connect", Package: "arduino"},
37+
{Name: "Arduino MKR WiFi 1010", Value: "arduino:samd:mkrwifi1010", Package: "arduino"},
38+
{Name: "ESP32 Dev Module", Value: "esp32:esp32:esp32", Package: "esp32"},
39+
{Name: "4D Systems gen4 IoD Range", Value: "esp8266:esp8266:gen4iod", Package: "esp8266"},
40+
{Name: "BPI-BIT", Value: "esp32:esp32:bpi-bit", Package: "esp32"},
41+
}
42+
)
43+
all := append(wrong, good...)
44+
filtered := filterFQBN(all)
45+
if !cmp.Equal(good, filtered) {
46+
t.Errorf("Wrong filter, diff:\n%s", cmp.Diff(good, filtered))
47+
}
48+
}

0 commit comments

Comments
 (0)