Skip to content

Commit 1f2446e

Browse files
Uploaded files
First Release
1 parent 9aead25 commit 1f2446e

10 files changed

Lines changed: 1455 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## Changelog for lordofscripts/govee
2+
3+
---
4+
### 1.0
5+
##### Released on _Wed 1.Nov.2023_ by _lordofscripts_
6+
### First Release!
7+
* Supports queries (-list and -state) and state changes (-on and -off)
8+
* While it seems like an overkill, it uses a _Command Pattern_ to process commands
9+
* Command-line interface application written in GO

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Lord of Scripts(tm)) Govee CLI
2+
3+
This is a handy command-line interface application to list all Govee smart
4+
devices in your home network, as well as control them (On/Off).
5+
6+
The more verbose way requires several **CLI** parameters such as device MAC & Model
7+
in order to control them. Alternatively, you can put all your Govee smart
8+
devices in a configuration file and assign them an easy-to-remember ID (alias).
9+
10+
With this you can use the CLI to perform the actions you desire, or create
11+
desktop shortcuts that invoke the CLI command.
12+
13+
I'm sure you will find this utility quite handy. Feel free to consider a small
14+
donation so that I can continue working on these utilities:
15+
16+
[Buy LostInWriting a coffee!](https://www.buymeacoffee.com/lostinwriting)
17+
18+
## Requirements
19+
20+
- GO v1.20
21+
- github.com/loxhill/go-vee
22+
- Request a Govee API key
23+
24+
## Using Govee API
25+
This small Go app is conveniently named "govee" which is the name of the
26+
executable.
27+
28+
### Configuration File
29+
30+
The configuration file, if present, should be at `~/.config/govee.json` and it
31+
must be in JSON format. It would look like this:
32+
33+
>{
34+
> "version": "0.1",
35+
> "apiKey": "YOUR GOVEE API KEY HERE"
36+
> "devices": [
37+
> {
38+
> "model": "H5083",
39+
> "mac": "DEVICE MAC ADDRESS",
40+
> "alias": "SmartPlug1",
41+
> "location": "Recamara #2"
42+
> }
43+
> ]
44+
>}
45+
46+
The MAC address is a series of 8 hexadecimal pairs which is unique to each
47+
device and looks like this: `AA:BB:CC:DD:EE:FF:00:11`
48+
49+
You will need to request a personal API key to Govee, they are quite quick. But
50+
do remember that it is only for **personal** use, not commercial use! If you
51+
use a configuration file, make sure to put your API key where indicated.
52+
Alternatively, you can put the API key in the `GOVEE_API` environment variable.
53+
54+
### Obtaining a GOVEE API key
55+
56+
The keys are for personal use only, if you keep that in mind and suits your needs,
57+
then all is okay.
58+
59+
- Install the Govee Home app on your mobile. You probably already have since
60+
you have a Govee device. I am using Govee Home app v5.8.30.
61+
- Click on the Cog wheel (`Settings`) on the upper right)
62+
- In the `Settings` page, click on the `Apply for API Key`
63+
- Fill out the requested information (Name, reason, agreement) and send.
64+
65+
You should receive the personal API key within a day.)
66+
67+
### Usage
68+
69+
> govee -list
70+
Uses your WiFi/Internet connection to query your local network and list ALL
71+
Govee smart devices. It will list them giving you their model number (for use
72+
with -model flag), MAC address (-mac flag) and official name.
73+
74+
> govee -mac {MAC_ADDRESS} -model {MODEL_NUMBER} -on
75+
Turns on the device identified by the MAC address and Model number.
76+
77+
> govee -mac {MAC_ADDRESS} -model {MODEL_NUMBER} -off
78+
Turns off the device identified by the MAC address and Model number.
79+
80+
However, it is much easier to assign easy identifications to your device and
81+
even annotate it with their locations within your premises. For that, you need
82+
to use a *configuration file* as described above. Once you do that, you can
83+
easily turn your devices on or off using their alias, which you can remember
84+
without having to look them up. Here are the config-based versions of the
85+
previous two commands:
86+
87+
> govee -id {ALIAS_NAME} -on
88+
Turns on the device identified by the MAC address and Model number. For the
89+
sample configuration above it would become `govee -id SmartPlug1 -on`
90+
91+
> govee -id {ALIAS_NAME} -off
92+
Turns off the device identified by the MAC address and Model number.
93+
94+
-----
95+
https://allmylinks.com/lordofscripts

