-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a9c3d9f
Showing
12 changed files
with
675 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
name: Go | ||
on: | ||
push: | ||
tags: ["v*"] | ||
jobs: | ||
build: | ||
strategy: | ||
matrix: | ||
arch: [linux_amd64, linux_arm, windows_amd64, darwin_amd64] | ||
name: Build | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Set up Go 1.15 | ||
uses: actions/setup-go@v1 | ||
with: | ||
go-version: 1.15 | ||
- name: Check out code into the Go module directory | ||
uses: actions/checkout@v2 | ||
- name: build | ||
run: | | ||
go get -v -t -d ./... | ||
go generate ./... | ||
arch=${{matrix.arch}} | ||
export GOOS=${arch%%_*} GOARCH=${arch##*_} GOARM=5 | ||
go build -o unifi2mqtt.$arch -v --ldflags="-s -X'main.version=${{ github.ref }}'" . | ||
- name: upload-artifact unifi2mqtt | ||
uses: actions/upload-artifact@v1 | ||
with: | ||
name: unifi2mqtt.${{matrix.arch}} | ||
path: unifi2mqtt.${{matrix.arch}} | ||
release: | ||
name: Release | ||
runs-on: ubuntu-latest | ||
needs: build | ||
steps: | ||
- id: create_release | ||
name: Release | ||
uses: actions/create-release@v1 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
with: | ||
tag_name: ${{ github.ref }} | ||
release_name: Release ${{ github.ref }} | ||
draft: false | ||
prerelease: false | ||
outputs: | ||
upload_url: ${{ steps.create_release.outputs.upload_url }} | ||
assets: | ||
name: Upload Assets | ||
runs-on: ubuntu-latest | ||
needs: release | ||
strategy: | ||
matrix: | ||
arch: [linux_amd64, linux_arm, windows_amd64, darwin_amd64] | ||
artifact: [unifi2mqtt] | ||
steps: | ||
- name: download ${{matrix.artifact}}.${{matrix.arch}} | ||
uses: actions/download-artifact@v1 | ||
with: | ||
name: ${{matrix.artifact}}.${{matrix.arch}} | ||
path: ./ | ||
- name: upload ${{matrix.artifact}}.${{matrix.arch}} to release | ||
uses: actions/[email protected] | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
with: | ||
upload_url: ${{ needs.release.outputs.upload_url }} | ||
asset_path: ${{matrix.artifact}}.${{matrix.arch}} | ||
asset_name: ${{matrix.artifact}}.${{matrix.arch}} | ||
asset_content_type: application/octet-stream |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
docs/unifi2mqtt.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"github.com/rs/zerolog" | ||
"net" | ||
"path/filepath" | ||
"strings" | ||
"time" | ||
|
||
mqtt "github.com/eclipse/paho.mqtt.golang" | ||
"github.com/pdbogen/unifi2mqtt/types" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func startMqtt(cmd *cobra.Command, ch <-chan types.Client, | ||
log zerolog.Logger) error { | ||
log = log.With().Str("module", "mqtt").Logger() | ||
|
||
host, _ := cmd.Flags().GetString("mqtt-host") | ||
prefix, _ := cmd.Flags().GetString("mqtt-prefix") | ||
port, _ := cmd.Flags().GetInt("mqtt-port") | ||
proto, _ := cmd.Flags().GetString("mqtt-proto") | ||
|
||
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port)) | ||
if err != nil { | ||
return fmt.Errorf("checking MQTT config: %w", err) | ||
} | ||
conn.Close() | ||
|
||
opts := mqtt.NewClientOptions() | ||
opts.AddBroker(fmt.Sprintf("%s://%s:%d", proto, host, port)) | ||
client := mqtt.NewClient(opts) | ||
|
||
log.Info().Msg("connecting to MQTT server...") | ||
token := client.Connect() | ||
token.WaitTimeout(time.Minute) | ||
if err := token.Error(); err != nil { | ||
return fmt.Errorf("mqtt connect failed: %w", err) | ||
} | ||
if !client.IsConnected() { | ||
return errors.New("timeout waiting for mqtt to connect") | ||
} | ||
log.Info().Msg("connected to MQTT") | ||
|
||
go func(client mqtt.Client, prefix string, ch <-chan types.Client) { | ||
for device := range ch { | ||
payload := "not_home" | ||
if device.Present { | ||
payload = "home" | ||
} | ||
tok := client.Publish(filepath.Join(prefix, strings.ToLower(device.Name)), 1, true, payload) | ||
if err := tok.Error(); err != nil { | ||
log.Error().Err(err).Msg("error while publishing") | ||
} | ||
} | ||
}(client, prefix, ch) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/rs/zerolog" | ||
"golang.org/x/crypto/ssh/terminal" | ||
"os" | ||
"time" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
var Root = &cobra.Command{ | ||
Use: "unifi2mqtt", | ||
Short: "a tool to poll the UniFi controller API and populate clients to mqtt", | ||
Long: "unifi2mqtt is a small gateway daemon in the spirit of " + | ||
"zwave2mqtt, which polls the UniFi Controller API and feeds " + | ||
"information about connected devices into MQTT, for the intended " + | ||
"purpose of powering HomeAssistant's MQTT Device Tracker.", | ||
Run: rootRun, | ||
} | ||
|
||
func init() { | ||
Root.Flags().String("host", "localhost", "hostname of the UniFi "+ | ||
"controller") | ||
Root.Flags().Int("port", 8443, "UniFi controller port") | ||
Root.Flags().Bool("verify-tls", false, "if true, verify the TLS "+ | ||
"certificate of the UniFi controller") | ||
Root.Flags().String("username", "", "login user on UniFi controller "+ | ||
"(env: UNIFI2MQTT_USER)") | ||
Root.Flags().String("password", "", "user password on UniFi controller "+ | ||
"(env: UNIFI2MQTT_PASS)") | ||
Root.Flags().StringSlice("include-name", nil, "client names to include; "+ | ||
"if no --include flags are set, all clients will be reported. If a "+ | ||
"name is surrounded with `/`, it's interpreted as a regular "+ | ||
"expression. Flag may be specified multiple times, or just once "+ | ||
"with `,`-separated options.") | ||
Root.Flags().Duration("unifi-timeout", time.Minute, "how long does a "+ | ||
"device need to be not 'seen' before it's repoted as not_home. Note "+ | ||
"that since unifi2mqtt _polls_ the unifi API once a minute, there's "+ | ||
"no point in setting this lower than that. Unifi itself will stop "+ | ||
"reporting a device after about five minutes.") | ||
|
||
Root.Flags().String("mqtt-host", "localhost", "hostname of the MQTT server to publish to") | ||
Root.Flags().String("mqtt-prefix", "unifi", "a prefix for the mqtt "+ | ||
"space to publish, messages are published to "+ | ||
"{mqtt-prefix}/{device-name}") | ||
Root.Flags().Int("mqtt-port", 1883, "port used to connect to MQTT") | ||
Root.Flags().String("mqtt-proto", "tcp", "protocol used to connect to MQTT; options are `tcp`, `ssl`, `ws`") | ||
} | ||
|
||
func rootRun(cmd *cobra.Command, _ []string) { | ||
var log zerolog.Logger | ||
|
||
if terminal.IsTerminal(int(os.Stdout.Fd())) { | ||
log = zerolog.New(zerolog.NewConsoleWriter()) | ||
} else { | ||
log = zerolog.New(os.Stdout) | ||
} | ||
log = log.With().Timestamp().Logger() | ||
|
||
log.Info().Str("version", cmd.Root().Version).Msg("unifi2mqtt starting...") | ||
|
||
ch, err := startUnifi(cmd, log) | ||
if err != nil { | ||
log.Fatal().Err(err).Msg("could not connect to unifi controller") | ||
} | ||
|
||
if err := startMqtt(cmd, ch, log); err != nil { | ||
log.Fatal().Err(err).Msg("could not start mqtt client") | ||
} | ||
|
||
// block forever | ||
select {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"github.com/rs/zerolog" | ||
"os" | ||
|
||
"github.com/pdbogen/unifi2mqtt/types" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func startUnifi(cmd *cobra.Command, log zerolog.Logger) (<-chan types.Client, error) { | ||
log = log.With().Str("module", "unifi").Logger() | ||
|
||
verifyFlags(cmd, log) | ||
|
||
names, _ := cmd.Flags().GetStringSlice("include-name") | ||
matchers, err := types.NewMatchers(names) | ||
if err != nil { | ||
return nil, fmt.Errorf("parsing --include-name: %w", err) | ||
} | ||
|
||
username, _ := cmd.Flags().GetString("username") | ||
password, _ := cmd.Flags().GetString("password") | ||
host, _ := cmd.Flags().GetString("host") | ||
port, _ := cmd.Flags().GetInt("port") | ||
verifyTls, _ := cmd.Flags().GetBool("verify-tls") | ||
deviceTimeout, _ := cmd.Flags().GetDuration("unifi-timeout") | ||
|
||
u, err := types.NewUnifi(username, password, host, port, verifyTls, deviceTimeout, matchers, log) | ||
if err != nil { | ||
return nil, fmt.Errorf("starting unifi client: %w", err) | ||
} | ||
|
||
return u.Chan(), nil | ||
} | ||
|
||
func verifyFlags(cmd *cobra.Command, log zerolog.Logger) { | ||
for flag, env := range map[string]string{"username": "UNIFI2MQTT_USER", "password": "UNIFI2MQTT_PASS"} { | ||
flagV, _ := cmd.Flags().GetString(flag) | ||
if flagV == "" { | ||
flagV = os.Getenv(env) | ||
} | ||
if flagV == "" { | ||
log.Fatal().Msgf("flag --%s or environment variable %s is required", flag, env) | ||
} | ||
cmd.Flags().Set(flag, flagV) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"os" | ||
|
||
"github.com/pdbogen/unifi2mqtt/cmd" | ||
"github.com/spf13/cobra/doc" | ||
) | ||
|
||
func main() { | ||
_ = os.Mkdir("./docs", os.FileMode(0755)) | ||
err := doc.GenMarkdownTree(cmd.Root, "./docs/") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
## unifi2mqtt | ||
|
||
a tool to poll the UniFi controller API and populate clients to mqtt | ||
|
||
### Synopsis | ||
|
||
unifi2mqtt is a small gateway daemon in the spirit of zwave2mqtt, which polls the UniFi Controller API and feeds information about connected devices into MQTT, for the intended purpose of powering HomeAssistant's MQTT Device Tracker. | ||
|
||
``` | ||
unifi2mqtt [flags] | ||
``` | ||
|
||
### Options | ||
|
||
``` | ||
-h, --help help for unifi2mqtt | ||
--host string hostname of the UniFi controller (default "localhost") | ||
--include-name / client names to include; if no --include flags are set, all clients will be reported. If a name is surrounded with /, it's interpreted as a regular expression. Flag may be specified multiple times, or just once with `,`-separated options. | ||
--mqtt-host string hostname of the MQTT server to publish to (default "localhost") | ||
--mqtt-port int port used to connect to MQTT (default 1883) | ||
--mqtt-prefix string a prefix for the mqtt space to publish, messages are published to {mqtt-prefix}/{device-name} (default "unifi") | ||
--mqtt-proto tcp protocol used to connect to MQTT; options are tcp, `ssl`, `ws` (default "tcp") | ||
--password string user password on UniFi controller (env: UNIFI2MQTT_PASS) | ||
--port int UniFi controller port (default 8443) | ||
--unifi-timeout duration how long does a device need to be not 'seen' before it's repoted as not_home. Note that since unifi2mqtt _polls_ the unifi API once a minute, there's no point in setting this lower than that. Unifi itself will stop reporting a device after about five minutes. (default 1m0s) | ||
--username string login user on UniFi controller (env: UNIFI2MQTT_USER) | ||
--verify-tls if true, verify the TLS certificate of the UniFi controller | ||
``` | ||
|
||
###### Auto generated by spf13/cobra on 17-Aug-2020 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module github.com/pdbogen/unifi2mqtt | ||
|
||
go 1.14 | ||
|
||
require ( | ||
github.com/eclipse/paho.mqtt.golang v1.2.0 | ||
github.com/rs/zerolog v1.19.0 | ||
github.com/spf13/cobra v1.0.0 | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de | ||
golift.io/unifi v4.1.6+incompatible | ||
) |
Oops, something went wrong.