forked from OpenPDU/openpdu
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d253644bd | |||
| 93846dbbea | |||
| f781e24028 | |||
| fb819a71c4 | |||
| 82c33f9277 | |||
| c4567d780c | |||
| 56ad55b967 |
18
poc/homeassistant_autodiscover.txt
Normal file
18
poc/homeassistant_autodiscover.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
sonoff10/tele/LWT Online
|
||||
sonoff10/cmnd/POWER (null)
|
||||
sonoff10/tele/INFO1 {"Module":"Sonoff Basic","Version":"6.6.0(release-sonoff)","FallbackTopic":"cmnd/DVES_E4A1F5_fb/","GroupTopic":"sonoffs"}
|
||||
sonoff10/tele/INFO2 {"WebServerMode":"Admin","Hostname":"sonoff10-0501","IPAddress":"192.168.107.105"}
|
||||
sonoff10/tele/INFO3 {"RestartReason":"Software/System restart"}
|
||||
sonoff10/stat/RESULT {"POWER":"ON"}
|
||||
sonoff10/stat/POWER ON
|
||||
|
||||
homeassistant/switch/E4A1F5_RL_1/config {"name":"sonoff10","cmd_t":"~cmnd/POWER","stat_t":"~tele/STATE","val_tpl":"{{value_json.POWER}}","pl_off":"OFF","pl_on":"ON","avty_t":"~tele/LWT","pl_avail":"Online","pl_not_avail":"Offline","uniq_id":"E4A1F5_RL_1","device":{"identifiers":["E4A1F5"]},"~":"sonoff10/"}
|
||||
homeassistant/sensor/E4A1F5_status/config {"name":"sonoff10 status","stat_t":"~HASS_STATE","avty_t":"~LWT","pl_avail":"Online","pl_not_avail":"Offline","json_attributes_topic":"~HASS_STATE","unit_of_meas":" ","val_tpl":"{{value_json['RSSI']}}","uniq_id":"E4A1F5_status","device":{"identifiers":["E4A1F5"],"name":"sonoff10","model":"Sonoff Basic","sw_version":"6.6.0(release-sonoff)","manufacturer":"Tasmota"},"~":"sonoff10/tele/"}
|
||||
|
||||
sonoff10/tele/STATE {"Time":"1970-01-01T00:00:10","Uptime":"0T00:00:10","Heap":15,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":23,"POWER":"ON","Wifi":{"AP":1,"SSId":"iot","BSSId":"02:9F:C2:F7:CD:A9","Channel":5,"RSSI":100,"LinkCount":1,"Downtime":"0T00:00:06"}}
|
||||
sonoff10/stat/RESULT {"POWER":"ON"}
|
||||
sonoff10/stat/POWER ON
|
||||
sonoff10/tele/STATE {"Time":"2019-09-21T12:10:18","Uptime":"0T00:00:16","Heap":15,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"POWER":"ON","Wifi":{"AP":1,"SSId":"iot","BSSId":"02:9F:C2:F7:CD:A9","Channel":5,"RSSI":100,"LinkCount":1,"Downtime":"0T00:00:06"}}
|
||||
|
||||
|
||||
|
||||
3
poc/info.txt
Normal file
3
poc/info.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
docker run --rm -ti -p 1883:1883 alpine:latest sh -c "apk -U add mosquitto && mosquitto"
|
||||
|
||||
|
||||
37
poc/mqtt.go
Normal file
37
poc/mqtt.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
opts := MQTT.NewClientOptions()
|
||||
opts.AddBroker("tcp://127.0.0.1:1883")
|
||||
opts.SetClientID("ortobio")
|
||||
opts.SetUsername("DVES_USER")
|
||||
opts.SetPassword("DVES_PASS")
|
||||
opts.SetCleanSession(false)
|
||||
|
||||
client := MQTT.NewClient(opts)
|
||||
if token := client.Connect(); token.Wait() && token.Error() != nil {
|
||||
panic(token.Error())
|
||||
}
|
||||
fmt.Println("Sample Publisher Started")
|
||||
|
||||
qos := 0
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
fmt.Println("---- doing publish ----", i)
|
||||
token := client.Publish("ciaouno", byte(qos), false, fmt.Sprintf("%d", i))
|
||||
token.Wait()
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
|
||||
client.Disconnect(250)
|
||||
fmt.Println("Sample Publisher Disconnected")
|
||||
|
||||
}
|
||||
116
poc/mqtt2.go
Normal file
116
poc/mqtt2.go
Normal file
@@ -0,0 +1,116 @@
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
)
|
||||
|
||||
/*
|
||||
Options:
|
||||
[-help] Display help
|
||||
[-a pub|sub] Action pub (publish) or sub (subscribe)
|
||||
[-m <message>] Payload to send
|
||||
[-n <number>] Number of messages to send or receive
|
||||
[-q 0|1|2] Quality of Service
|
||||
[-clean] CleanSession (true if -clean is present)
|
||||
[-id <clientid>] CliendID
|
||||
[-user <user>] User
|
||||
[-password <password>] Password
|
||||
[-broker <uri>] Broker URI
|
||||
[-topic <topic>] Topic
|
||||
[-store <path>] Store Directory
|
||||
*/
|
||||
|
||||
func main() {
|
||||
topic := flag.String("topic", "", "The topic name to/from which to publish/subscribe")
|
||||
broker := flag.String("broker", "tcp://iot.eclipse.org:1883", "The broker URI. ex: tcp://10.10.1.1:1883")
|
||||
password := flag.String("password", "", "The password (optional)")
|
||||
user := flag.String("user", "", "The User (optional)")
|
||||
id := flag.String("id", "testgoid", "The ClientID (optional)")
|
||||
cleansess := flag.Bool("clean", false, "Set Clean Session (default false)")
|
||||
qos := flag.Int("qos", 0, "The Quality of Service 0,1,2 (default 0)")
|
||||
num := flag.Int("num", 1, "The number of messages to publish or subscribe (default 1)")
|
||||
payload := flag.String("message", "", "The message text to publish (default empty)")
|
||||
action := flag.String("action", "", "Action publish or subscribe (required)")
|
||||
store := flag.String("store", ":memory:", "The Store Directory (default use memory store)")
|
||||
flag.Parse()
|
||||
|
||||
if *action != "pub" && *action != "sub" {
|
||||
fmt.Println("Invalid setting for -action, must be pub or sub")
|
||||
return
|
||||
}
|
||||
|
||||
if *topic == "" {
|
||||
fmt.Println("Invalid setting for -topic, must not be empty")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Sample Info:\n")
|
||||
fmt.Printf("\taction: %s\n", *action)
|
||||
fmt.Printf("\tbroker: %s\n", *broker)
|
||||
fmt.Printf("\tclientid: %s\n", *id)
|
||||
fmt.Printf("\tuser: %s\n", *user)
|
||||
fmt.Printf("\tpassword: %s\n", *password)
|
||||
fmt.Printf("\ttopic: %s\n", *topic)
|
||||
fmt.Printf("\tmessage: %s\n", *payload)
|
||||
fmt.Printf("\tqos: %d\n", *qos)
|
||||
fmt.Printf("\tcleansess: %v\n", *cleansess)
|
||||
fmt.Printf("\tnum: %d\n", *num)
|
||||
fmt.Printf("\tstore: %s\n", *store)
|
||||
|
||||
opts := MQTT.NewClientOptions()
|
||||
opts.AddBroker(*broker)
|
||||
opts.SetClientID(*id)
|
||||
opts.SetUsername(*user)
|
||||
opts.SetPassword(*password)
|
||||
opts.SetCleanSession(*cleansess)
|
||||
if *store != ":memory:" {
|
||||
opts.SetStore(MQTT.NewFileStore(*store))
|
||||
}
|
||||
|
||||
if *action == "pub" {
|
||||
client := MQTT.NewClient(opts)
|
||||
if token := client.Connect(); token.Wait() && token.Error() != nil {
|
||||
panic(token.Error())
|
||||
}
|
||||
fmt.Println("Sample Publisher Started")
|
||||
for i := 0; i < *num; i++ {
|
||||
fmt.Println("---- doing publish ----")
|
||||
token := client.Publish(*topic, byte(*qos), false, *payload)
|
||||
token.Wait()
|
||||
}
|
||||
|
||||
client.Disconnect(250)
|
||||
fmt.Println("Sample Publisher Disconnected")
|
||||
} else {
|
||||
receiveCount := 0
|
||||
choke := make(chan [2]string)
|
||||
|
||||
opts.SetDefaultPublishHandler(func(client MQTT.Client, msg MQTT.Message) {
|
||||
choke <- [2]string{msg.Topic(), string(msg.Payload())}
|
||||
})
|
||||
|
||||
client := MQTT.NewClient(opts)
|
||||
if token := client.Connect(); token.Wait() && token.Error() != nil {
|
||||
panic(token.Error())
|
||||
}
|
||||
|
||||
if token := client.Subscribe(*topic, byte(*qos), nil); token.Wait() && token.Error() != nil {
|
||||
fmt.Println(token.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for receiveCount < *num {
|
||||
incoming := <-choke
|
||||
fmt.Printf("RECEIVED TOPIC: %s MESSAGE: %s\n", incoming[0], incoming[1])
|
||||
receiveCount++
|
||||
}
|
||||
|
||||
client.Disconnect(250)
|
||||
fmt.Println("Sample Subscriber Disconnected")
|
||||
}
|
||||
}
|
||||
83
source/board.go
Normal file
83
source/board.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
// BoardType Status
|
||||
const (
|
||||
BoardTypeDummy uint = 0
|
||||
BoardTypeGPIO uint = 1
|
||||
BoardTypeI2CGPIO uint = 2
|
||||
BoardTypeI2CADC uint = 3
|
||||
)
|
||||
|
||||
// Board def
|
||||
type Board struct {
|
||||
ChannelCount uint `json:"channelcount"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type uint `json:"type"`
|
||||
Bus uint `json:"bus"`
|
||||
Address uint `json:"address"`
|
||||
dummyValue map[uint]bool
|
||||
}
|
||||
|
||||
// ChannelName - return the name of a channel, useful for onboard GPIO
|
||||
func (b *Board) ChannelName(num uint) string {
|
||||
return string(num)
|
||||
}
|
||||
|
||||
// PowerON def
|
||||
func (b *Board) PowerON(num uint) error {
|
||||
switch b.Type {
|
||||
case BoardTypeDummy:
|
||||
if b.dummyValue == nil {
|
||||
b.dummyValue = make(map[uint]bool)
|
||||
}
|
||||
b.dummyValue[num] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PowerOFF def
|
||||
func (b *Board) PowerOFF(num uint) error {
|
||||
switch b.Type {
|
||||
case BoardTypeDummy:
|
||||
if b.dummyValue == nil {
|
||||
b.dummyValue = make(map[uint]bool)
|
||||
}
|
||||
b.dummyValue[num] = false
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PowerToggle def
|
||||
func (b *Board) PowerToggle(num uint) error {
|
||||
switch b.Type {
|
||||
case BoardTypeDummy:
|
||||
if b.dummyValue == nil {
|
||||
b.dummyValue = make(map[uint]bool)
|
||||
}
|
||||
v, _ := b.PowerStatus(num)
|
||||
b.dummyValue[num] = !v
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PowerStatus def
|
||||
func (b *Board) PowerStatus(num uint) (bool, error) {
|
||||
switch b.Type {
|
||||
case BoardTypeDummy:
|
||||
if b.dummyValue == nil {
|
||||
b.dummyValue = make(map[uint]bool)
|
||||
}
|
||||
val, ok := b.dummyValue[num]
|
||||
if !ok {
|
||||
b.dummyValue[num] = false
|
||||
val = false
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
45
source/boardlink.go
Normal file
45
source/boardlink.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
// Boardlink def
|
||||
type Boardlink struct {
|
||||
BoardID string `json:"boardid"`
|
||||
Channel uint `json:"channel"`
|
||||
board *Board
|
||||
}
|
||||
|
||||
// Board def
|
||||
func (bl Boardlink) Board() *Board {
|
||||
if bl.board == nil {
|
||||
for i := range TheConfig.Boards {
|
||||
if TheConfig.Boards[i].ID == bl.BoardID {
|
||||
bl.board = TheConfig.Boards[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return bl.board
|
||||
}
|
||||
|
||||
// PowerON def
|
||||
func (bl Boardlink) PowerON() error {
|
||||
b := bl.Board()
|
||||
return b.PowerON(bl.Channel)
|
||||
}
|
||||
|
||||
// PowerOFF def
|
||||
func (bl Boardlink) PowerOFF() error {
|
||||
b := bl.Board()
|
||||
return b.PowerOFF(bl.Channel)
|
||||
}
|
||||
|
||||
// PowerToggle def
|
||||
func (bl Boardlink) PowerToggle() error {
|
||||
b := bl.Board()
|
||||
return b.PowerToggle(bl.Channel)
|
||||
}
|
||||
|
||||
// PowerStatus def
|
||||
func (bl Boardlink) PowerStatus() (bool, error) {
|
||||
b := bl.Board()
|
||||
return b.PowerStatus(bl.Channel)
|
||||
}
|
||||
@@ -1,20 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func readConfig() {
|
||||
viper.SetConfigName("openpdu.yaml") // name of config file (without extension)
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath("/etc/openpdu/") // path to look for the config file in
|
||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||
err := viper.ReadInConfig() // Find and read the config file
|
||||
if err != nil { // Handle errors reading the config file
|
||||
log.Printf("Fatal error config file: %s \n", err)
|
||||
// Configuration def
|
||||
type Configuration struct {
|
||||
Hostname string `json:"hostname"`
|
||||
Outlets map[(uint)]Outlet `json:"outlets"`
|
||||
Boards []*Board `json:"boards"`
|
||||
MQTT MQTTConfig `json:"mqtt"`
|
||||
}
|
||||
|
||||
// TheConfig def
|
||||
var TheConfig Configuration
|
||||
|
||||
func loadConfig(filename string) (Configuration, error) {
|
||||
bytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
viper.SetDefault("hostname", "openpdu")
|
||||
var c Configuration
|
||||
err = json.Unmarshal(bytes, &c)
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func saveConfig(c Configuration, filename string) error {
|
||||
bytes, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename, bytes, 0644)
|
||||
}
|
||||
|
||||
func writeConfig() error {
|
||||
return saveConfig(TheConfig, "t1.json")
|
||||
}
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
// "encoding/json"
|
||||
"log"
|
||||
|
||||
// "periph.io/x/periph"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Dictionary aaa
|
||||
// Dictionary definition
|
||||
type Dictionary map[string]interface{}
|
||||
|
||||
func initOutlets() {
|
||||
for _, o := range TheConfig.Outlets {
|
||||
o.PowerInitial()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
readConfig()
|
||||
var c1 Configuration
|
||||
|
||||
nam := viper.Get("hostname")
|
||||
log.Printf("hostname: %v\n", nam)
|
||||
c1 = createMockConfig()
|
||||
|
||||
initI2C()
|
||||
err = saveConfig(c1, "t.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
TheConfig, err = loadConfig("t.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = saveConfig(TheConfig, "t1.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
startServer()
|
||||
|
||||
}
|
||||
|
||||
// https://github.com/ColorlibHQ/AdminLTE/archive/v2.4.17.tar.gz
|
||||
|
||||
101
source/mock.go
Normal file
101
source/mock.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
func createMockConfig() Configuration {
|
||||
b1 := new(Board)
|
||||
b1.ID = "47e41dc9-4a14-4b79-8644-d7442a15cb50"
|
||||
b1.Name = "Virtual IO"
|
||||
b1.Type = BoardTypeDummy
|
||||
b1.ChannelCount = 40
|
||||
|
||||
b2 := new(Board)
|
||||
b2.ID = "6561df75-bf93-43f5-82ac-9b3dda081961"
|
||||
b2.Name = "Internal GPIO"
|
||||
b2.Type = BoardTypeGPIO
|
||||
b2.ChannelCount = 40
|
||||
|
||||
b3 := new(Board)
|
||||
b3.Bus = 1
|
||||
b3.Address = 0x29
|
||||
b3.ID = "79690164-214f-41b0-93f9-e910dd54f323"
|
||||
b3.Name = "bordo1"
|
||||
b3.Type = BoardTypeI2CGPIO
|
||||
b3.ChannelCount = 8
|
||||
|
||||
b4 := new(Board)
|
||||
b4.Bus = 1
|
||||
b4.Address = 0x27
|
||||
b4.ID = "93f446d8-59e4-4abd-8bf7-e31cd80bc713"
|
||||
b4.Name = "bordo2"
|
||||
b4.Type = BoardTypeI2CADC
|
||||
b4.ChannelCount = 4
|
||||
|
||||
return Configuration{
|
||||
Hostname: "maramao",
|
||||
MQTT: MQTTConfig{
|
||||
BrokerIP: "192.168.2.190",
|
||||
BrokerPort: "1883",
|
||||
ClientID: "openpdu-123",
|
||||
Username: "DVES_USER",
|
||||
Password: "DVES_PASS",
|
||||
CleanSession: false,
|
||||
Topic: "openpdu/ok",
|
||||
HomeAssistant: true,
|
||||
},
|
||||
Boards: []*Board{b1, b2, b3, b4},
|
||||
Outlets: map[(uint)]Outlet{
|
||||
0: Outlet{
|
||||
Name: "uscita 0",
|
||||
Location: "port 1 dx",
|
||||
HasPowerMeter: true,
|
||||
Command: Boardlink{
|
||||
BoardID: b1.ID,
|
||||
Channel: 0,
|
||||
},
|
||||
PowerMeter: Boardlink{
|
||||
BoardID: b4.ID,
|
||||
Channel: 0,
|
||||
},
|
||||
},
|
||||
1: Outlet{
|
||||
Name: "uscita 1",
|
||||
Location: "port 1 sx",
|
||||
HasPowerMeter: true,
|
||||
Command: Boardlink{
|
||||
BoardID: b1.ID,
|
||||
Channel: 1,
|
||||
},
|
||||
PowerMeter: Boardlink{
|
||||
BoardID: b4.ID,
|
||||
Channel: 1,
|
||||
},
|
||||
},
|
||||
2: Outlet{
|
||||
Name: "uscita 2",
|
||||
Location: "port 2 dx",
|
||||
HasPowerMeter: false,
|
||||
Command: Boardlink{
|
||||
BoardID: "79690164-214f-41b0-93f9-e910dd54f323",
|
||||
Channel: 2,
|
||||
},
|
||||
},
|
||||
3: Outlet{
|
||||
Name: "uscita 5v 1",
|
||||
Location: "usb avanti 1",
|
||||
HasPowerMeter: false,
|
||||
Command: Boardlink{
|
||||
BoardID: "47e41dc9-4a14-4b79-8644-d7442a15cb50",
|
||||
Channel: 21,
|
||||
},
|
||||
},
|
||||
4: Outlet{
|
||||
Name: "uscita 5v 2",
|
||||
Location: "usb avanti 2",
|
||||
HasPowerMeter: false,
|
||||
Command: Boardlink{
|
||||
BoardID: "47e41dc9-4a14-4b79-8644-d7442a15cb50",
|
||||
Channel: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
13
source/mqtt.go
Normal file
13
source/mqtt.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
// MQTTConfig def
|
||||
type MQTTConfig struct {
|
||||
BrokerIP string `json:"ip"`
|
||||
BrokerPort string `json:"port"`
|
||||
ClientID string `json:"clientid"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
CleanSession bool `json:"cleansession"`
|
||||
Topic string `json:"topic"`
|
||||
HomeAssistant bool `json:"homeassistant"`
|
||||
}
|
||||
49
source/outlet.go
Normal file
49
source/outlet.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
// Outlet Initial Status
|
||||
const (
|
||||
OutletInitialStatusOFF uint = 0
|
||||
OutletInitialStatusON uint = 1
|
||||
OutletInitialStatusLAST uint = 2 // if hardware supported
|
||||
)
|
||||
|
||||
// Outlet def
|
||||
type Outlet struct {
|
||||
Name string `json:"name"`
|
||||
Location string `json:"location"`
|
||||
HasPowerMeter bool `json:"haspowermeter"`
|
||||
InitialStatus uint `json:"initialstatus"`
|
||||
Command Boardlink `json:"command"`
|
||||
PowerMeter Boardlink `json:"powermeter"`
|
||||
}
|
||||
|
||||
// PowerON def
|
||||
func (o Outlet) PowerON() error {
|
||||
return o.Command.PowerON()
|
||||
}
|
||||
|
||||
// PowerOFF def
|
||||
func (o Outlet) PowerOFF() error {
|
||||
return o.Command.PowerOFF()
|
||||
}
|
||||
|
||||
// PowerToggle def
|
||||
func (o Outlet) PowerToggle() error {
|
||||
return o.Command.PowerToggle()
|
||||
}
|
||||
|
||||
// PowerInitial def
|
||||
func (o Outlet) PowerInitial() error {
|
||||
switch o.InitialStatus {
|
||||
case OutletInitialStatusOFF:
|
||||
return o.Command.PowerOFF()
|
||||
case OutletInitialStatusON:
|
||||
return o.Command.PowerON()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PowerStatus def
|
||||
func (o Outlet) PowerStatus() (bool, error) {
|
||||
return o.Command.PowerStatus()
|
||||
}
|
||||
34
source/web.go
Normal file
34
source/web.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-macaron/pongo2"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func startServer() {
|
||||
m := macaron.Classic()
|
||||
m.Use(pongo2.Pongoer())
|
||||
m.Use(macaron.Static("static"))
|
||||
// m.Get("/", myHandler)
|
||||
|
||||
m.Get("/", statusPage)
|
||||
m.Get("/json/status", jsonStatus)
|
||||
m.Post("/json/outlet/:outlet/on", jsonOutletPowerON)
|
||||
m.Post("/json/outlet/:outlet/off", jsonOutletPowerOFF)
|
||||
m.Post("/json/outlet/:outlet/toggle", jsonOutletPowerToggle)
|
||||
|
||||
m.Get("/settings/mqtt", webGETSettingsMQTT)
|
||||
m.Post("/json/settings/mqtt", binding.Bind(SettingsMQTTForm{}), webPOSTSettingsMQTT)
|
||||
// m.Post("/settings/mqtt", webPOSTSettingsMQTT)
|
||||
|
||||
m.Get("/boards", func(ctx *macaron.Context) {
|
||||
ctx.HTML(200, "boards") // 200 is the response code.
|
||||
})
|
||||
|
||||
log.Println("Server is running...")
|
||||
log.Println(http.ListenAndServe("0.0.0.0:4000", m))
|
||||
}
|
||||
129
source/webjson.go
Normal file
129
source/webjson.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func jsonStatus(ctx *macaron.Context) {
|
||||
|
||||
out := [][]string{}
|
||||
|
||||
for num, o := range TheConfig.Outlets {
|
||||
pwr, _ := o.PowerStatus()
|
||||
pwrstr := "0"
|
||||
if pwr {
|
||||
pwrstr = "1"
|
||||
}
|
||||
out = append(out, []string{fmt.Sprintf("%d", num), o.Name, pwrstr})
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": out,
|
||||
})
|
||||
}
|
||||
|
||||
func jsonOutletPowerToggle(ctx *macaron.Context) {
|
||||
var outletnum64 uint64
|
||||
var err error
|
||||
|
||||
outletnum64, err = strconv.ParseUint(ctx.Params(":outlet"), 10, 32)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "No outlet specified",
|
||||
})
|
||||
}
|
||||
|
||||
outletnum := uint(outletnum64)
|
||||
outlet, exists := TheConfig.Outlets[outletnum]
|
||||
if !exists {
|
||||
// error: outlet doesn't exists
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "Outlet doesn't exists",
|
||||
})
|
||||
}
|
||||
|
||||
err = outlet.PowerToggle()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "Can't toggle power",
|
||||
})
|
||||
}
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "ok",
|
||||
})
|
||||
}
|
||||
|
||||
func jsonOutletPowerON(ctx *macaron.Context) {
|
||||
var outletnum64 uint64
|
||||
var err error
|
||||
|
||||
outletnum64, err = strconv.ParseUint(ctx.Params(":outlet"), 10, 32)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "No outlet specified",
|
||||
})
|
||||
}
|
||||
outletnum := uint(outletnum64)
|
||||
|
||||
outlet, exists := TheConfig.Outlets[outletnum]
|
||||
if !exists {
|
||||
// error: outlet doesn't exists
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "Outlet doesn't exists",
|
||||
})
|
||||
}
|
||||
|
||||
err = outlet.PowerON()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "Can't toggle power",
|
||||
})
|
||||
}
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "ok",
|
||||
})
|
||||
}
|
||||
|
||||
func jsonOutletPowerOFF(ctx *macaron.Context) {
|
||||
var outletnum64 uint64
|
||||
var err error
|
||||
|
||||
outletnum64, err = strconv.ParseUint(ctx.Params(":outlet"), 10, 32)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "No outlet specified",
|
||||
})
|
||||
}
|
||||
outletnum := uint(outletnum64)
|
||||
|
||||
outlet, exists := TheConfig.Outlets[outletnum]
|
||||
if !exists {
|
||||
// error: outlet doesn't exists
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "Outlet doesn't exists",
|
||||
})
|
||||
}
|
||||
|
||||
err = outlet.PowerOFF()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error",
|
||||
"error": "Can't toggle power",
|
||||
})
|
||||
}
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "ok",
|
||||
})
|
||||
}
|
||||
66
source/webpages.go
Normal file
66
source/webpages.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func statusPage(ctx *macaron.Context) {
|
||||
var pluglist []Dictionary
|
||||
|
||||
for num, o := range TheConfig.Outlets {
|
||||
pluglist = append(pluglist, Dictionary{"id": num, "description": o.Name})
|
||||
}
|
||||
|
||||
ctx.Data["pluglist"] = pluglist
|
||||
ctx.HTML(200, "status") // 200 is the response code.
|
||||
}
|
||||
|
||||
//SettingsMQTTForm definition
|
||||
type SettingsMQTTForm struct {
|
||||
BrokerIP string `form:"brokerip" binding:"Required"`
|
||||
BrokerPort string `form:"brokerport" binding:"Required"`
|
||||
ClientID string `form:"clientid" binding:"Required"`
|
||||
Username string `form:"username"`
|
||||
Password string `form:"password"`
|
||||
Topic string `form:"topic" binding:"Required"`
|
||||
CleanSession bool `form:"cleansession"`
|
||||
HomeAssistant bool `form:"homeassistant"`
|
||||
}
|
||||
|
||||
func webGETSettingsMQTT(ctx *macaron.Context) {
|
||||
ctx.Data["r_brokerip"] = TheConfig.MQTT.BrokerIP
|
||||
ctx.Data["r_brokerport"] = TheConfig.MQTT.BrokerPort
|
||||
ctx.Data["r_clientid"] = TheConfig.MQTT.ClientID
|
||||
ctx.Data["r_username"] = TheConfig.MQTT.Username
|
||||
ctx.Data["r_password"] = TheConfig.MQTT.Password
|
||||
ctx.Data["r_cleansession"] = TheConfig.MQTT.CleanSession
|
||||
ctx.Data["r_topic"] = TheConfig.MQTT.Topic
|
||||
ctx.Data["r_homeassistant"] = TheConfig.MQTT.HomeAssistant
|
||||
ctx.HTML(200, "settings_mqtt") // 200 is the response code.
|
||||
}
|
||||
|
||||
func webPOSTSettingsMQTT(ctx *macaron.Context, f SettingsMQTTForm) {
|
||||
|
||||
TheConfig.MQTT.BrokerIP = f.BrokerIP
|
||||
TheConfig.MQTT.BrokerPort = f.BrokerPort
|
||||
TheConfig.MQTT.ClientID = f.ClientID
|
||||
TheConfig.MQTT.Username = f.Username
|
||||
TheConfig.MQTT.Password = f.Password
|
||||
TheConfig.MQTT.Topic = f.Topic
|
||||
// TODO: cleansession
|
||||
// TODO: homeassistant
|
||||
|
||||
err := writeConfig()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"result": "error",
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"result": "ok",
|
||||
})
|
||||
}
|
||||
113
source/webui.go
113
source/webui.go
@@ -1,113 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-macaron/pongo2"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func statusPage(ctx *macaron.Context) {
|
||||
ctx.Data["pluglist"] = []Dictionary{
|
||||
{"id": 1, "description": "p1"},
|
||||
{"id": 2, "description": "p2"},
|
||||
{"id": 3, "description": "p3"},
|
||||
{"id": 4, "description": "p4"},
|
||||
{"id": 5, "description": "p5"},
|
||||
{"id": 6, "description": "p6"},
|
||||
{"id": 7, "description": "p7"},
|
||||
{"id": 8, "description": "p8"},
|
||||
}
|
||||
ctx.HTML(200, "status") // 200 is the response code.
|
||||
}
|
||||
|
||||
func jsonStatus(ctx *macaron.Context) {
|
||||
|
||||
p0, err := MyBoard.channelStatus(0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p1, err := MyBoard.channelStatus(1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p2, err := MyBoard.channelStatus(2)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p3, err := MyBoard.channelStatus(3)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p4, err := MyBoard.channelStatus(4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p5, err := MyBoard.channelStatus(5)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p6, err := MyBoard.channelStatus(6)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p7, err := MyBoard.channelStatus(7)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": [][]string{
|
||||
{"0", "p0", fmt.Sprint(p0)},
|
||||
{"1", "p1", fmt.Sprint(p1)},
|
||||
{"2", "p2", fmt.Sprint(p2)},
|
||||
{"3", "p3", fmt.Sprint(p3)},
|
||||
{"4", "p4", fmt.Sprint(p4)},
|
||||
{"5", "p5", fmt.Sprint(p5)},
|
||||
{"6", "p6", fmt.Sprint(p6)},
|
||||
{"7", "p7", fmt.Sprint(p7)},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func jsonOutletToggle(ctx *macaron.Context) {
|
||||
id, err := strconv.ParseUint(ctx.Params(":id"), 10, 64)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = MyBoard.channelToggle(uint(id))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "ok",
|
||||
})
|
||||
}
|
||||
|
||||
func startServer() {
|
||||
m := macaron.Classic()
|
||||
m.Use(pongo2.Pongoer())
|
||||
m.Use(macaron.Static("static"))
|
||||
// m.Get("/", myHandler)
|
||||
|
||||
m.Get("/", statusPage)
|
||||
m.Get("/json/status", jsonStatus)
|
||||
m.Post("/json/outlet/:id/toggle", jsonOutletToggle)
|
||||
|
||||
m.Get("/boards", func(ctx *macaron.Context) {
|
||||
ctx.HTML(200, "boards") // 200 is the response code.
|
||||
})
|
||||
|
||||
log.Println("Server is running...")
|
||||
log.Println(http.ListenAndServe("0.0.0.0:4000", m))
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func (b I2CBoard) channelToggle(ch uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func initI2C() {
|
||||
func init() {
|
||||
// Make sure periph is initialized.
|
||||
if _, err := host.Init(); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -101,14 +101,4 @@ func initI2C() {
|
||||
MyBoard.i2cdev = *mydevice
|
||||
MyBoard.data = make([]byte, (MyBoard.channels-1)/8+1)
|
||||
|
||||
go func() {
|
||||
var i uint
|
||||
for i = 0; i < 8; i++ {
|
||||
v, err := MyBoard.channelStatus(i)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("Channel %d status: %v", i, v)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -82,8 +82,8 @@
|
||||
|
||||
|
||||
<!-- DataTables -->
|
||||
<script src="../../bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
|
||||
<script src="../../bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
|
||||
<script src="/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
<link rel="stylesheet" href="../../bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="../../bower_components/font-awesome/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="/bower_components/font-awesome/css/font-awesome.min.css">
|
||||
<!-- Ionicons -->
|
||||
<link rel="stylesheet" href="../../bower_components/Ionicons/css/ionicons.min.css">
|
||||
<link rel="stylesheet" href="/bower_components/Ionicons/css/ionicons.min.css">
|
||||
<!-- Theme style -->
|
||||
<link rel="stylesheet" href="adminlte/css/AdminLTE.min.css">
|
||||
<link rel="stylesheet" href="/adminlte/css/AdminLTE.min.css">
|
||||
<!-- AdminLTE Skins. Choose a skin from the css/skins
|
||||
folder instead of downloading all of them to reduce the load. -->
|
||||
<link rel="stylesheet" href="adminlte/css/skins/_all-skins.min.css">
|
||||
<link rel="stylesheet" href="/adminlte/css/skins/_all-skins.min.css">
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
@@ -23,4 +23,4 @@
|
||||
<![endif]-->
|
||||
|
||||
<!-- Google Font -->
|
||||
<link rel="stylesheet" href="../../googlefonts/fonts.css">
|
||||
<link rel="stylesheet" href="/googlefonts/fonts.css">
|
||||
@@ -1,10 +1,10 @@
|
||||
<!-- jQuery 3 -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.min.js"></script>
|
||||
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
<script src="../../bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<!-- Slimscroll -->
|
||||
<script src="../../bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script>
|
||||
<script src="/bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script>
|
||||
<!-- FastClick -->
|
||||
<script src="../../bower_components/fastclick/lib/fastclick.js"></script>
|
||||
<script src="/bower_components/fastclick/lib/fastclick.js"></script>
|
||||
<!-- AdminLTE App -->
|
||||
<script src="adminlte/js/adminlte.min.js"></script>
|
||||
<script src="/adminlte/js/adminlte.min.js"></script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<header class="main-header">
|
||||
<!-- Logo -->
|
||||
<a href="../../index2.html" class="logo">
|
||||
<a href="/" class="logo">
|
||||
<!-- mini logo for sidebar mini 50x50 pixels -->
|
||||
<span class="logo-mini">O<b>P</b></span>
|
||||
<!-- logo for regular state and mobile devices -->
|
||||
|
||||
@@ -26,19 +26,46 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
{% if pageselected == "settings" || pageselected == "settings/mqtt" %}
|
||||
<li class="treeview menu-open">
|
||||
{% else %}
|
||||
<li class="treeview">
|
||||
{% endif %}
|
||||
|
||||
<a href="#">
|
||||
<i class="fa fa-gears"></i> <span>Settings</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li><a href="/settings/lan"><i class="fa fa-circle-o"></i> LAN</a></li>
|
||||
<li><a href="/settings/mqtt"><i class="fa fa-circle-o"></i> MQTT</a></li>
|
||||
<li><a href="/settings/syslog"><i class="fa fa-circle-o"></i> syslog</a></li>
|
||||
<li><a href="/settings/backup"><i class="fa fa-circle-o"></i> backup</a></li>
|
||||
<li><a href="/settings/restore"><i class="fa fa-circle-o"></i> restore</a></li>
|
||||
|
||||
{% if pageselected == "settings" || pageselected == "settings/mqtt" %}
|
||||
<ul class="treeview-menu" style="display: block;">
|
||||
{% else %}
|
||||
<ul class="treeview-menu" style="display: none;">
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="/settings/lan"><i class="fa fa-circle-o"></i> LAN</a>
|
||||
</li>
|
||||
|
||||
{% if pageselected == "settings/mqtt" %}
|
||||
<li class="active">
|
||||
{% else %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a href="/settings/mqtt"><i class="fa fa-circle-o"></i> MQTT</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/settings/syslog"><i class="fa fa-circle-o"></i> syslog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/settings/backup"><i class="fa fa-circle-o"></i> backup</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/settings/restore"><i class="fa fa-circle-o"></i> restore</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
242
templates/settings_mqtt.html
Normal file
242
templates/settings_mqtt.html
Normal file
@@ -0,0 +1,242 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
{% include "common/common-head.html" %}
|
||||
</head>
|
||||
<body class="hold-transition skin-blue sidebar-mini">
|
||||
<div class="wrapper">
|
||||
|
||||
{% include "common/page-header.html" %}
|
||||
{% with pageselected="settings/mqtt" %}
|
||||
{% include "common/sidebar-menu.html" %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Content Wrapper. Contains page content -->
|
||||
<div class="content-wrapper">
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
MQTT configuration
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
|
||||
|
||||
|
||||
<div class="row" id="notification-success" style="display: none;">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-success box-solid">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Settings saved!</h3>
|
||||
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn btn-box-tool" data-widget="remove"><i
|
||||
class="fa fa-times"></i></button>
|
||||
</div>
|
||||
<!-- /.box-tools -->
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
The body of the box
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
<div class="row" id="notification-error" style="display: none;">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-danger box-solid">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Error saving settings!</h3>
|
||||
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn btn-box-tool" data-widget="remove"><i
|
||||
class="fa fa-times"></i></button>
|
||||
</div>
|
||||
<!-- /.box-tools -->
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
The body of the box
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<!-- left column -->
|
||||
<div class="col-md-12">
|
||||
<!-- general form elements -->
|
||||
<div class="box box-primary">
|
||||
<!-- form start -->
|
||||
<form role="form" id="theform" class="form-horizontal">
|
||||
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="brokerip">Broker IP address</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="brokerip" placeholder="MQTT broker IP address or hostname" value="{{ r_brokerip }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="brokerport">Broker port</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="brokerport" placeholder="MQTT broker port" value="{{ r_brokerport }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="clientid">MQTT Client ID</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="clientid" placeholder="MQTT client ID" value="{{ r_clientid }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="username">Username</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="username" placeholder="MQTT broker username" value="{{ r_username }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="password">Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="password" placeholder="MQTT broker password" value="{{ r_password }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="topic">Topic</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="topic" placeholder="MQTT topic" value="{{ r_topic }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<!-- value !!!! -->
|
||||
<input type="checkbox" id="cleansession"> Clean session
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<!-- value !!!! -->
|
||||
<input type="checkbox" id="homeassistant"> HomeAssistant auto discovery
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary pull-right">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
|
||||
</div>
|
||||
<!--/.col (right) -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
</div>
|
||||
<!-- /.content-wrapper -->
|
||||
|
||||
{% include "common/footer.html" %}
|
||||
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
|
||||
{% include "common/common-js.html" %}
|
||||
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('#brokerip').select();
|
||||
$('#theform').on( 'submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: '/json/settings/mqtt',
|
||||
type: 'post',
|
||||
data: {
|
||||
'brokerip': $('#brokerip').val(),
|
||||
'brokerport': $('#brokerport').val(),
|
||||
'clientid': $('#clientid').val(),
|
||||
'username': $('#username').val(),
|
||||
'password': $('#password').val(),
|
||||
'topic': $('#topic').val(),
|
||||
'cleansession': $('#cleansession').val(),
|
||||
'homeassistant': $('#homeassistant').val(),
|
||||
},
|
||||
})
|
||||
.done(function(r) {
|
||||
if (r.result=='ok') {
|
||||
$('#notification-success .box-body').text('MQTT settings saved.');
|
||||
$("#notification-success").show("fast");
|
||||
setTimeout(function(){
|
||||
$("#notification-success").fadeOut("slow");
|
||||
},1000);
|
||||
} else {
|
||||
$('#notification-error .box-body').text('Error saving MQTT settings.');
|
||||
$("#notification-error").show("fast");
|
||||
setTimeout(function(){
|
||||
$("#notification-error").fadeOut("slow");
|
||||
},5000);
|
||||
}
|
||||
})
|
||||
.fail(function(jqXHR, textStatus, errorThrown) {
|
||||
var responseText = jQuery.parseJSON(jqXHR.responseText);
|
||||
$('#notification-error .box-body').text('Something went wrong. Settings not saved.');
|
||||
$("#notification-error").show("fast");
|
||||
setTimeout(function(){
|
||||
$("#notification-error").fadeOut("slow");
|
||||
},5000);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -65,8 +65,8 @@
|
||||
{% include "common/common-js.html" %}
|
||||
|
||||
<!-- DataTables -->
|
||||
<script src="../../bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
|
||||
<script src="../../bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
|
||||
<script src="/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
42
v.go
Normal file
42
v.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
|
||||
// Board def
|
||||
type Board struct {
|
||||
ChannelCount uint `json:"channelcount"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// Configuration def
|
||||
type Configuration struct {
|
||||
Hostname string `json:"hostname"`
|
||||
// Outlets map[(uint)]Outlet `json:"outlets"`
|
||||
Boards []Board `json:"boards"`
|
||||
// MQTT MQTTConfig `json:"mqtt"`
|
||||
}
|
||||
|
||||
// Boards definition
|
||||
var Boards []Board
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
viper.SetConfigName("v")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(".")
|
||||
|
||||
mock()
|
||||
|
||||
}
|
||||
|
||||
func mock() {
|
||||
b := Board{
|
||||
ID: "6561df75-bf93-43f5-82ac-9b3dda081961",
|
||||
Name: "Internal GPIO",
|
||||
Type: "GPIORelayBoard",
|
||||
ChannelCount: 40,
|
||||
}
|
||||
Boards = append(Boards, b)
|
||||
}
|
||||
Reference in New Issue
Block a user