cmd/main.go

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/* -----------------------------------------------------------------
2+
* https://allmylinks.com/lordofscripts
3+
* Copyright (C)2023 Lord of Scripts(tm)
4+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5+
* Utility program that uses GOVEE API to control (On/Off/List/State)
6+
* GOVEE smart devices in your network.
7+
*-----------------------------------------------------------------*/
8+
package main
9+
// https://govee-public.s3.amazonaws.com/developer-docs/GoveeDeveloperAPIReference.pdf
10+
11+
import (
12+
"flag"
13+
"fmt"
14+
"os"
15+
"path"
16+
"strings"
17+
18+
"lordofscripts/govee"
19+
"lordofscripts/govee/util"
20+
veex "github.com/loxhill/go-vee"
21+
)
22+
23+
const (
24+
HOME_ENV = "HOME" // environment var. holding user home directory
25+
MY_CONFIG = ".config/govee.json" // config file relative to HOME_ENV
26+
API_KEY = ""
27+
)
28+
29+
/* ----------------------------------------------------------------
30+
* T y p e s
31+
*-----------------------------------------------------------------*/
32+
type ICommand interface {
33+
execute() error
34+
}
35+
36+
type GoveeCommand struct {
37+
Client *veex.Client
38+
Address string
39+
Model string
40+
}
41+
42+
/* ----------------------------------------------------------------
43+
* T y p e s
44+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
45+
* OnCommand::GoveeCommand
46+
*-----------------------------------------------------------------*/
47+
type OnCommand struct {
48+
GoveeCommand
49+
}
50+
51+
func newCmdOn(clientPtr *veex.Client, address, model string) *OnCommand {
52+
o := &OnCommand{
53+
GoveeCommand: GoveeCommand{
54+
Client: clientPtr,
55+
Address: address,
56+
Model: model,
57+
},
58+
}
59+
return o
60+
}
61+
62+
// implements ICommand for OnCommand
63+
func (c *OnCommand) execute() error {
64+
controlRequest, _ := c.Client.Device(c.Address, c.Model).TurnOn()
65+
_, err := c.Client.Run(controlRequest)
66+
return err
67+
}
68+
69+
/* ----------------------------------------------------------------
70+
* T y p e s
71+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
72+
* OffCommand::GoveeCommand
73+
*-----------------------------------------------------------------*/
74+
type OffCommand struct {
75+
GoveeCommand
76+
}
77+
78+
func newCmdOff(clientPtr *veex.Client, address, model string) *OffCommand {
79+
o := &OffCommand{
80+
GoveeCommand: GoveeCommand{
81+
Client: clientPtr,
82+
Address: address,
83+
Model: model,
84+
},
85+
}
86+
return o
87+
}
88+
89+
// implements ICommand for OffCommand
90+
func (c *OffCommand) execute() error {
91+
controlRequest, _ := c.Client.Device(c.Address, c.Model).TurnOff()
92+
_, err := c.Client.Run(controlRequest)
93+
return err
94+
}
95+
96+
/* ----------------------------------------------------------------
97+
* T y p e s
98+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
99+
* StateCommand::GoveeCommand
100+
*-----------------------------------------------------------------*/
101+
102+
// the State commands obtains the online and powered state of a device
103+
// along its h/w address and model-
104+
type StateCommand struct {
105+
GoveeCommand
106+
}
107+
108+
func newCmdState(clientPtr *veex.Client, address, model string) *StateCommand {
109+
o := &StateCommand{
110+
GoveeCommand: GoveeCommand{
111+
Client: clientPtr,
112+
Address: address,
113+
Model: model,
114+
},
115+
}
116+
return o
117+
}
118+
119+
// implements ICommand for StateCommand
120+
func (c *StateCommand) execute() error {
121+
stateRequest := c.Client.Device(c.Address, c.Model).State()
122+
// {Code:200 Message:Success Data:{Device:D7:B6:60:74:F4:02:D5:A2 Model:H5083 Properties:[map[online:true] map[powerState:off]] Devices:[]}}
123+
rsp, err := c.Client.Run(stateRequest) // govee.GoveeResponse
124+
//fmt.Printf("%+v\n", rsp)
125+
if rsp.Code != 200 {
126+
err = fmt.Errorf("State request failed: %q", rsp.Message)
127+
} else {
128+
fmt.Println("\tMAC :", rsp.Data.Device)
129+
fmt.Println("\tModel :", rsp.Data.Model)
130+
fmt.Println("\tOnline :", rsp.Data.Properties[0]["online"])
131+
fmt.Println("\tPowered:", rsp.Data.Properties[1]["powerState"])
132+
}
133+
return err
134+
}
135+
/* ----------------------------------------------------------------
136+
* T y p e s
137+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
138+
* ListCommand::GoveeCommand
139+
*-----------------------------------------------------------------*/
140+
type ListCommand struct {
141+
GoveeCommand
142+
}
143+
144+
func newCmdList(clientPtr *veex.Client) *ListCommand {
145+
o := &ListCommand{
146+
GoveeCommand: GoveeCommand{
147+
Client: clientPtr,
148+
Address: "",
149+
Model: "",
150+
},
151+
}
152+
return o
153+
}
154+
155+
// implements ICommand for ListCommand
156+
func (c *ListCommand) execute() error {
157+
listRequest := c.Client.ListDevices()
158+
if response, err := c.Client.Run(listRequest); err != nil {
159+
return err
160+
} else {
161+
devices := response.Devices()
162+
for i,d := range devices {
163+
fmt.Printf("#%d\n\tDevice: %s\n\tModel : %s\n\tType : %s\n", i+1, d.Device, d.Model, d.DeviceName)
164+
}
165+
}
166+
return nil
167+
}
168+
169+
/* ----------------------------------------------------------------
170+
* F u n c t i o n s
171+
*-----------------------------------------------------------------*/
172+
173+
func getHelp() {
174+
fmt.Printf("Govee v%s by Lord of Scripts\n", govee.CURRENT_VERSION)
175+
fmt.Println("Usage:")
176+
fmt.Println(" govee -list")
177+
fmt.Println(" govee -mac {MAC_ADDRESS} -model {MODEL_NUMBER} -on")
178+
fmt.Println(" govee -mac {MAC_ADDRESS} -model {MODEL_NUMBER} -off")
179+
fmt.Println(" govee -mac {MAC_ADDRESS} -model {MODEL_NUMBER} -state")
180+
// these two need a config file with entries
181+
fmt.Println(" govee -id {ALIAS} -on")
182+
fmt.Println(" govee -id {ALIAS} -off")
183+
fmt.Println(" govee -id {ALIAS} -state")
184+
flag.PrintDefaults()
185+
fmt.Println("\t*** ***")
186+
}
187+
188+
/* ----------------------------------------------------------------
189+
* M A I N
190+
*-----------------------------------------------------------------*/
191+
func main() {
192+
fmt.Printf("\t\t../ GoveeLux v%s (c)2023 Lord of Scripts \\..\n", govee.CURRENT_VERSION)
193+
fmt.Println("\t\t https://allmylinks.com/lordofscripts")
194+
195+
var optHelp, optList, optOn, optOff, optState bool
196+
var inDevice, inModel, inAlias string
197+
flag.BoolVar(&optHelp, "help", false, "This help")
198+
flag.BoolVar(&optList, "list", false, "List devices")
199+
flag.BoolVar(&optOn, "on", false, "Turn ON")
200+
flag.BoolVar(&optOff, "off", false, "Turn OFF")
201+
flag.BoolVar(&optState, "state", false, "Request online/power state of device")
202+
flag.StringVar(&inDevice, "mac", "", "Device MAC")
203+
flag.StringVar(&inModel, "model", "", "Device Model")
204+
flag.StringVar(&inAlias, "id", "", "Device alias (derive Model & MAC from this)")
205+
flag.Parse()
206+
207+
// any command given or at least help?
208+
if optHelp || !(optList || optOn || optOff || optState) {
209+
getHelp()
210+
if optHelp {
211+
os.Exit(0)
212+
}
213+
os.Exit(1)
214+
}
215+
// state && on & off are mutually exclusive
216+
if !util.One(optOn, optOff, optState, optList) {
217+
getHelp()
218+
fmt.Println("Decide either -on OR -off OR -state OR -list")
219+
os.Exit(1)
220+
}
221+
222+
// Config
223+
cfgFilename := path.Join(os.Getenv(HOME_ENV), MY_CONFIG)
224+
225+
if len(inAlias) != 0 {
226+
cfg := govee.Read(cfgFilename)
227+
candidates := cfg.Devices.Where(govee.ALIAS, inAlias)
228+
cnt := candidates.Count()
229+
if cnt == 0 {
230+
fmt.Printf("Could not find alias %q in repository\n", inAlias)
231+
os.Exit(2)
232+
}
233+
234+
if cnt > 1 {
235+
fmt.Printf("Found %d entries. Alias not unique, please correct config\n", cnt)
236+
os.Exit(2)
237+
}
238+
239+
fmt.Println("\tFound ", candidates[0], "\n\tAt :", candidates[0].Location)
240+
inDevice = candidates[0].MacAddress
241+
inModel = candidates[0].Model
242+
}
243+
244+
// with STATE, ON & OFF commands DEVICE & MODEL are required
245+
if (optOn || optOff || optState) && ((len(inDevice) == 0) && (len(inModel) == 0)) {
246+
getHelp()
247+
fmt.Println("-dev MAC and -model MODEL options are required!")
248+
os.Exit(1)
249+
} else {
250+
inDevice = strings.ToUpper(inDevice)
251+
inModel = strings.ToUpper(inModel)
252+
}
253+
254+
key := govee.GetAPI(cfgFilename)
255+
if len(key) == 0 {
256+
fmt.Println("You forgot to set your GOVEE API Key!")
257+
os.Exit(2)
258+
}
259+
260+
// TurnOff, TurnOn, SetBrightness, SetColor, SetColorTem
261+
client := veex.New(key)
262+
var command ICommand
263+
switch {
264+
case optList:
265+
command = newCmdList(client)
266+
267+
case optOn:
268+
command = newCmdOn(client, inDevice, inModel)
269+
270+
case optOff:
271+
command = newCmdOff(client, inDevice, inModel)
272+
273+
case optState:
274+
command = newCmdState(client, inDevice, inModel)
275+
}
276+
277+
if err := command.execute(); err != nil {
278+
//fmt.Printf("Error type: %T\nMessage: %s\n%v\n", err, err, err)
279+
fmt.Printf("\tError : %v\n", err)
280+
}
281+
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module lordofscripts/govee
2+
3+
go 1.21.0
4+
5+
require github.com/loxhill/go-vee v0.0.0-20230825161508-7c8c34d2ee28

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/loxhill/go-vee v0.0.0-20230825161508-7c8c34d2ee28 h1:oSED7BzozltkdBVaMIaQkK5d7/V9avQx/XK3HfV8Aw0=
2+
github.com/loxhill/go-vee v0.0.0-20230825161508-7c8c34d2ee28/go.mod h1:gLqZGKn+W6944XJfI9VastC/QRVTVs+Htd9R8aiE1dQ=

0 commit comments

Comments
 (0)