This commit is contained in:
2019-10-27 22:39:00 +01:00
parent 82c33f9277
commit fb819a71c4
11 changed files with 546 additions and 270 deletions

88
t/board.go Normal file
View File

@@ -0,0 +1,88 @@
package main
import "log"
// 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)
log.Printf("toggle prestatus %v:%v", num, b.dummyValue[num])
b.dummyValue[num] = !v
log.Printf("toggle poststatus %v:%v", num, b.dummyValue[num])
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] = true
val = true
}
log.Printf("status %v:%v", num, val)
return val, nil
}
return false, nil
}

45
t/boardlink.go Normal file
View 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.ID == "" {
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)
}

41
t/config.go Normal file
View File

@@ -0,0 +1,41 @@
package main
import (
"encoding/json"
"io/ioutil"
)
// Configuration def
type Configuration struct {
Hostname string `json:"hostname"`
Outlets map[(uint)]Outlet `json:"outlets"`
Boards []Board `json:"boards"`
MQTT MQTTConfig `json:"mqtt"`
}
func loadConfig(filename string) (Configuration, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return Configuration{}, err
}
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)
}
// TheConfig def
var TheConfig Configuration

36
t/main.go Normal file
View File

@@ -0,0 +1,36 @@
package main
// Dictionary definition
type Dictionary map[string]interface{}
func initOutlets() {
for _, o := range TheConfig.Outlets {
o.PowerInitial()
}
}
func main() {
var err error
var c1 Configuration
c1 = createMockConfig()
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()
}

102
t/mock.go Normal file
View File

@@ -0,0 +1,102 @@
package main
func createMockConfig() Configuration {
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{
Board{
ID: "47e41dc9-4a14-4b79-8644-d7442a15cb50",
Name: "Virtual IO",
Type: BoardTypeDummy,
ChannelCount: 40,
},
Board{
ID: "6561df75-bf93-43f5-82ac-9b3dda081961",
Name: "Internal GPIO",
Type: BoardTypeGPIO,
ChannelCount: 40,
},
Board{
Bus: 1,
Address: 0x29,
ID: "79690164-214f-41b0-93f9-e910dd54f323",
Name: "bordo1",
Type: BoardTypeI2CGPIO,
ChannelCount: 8,
},
Board{
Bus: 1,
Address: 0x27,
ID: "93f446d8-59e4-4abd-8bf7-e31cd80bc713",
Name: "bordo2",
Type: BoardTypeI2CADC,
ChannelCount: 4,
},
},
Outlets: map[(uint)]Outlet{
0: Outlet{
Name: "uscita 0",
Location: "port 1 dx",
HasPowerMeter: true,
Command: Boardlink{
BoardID: "79690164-214f-41b0-93f9-e910dd54f323",
Channel: 0,
},
PowerMeter: Boardlink{
BoardID: "93f446d8-59e4-4abd-8bf7-e31cd80bc713",
Channel: 0,
},
},
1: Outlet{
Name: "uscita 1",
Location: "port 1 sx",
HasPowerMeter: true,
Command: Boardlink{
BoardID: "79690164-214f-41b0-93f9-e910dd54f323",
Channel: 1,
},
PowerMeter: Boardlink{
BoardID: "93f446d8-59e4-4abd-8bf7-e31cd80bc713",
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
t/mqtt.go Normal file
View 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
t/outlet.go Normal file
View 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()
}

270
t/t.go
View File

@@ -1,270 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
// Board def
type Board struct {
ChannelCount uint `json:"channelcount"`
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Bus uint `json:"bus"`
Address uint `json:"address"`
}
// 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 {
return nil
}
// PowerOFF def
func (b Board) PowerOFF(num uint) error {
return nil
}
// PowerToggle def
func (b Board) PowerToggle(num uint) error {
return nil
}
// Boardlink def
type Boardlink struct {
BoardID string `json:"boardid"`
Channel uint `json:"channel"`
board Board
}
// Board def
func (bl Boardlink) Board() Board {
fmt.Printf("\n boardlink: \n\n")
if bl.board.ID == "" {
fmt.Printf("\n board nil: \n\n")
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 {
return bl.Board().PowerON(bl.Channel)
}
// PowerOFF def
func (bl Boardlink) PowerOFF() error {
return bl.Board().PowerOFF(bl.Channel)
}
// PowerToggle def
func (bl Boardlink) PowerToggle() error {
return bl.Board().PowerToggle(bl.Channel)
}
// Outlet def
type Outlet struct {
Name string `json:"name"`
Location string `json:"location"`
HasPowerMeter bool `json:"haspowermeter"`
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()
}
// 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"`
}
// Configuration def
type Configuration struct {
Hostname string `json:"hostname"`
Outlets map[(uint)]Outlet `json:"outlets"`
Boards []Board `json:"boards"`
MQTT MQTTConfig `json:"mqtt"`
}
func loadConfig(filename string) (Configuration, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return Configuration{}, err
}
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)
}
// TheConfig def
var TheConfig Configuration
func main() {
var err error
var c1 Configuration
c1 = createMockConfig()
for n, br := range c1.Boards {
fmt.Printf("A - Board %v (%v)\n", n, br.Type)
}
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)
}
fmt.Printf("\n\nmqtt: %v\n\n", TheConfig.MQTT)
TheConfig.Outlets[0].PowerON()
}
func createMockConfig() Configuration {
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{
Board{
ID: "6561df75-bf93-43f5-82ac-9b3dda081961",
Name: "Internal GPIO",
Type: "GPIO",
ChannelCount: 40,
},
Board{
Bus: 1,
Address: 0x29,
ID: "79690164-214f-41b0-93f9-e910dd54f323",
Name: "bordo1",
Type: "I2C-GPIO",
ChannelCount: 8,
},
Board{
Bus: 1,
Address: 0x27,
ID: "93f446d8-59e4-4abd-8bf7-e31cd80bc713",
Name: "bordo2",
Type: "I2C-ADC",
ChannelCount: 4,
},
},
Outlets: map[(uint)]Outlet{
0: Outlet{
Name: "uscita 0",
Location: "port 1 dx",
HasPowerMeter: true,
Command: Boardlink{
BoardID: "79690164-214f-41b0-93f9-e910dd54f323",
Channel: 0,
},
PowerMeter: Boardlink{
BoardID: "93f446d8-59e4-4abd-8bf7-e31cd80bc713",
Channel: 0,
},
},
1: Outlet{
Name: "uscita 1",
Location: "port 1 sx",
HasPowerMeter: true,
Command: Boardlink{
BoardID: "79690164-214f-41b0-93f9-e910dd54f323",
Channel: 1,
},
PowerMeter: Boardlink{
BoardID: "93f446d8-59e4-4abd-8bf7-e31cd80bc713",
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: "6561df75-bf93-43f5-82ac-9b3dda081961",
Channel: 21,
},
},
4: Outlet{
Name: "uscita 5v 2",
Location: "usb avanti 2",
HasPowerMeter: false,
Command: Boardlink{
BoardID: "6561df75-bf93-43f5-82ac-9b3dda081961",
Channel: 22,
},
},
},
}
}

29
t/web.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"log"
"net/http"
"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("/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
t/webjson.go Normal file
View 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",
})
}

14
t/webpages.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "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.
}