diff --git a/README.md b/README.md index fc47991d..9fcf2556 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,10 @@ An optional `--fqbn` flag can be passed to specify the fqbn of the device, other `$ arduino-cloud-cli device create-generic --name --fqbn ` +The list of supported FQBN can be retrieved with: + +`$ arduino-cloud-cli device list-fqbn` + ## Device commands 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: diff --git a/cli/device/device.go b/cli/device/device.go index 080a8f5a..86e313a5 100644 --- a/cli/device/device.go +++ b/cli/device/device.go @@ -37,6 +37,7 @@ func NewCommand() *cobra.Command { deviceCommand.AddCommand(initListFrequencyPlansCommand()) deviceCommand.AddCommand(initCreateLoraCommand()) deviceCommand.AddCommand(initCreateGenericCommand()) + deviceCommand.AddCommand(initListFQBNCommand()) return deviceCommand } diff --git a/cli/device/listfqbn.go b/cli/device/listfqbn.go new file mode 100644 index 00000000..10622099 --- /dev/null +++ b/cli/device/listfqbn.go @@ -0,0 +1,74 @@ +// This file is part of arduino-cloud-cli. +// +// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package device + +import ( + "os" + + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-cli/table" + "github.com/arduino/arduino-cloud-cli/command/device" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func initListFQBNCommand() *cobra.Command { + listFQBNCommand := &cobra.Command{ + Use: "list-fqbn", + Short: "List supported FQBN", + Long: "List all the FQBN supported by Arduino IoT Cloud", + Run: runListFQBNCommand, + } + return listFQBNCommand +} + +func runListFQBNCommand(cmd *cobra.Command, args []string) { + logrus.Info("Listing supported FQBN") + + fqbn, err := device.ListFQBN() + if err != nil { + feedback.Errorf("Error during device list-fqbn: %v", err) + os.Exit(errorcodes.ErrGeneric) + } + + feedback.PrintResult(listFQBNResult{fqbn}) +} + +type listFQBNResult struct { + fqbn []device.FQBNInfo +} + +func (r listFQBNResult) Data() interface{} { + return r.fqbn +} + +func (r listFQBNResult) String() string { + if len(r.fqbn) == 0 { + return "No FQBN." + } + t := table.New() + t.SetHeader("Name", "FQBN") + for _, f := range r.fqbn { + t.AddRow( + f.Name, + f.Value, + ) + } + return t.Render() +} diff --git a/command/device/listfqbn.go b/command/device/listfqbn.go new file mode 100644 index 00000000..922c5f0d --- /dev/null +++ b/command/device/listfqbn.go @@ -0,0 +1,102 @@ +// This file is part of arduino-cloud-cli. +// +// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package device + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" +) + +const ( + arduinoPackage = "arduino" + esp32Package = "esp32" + esp8266Package = "esp8266" +) + +var ( + // this is temporary... it will be removed when + // https://github.com/arduino/arduino-cloud-cli/pull/74/files#diff-d891696d5c17ea0eecc6b1c23802cbaf553379e701c5e0e1ff23ee0d26d2877cR27-R39 + // will be merged + compatibleArduinoFQBN = []string{ + "arduino:samd:nano_33_iot", + "arduino:samd:mkrwifi1010", + "arduino:mbed_nano:nanorp2040connect", + "arduino:mbed_portenta:envie_m7", + "arduino:samd:mkr1000", + "arduino:samd:mkrgsm1400", + "arduino:samd:mkrnb1500", + "arduino:samd:mkrwan1310", + "arduino:samd:mkrwan1300", + } +) + +// FQBNInfo contains the details of a FQBN. +type FQBNInfo struct { + Value string `json:"fqbn"` + Name string `json:"name"` + Package string `json:"package"` +} + +// ListFQBN command returns a list of the supported FQBN. +func ListFQBN() ([]FQBNInfo, error) { + h := &http.Client{Timeout: time.Second * 5} + resp, err := h.Get("https://builder.arduino.cc/v3/boards/") + if err != nil { + return nil, fmt.Errorf("cannot retrieve boards from builder.arduino.cc: %w", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("reading boards from builder.arduino.cc: cannot read response's body: %w", err) + } + + var fqbnList struct { + Items []FQBNInfo `json:"items"` + } + if err = json.Unmarshal(body, &fqbnList); err != nil { + return nil, fmt.Errorf("cannot parse boards retrieved from builder.arduino.cc: %w", err) + } + + return filterFQBN(fqbnList.Items), nil +} + +// filterFQBN takes a list of fqbn and returns only the +// ones supported by iot cloud. +func filterFQBN(ls []FQBNInfo) []FQBNInfo { + filtered := make([]FQBNInfo, 0, len(ls)) + for _, fqbn := range ls { + switch fqbn.Package { + + case esp32Package, esp8266Package: + filtered = append(filtered, fqbn) + + case arduinoPackage: + for _, b := range compatibleArduinoFQBN { + if fqbn.Value == b { + filtered = append(filtered, fqbn) + break + } + } + } + } + return filtered +} diff --git a/command/device/listfqbn_test.go b/command/device/listfqbn_test.go new file mode 100644 index 00000000..af2e14a6 --- /dev/null +++ b/command/device/listfqbn_test.go @@ -0,0 +1,48 @@ +// This file is part of arduino-cloud-cli. +// +// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package device + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestFilterFQBN(t *testing.T) { + var ( + wrong = []FQBNInfo{ + {Name: "Arduino Uno", Value: "arduino:avr:uno", Package: "arduino"}, + {Name: "Arduino Industrial 101", Value: "arduino:avr:chiwawa", Package: "arduino"}, + {Name: "SmartEverything Lion (Native USB Port)", Value: "Arrow:samd:SmartEverything_Lion_native", Package: "Arrow"}, + {Name: "Arduino/Genuino 101", Value: "Intel:arc32:arduino_101", Package: "Intel"}, + {Name: "Atmel atmega328pb Xplained mini", Value: "atmel-avr-xminis:avr:atmega328pb_xplained_mini", Package: "atmel-avr-xminis"}, + } + good = []FQBNInfo{ + {Name: "Arduino Nano RP2040 Connect", Value: "arduino:mbed_nano:nanorp2040connect", Package: "arduino"}, + {Name: "Arduino MKR WiFi 1010", Value: "arduino:samd:mkrwifi1010", Package: "arduino"}, + {Name: "ESP32 Dev Module", Value: "esp32:esp32:esp32", Package: "esp32"}, + {Name: "4D Systems gen4 IoD Range", Value: "esp8266:esp8266:gen4iod", Package: "esp8266"}, + {Name: "BPI-BIT", Value: "esp32:esp32:bpi-bit", Package: "esp32"}, + } + ) + all := append(wrong, good...) + filtered := filterFQBN(all) + if !cmp.Equal(good, filtered) { + t.Errorf("Wrong filter, diff:\n%s", cmp.Diff(good, filtered)) + } +}