Skip to content

Commit

Permalink
Add panel command
Browse files Browse the repository at this point in the history
  • Loading branch information
tessro committed May 5, 2021
1 parent 8aa7dc3 commit 71d725b
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 10 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,10 @@ picoleaf brightness <temperature> # Set Nanoleaf to the provided brig
# Effects
picoleaf effect list # List installed effects
picoleaf effect select <name> # Activate the named effect

# Panel properties
picoleaf panel info # Print all panel information
picoleaf panel model # Print Nanoleaf model
picoleaf panel name # Print Nanoleaf name
picoleaf panel version # Print Nanoleaf and rhythm module versions
```
96 changes: 86 additions & 10 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,73 @@ func (c Client) Endpoint(path string) string {
return fmt.Sprintf("http://%s/api/v1/%s/%s", c.Host, c.Token, path)
}

// Effects represents the Nanoleaf panel effects state.
type Effects struct {
Selected string `json:"select"`
List []string `json:"effectsList"`
}

// Rhythm represents the Nanoleaf rhythm state.
type Rhythm struct {
Connected bool `json:"rhythmConnected"`
Active bool `json:"rhythmActive"`
ID int `json:"rhythmId"`
HardwareVersion string `json:"hardwareVersion"`
FirmwareVersion string `json:"firmwareVersion"`
AuxAvailable bool `json:"auxAvailable"`
Mode int `json:"rhythmMode"`
Position struct {
X float64 `json:"x"`
Y float64 `json:"y"`
O float64 `json:"o"`
} `json:"rhythmPos"`
}

// PanelLayout represents the Nanoleaf panel layout.
type PanelLayout struct {
Layout struct {
NumPanels int `json:"numPanels"`
SideLength int `json:"sideLength"`
PositionData []struct {
PanelID int `json:"panelId"`
X int `json:"x"`
Y int `json:"y"`
O int `json:"o"`
ShapeType int `json:"shapeType"`
} `json:"positionData"`
} `json:"layout"`
GlobalOrientation struct {
Value int `json:"value"`
Max int `json:"max"`
Min int `json:"min"`
} `json:"globalOrientation"`
}

// PanelInfo represents the Nanoleaf panel info response.
type PanelInfo struct {
Name string `json:"name"`
SerialNo string `json:"serialNo"`
Manufacturer string `json:"manufacturer"`
FirmwareVersion string `json:"firmwareVersion"`
Model string `json:"model"`
State State `json:"state"`
Effects Effects `json:"effects"`
PanelLayout PanelLayout `json:"panelLayout"`
Rhythm Rhythm `json:"rhythm"`
}

// GetPanelInfo returns the Nanoleaf panel info.
func (c Client) GetPanelInfo() (*PanelInfo, error) {
body, err := c.Get("")
if err != nil {
return nil, err
}

var panelInfo PanelInfo
err = json.Unmarshal([]byte(body), &panelInfo)
return &panelInfo, err
}

// ListEffects returns an array of effect names.
func (c Client) ListEffects() ([]string, error) {
body, err := c.Get("effects/effectsList")
Expand Down Expand Up @@ -143,7 +210,7 @@ func (c Client) SelectEffect(name string) error {
// SetBrightness sets the Nanoleaf's brightness.
func (c Client) SetBrightness(brightness int) error {
state := State{
Brightness: &BrightnessProperty{brightness, 0},
Brightness: &BrightnessProperty{Value: brightness},
}

bytes, err := json.Marshal(state)
Expand All @@ -158,7 +225,7 @@ func (c Client) SetBrightness(brightness int) error {
// SetColorTemperature sets the Nanoleaf's color temperature.
func (c Client) SetColorTemperature(temperature int) error {
state := State{
ColorTemperature: &ColorTemperatureProperty{temperature},
ColorTemperature: &ColorTemperatureProperty{Value: temperature},
}

bytes, err := json.Marshal(state)
Expand All @@ -173,9 +240,9 @@ func (c Client) SetColorTemperature(temperature int) error {
// SetHSL sets the Nanoleaf's hue, saturation, and lightness (brightness).
func (c Client) SetHSL(hue int, sat int, lightness int) error {
state := State{
Brightness: &BrightnessProperty{lightness, 0},
Hue: &HueProperty{hue},
Saturation: &SaturationProperty{sat},
Brightness: &BrightnessProperty{Value: lightness},
Hue: &HueProperty{Value: hue},
Saturation: &SaturationProperty{Value: sat},
}

bytes, err := json.Marshal(state)
Expand All @@ -195,18 +262,24 @@ func (c Client) SetRGB(red int, green int, blue int) error {

// BrightnessProperty represents the brightness of the Nanoleaf.
type BrightnessProperty struct {
Value int `json:"value"`
Duration int `json:"duration,omitempty"`
Min *int `json:"min,omitempty"`
Max *int `json:"max,omitempty"`
Value int `json:"value"`
Duration int `json:"duration,omitempty"`
}

// ColorTemperatureProperty represents the color temperature of the Nanoleaf.
type ColorTemperatureProperty struct {
Value int `json:"value"`
Min *int `json:"min,omitempty"`
Max *int `json:"max,omitempty"`
Value int `json:"value"`
}

// HueProperty represents the hue of the Nanoleaf.
type HueProperty struct {
Value int `json:"value"`
Min *int `json:"min,omitempty"`
Max *int `json:"max,omitempty"`
Value int `json:"value"`
}

// OnProperty represents the power state of the Nanoleaf.
Expand All @@ -216,7 +289,9 @@ type OnProperty struct {

// SaturationProperty represents the saturation of the Nanoleaf.
type SaturationProperty struct {
Value int `json:"value"`
Min *int `json:"min,omitempty"`
Max *int `json:"max,omitempty"`
Value int `json:"value"`
}

// State represents a Nanoleaf state.
Expand All @@ -226,6 +301,7 @@ type State struct {
ColorTemperature *ColorTemperatureProperty `json:"ct,omitempty"`
Hue *HueProperty `json:"hue,omitempty"`
Saturation *SaturationProperty `json:"sat,omitempty"`
ColorMode string `json:"colorMode,omitempty"`
}

// effectsSelectRequest represents a JSON PUT body for `effects/select`.
Expand Down
109 changes: 109 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func usage() {
fmt.Println(" off Turn off Nanoleaf")
fmt.Println()
fmt.Println(" effect Control Nanoleaf effects")
fmt.Println(" panel Control Nanoleaf panel")
fmt.Println()
fmt.Println(" hsl Set Nanoleaf to the provided HSL")
fmt.Println(" rgb Set Nanoleaf to the provided RGB")
Expand Down Expand Up @@ -81,6 +82,8 @@ func main() {
fmt.Printf("error: failed to turn on Nanoleaf: %v", err)
os.Exit(1)
}
case "panel":
doPanelCommand(client, flag.Args()[1:])
case "rgb":
doRGBCommand(client, flag.Args()[1:])
case "temp":
Expand Down Expand Up @@ -170,6 +173,112 @@ func doEffectCommand(client Client, args []string) {
}
}

func doPanelCommand(client Client, args []string) {
usage := func() {
fmt.Println("usage: picoleaf panel info")
fmt.Println(" picoleaf panel model")
fmt.Println(" picoleaf panel name")
fmt.Println(" picoleaf panel version")
os.Exit(1)
}

if len(args) != 1 {
usage()
}

panelInfo, err := client.GetPanelInfo()
if err != nil {
fmt.Printf("error: failed to get Nanoleaf state: %v", err)
os.Exit(1)
}

command := args[0]
switch command {
case "info":
fmt.Println("Name:", panelInfo.Name)
fmt.Println()
fmt.Println("Manufacturer:", panelInfo.Manufacturer)
fmt.Println("Model: ", panelInfo.Model)
fmt.Println("Serial No: ", panelInfo.SerialNo)
fmt.Println()
fmt.Println("Firmware Version:", panelInfo.FirmwareVersion)
fmt.Println()
fmt.Println("State:")
fmt.Println(" On: ", panelInfo.State.On.Value)
fmt.Println(" Mode:", panelInfo.State.ColorMode)
fmt.Println()
fmt.Printf(" Hue: %3d° [%d°-%d°]\n", panelInfo.State.Hue.Value, *panelInfo.State.Hue.Min, *panelInfo.State.Hue.Max)
fmt.Printf(" Saturation: %3d [%d-%d]\n", panelInfo.State.Saturation.Value, *panelInfo.State.Saturation.Min, *panelInfo.State.Saturation.Max)
fmt.Printf(" Brightness: %3d [%d-%d]\n", panelInfo.State.Brightness.Value, *panelInfo.State.Brightness.Min, *panelInfo.State.Brightness.Max)
fmt.Println()
fmt.Printf(" Color Temperature: %4dK [%dK-%dK]\n", panelInfo.State.ColorTemperature.Value, *panelInfo.State.ColorTemperature.Min, *panelInfo.State.ColorTemperature.Max)
fmt.Println()
fmt.Println("Effects:")
fmt.Println(" Selected:", panelInfo.Effects.Selected)
fmt.Println(" Available:")
for _, effect := range panelInfo.Effects.List {
fmt.Println(" -", effect)
}
fmt.Println()
fmt.Println("Layout:")
fmt.Printf(" Orientation: %d° [%d°-%d°]\n", panelInfo.PanelLayout.GlobalOrientation.Value, panelInfo.PanelLayout.GlobalOrientation.Min, panelInfo.PanelLayout.GlobalOrientation.Max)
fmt.Println(" Panels: ", panelInfo.PanelLayout.Layout.NumPanels)
fmt.Println(" Side Length:", panelInfo.PanelLayout.Layout.SideLength)
fmt.Println()
fmt.Println(" Panel Positions:")
for _, panel := range panelInfo.PanelLayout.Layout.PositionData {
fmt.Printf(" - %3d: (%d, %d, %d°)\n", panel.PanelID, panel.X, panel.Y, panel.O)
}
fmt.Println()
fmt.Println("Rhythm:")
fmt.Println(" ID: ", panelInfo.Rhythm.ID)
fmt.Printf(" Position: (%.0f, %.0f, %.0f°)\n", panelInfo.Rhythm.Position.X, panelInfo.Rhythm.Position.Y, panelInfo.Rhythm.Position.O)
fmt.Println()
fmt.Println(" Connected: ", panelInfo.Rhythm.Connected)
fmt.Println(" Aux Available:", panelInfo.Rhythm.AuxAvailable)
fmt.Println(" Active: ", panelInfo.Rhythm.Active)
fmt.Println(" Mode: ", panelInfo.Rhythm.Mode)
fmt.Println()
fmt.Println(" Versions:")
fmt.Println(" Hardware:", panelInfo.Rhythm.HardwareVersion)
fmt.Println(" Firmware:", panelInfo.Rhythm.FirmwareVersion)
fmt.Println()
case "layout":
fmt.Printf("Orientation: %d° [%d°-%d°]\n", panelInfo.PanelLayout.GlobalOrientation.Value, panelInfo.PanelLayout.GlobalOrientation.Min, panelInfo.PanelLayout.GlobalOrientation.Max)
fmt.Println("Panels: ", panelInfo.PanelLayout.Layout.NumPanels)
fmt.Println("Side Length:", panelInfo.PanelLayout.Layout.SideLength)
fmt.Println()
fmt.Println("Positions:")
for _, panel := range panelInfo.PanelLayout.Layout.PositionData {
fmt.Printf("- %3d: (%d, %d, %d°)\n", panel.PanelID, panel.X, panel.Y, panel.O)
}
fmt.Println()
case "model":
fmt.Println(panelInfo.Model)
case "name":
fmt.Println(panelInfo.Name)
case "state":
fmt.Println("On: ", panelInfo.State.On.Value)
fmt.Println("Mode:", panelInfo.State.ColorMode)
fmt.Println()
fmt.Printf("Brightness: %3d [%d-%d]\n", panelInfo.State.Brightness.Value, *panelInfo.State.Brightness.Min, *panelInfo.State.Brightness.Max)
fmt.Printf("Hue: %3d [%d-%d]\n", panelInfo.State.Hue.Value, *panelInfo.State.Hue.Min, *panelInfo.State.Hue.Max)
fmt.Printf("Saturation: %3d [%d-%d]\n", panelInfo.State.Saturation.Value, *panelInfo.State.Saturation.Min, *panelInfo.State.Saturation.Max)
fmt.Println()
fmt.Printf("Color Temperature: %4dK [%dK-%dK]\n", panelInfo.State.ColorTemperature.Value, *panelInfo.State.ColorTemperature.Min, *panelInfo.State.ColorTemperature.Max)
fmt.Println()
case "version":
fmt.Println("Panel Firmware:", panelInfo.FirmwareVersion)
fmt.Println()
fmt.Println("Rhythm:")
fmt.Println(" Hardware:", panelInfo.Rhythm.HardwareVersion)
fmt.Println(" Firmware:", panelInfo.Rhythm.FirmwareVersion)
fmt.Println()
default:
usage()
}
}

func doHSLCommand(client Client, args []string) {
if len(args) != 3 {
fmt.Println("usage: picoleaf hsl <hue> <saturation> <lightness>")
Expand Down

0 comments on commit 71d725b

Please sign in to comment.