initial import
This commit is contained in:
20
src/config.go
Normal file
20
src/config.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
viper.SetDefault("hostname", "openpdu")
|
||||
}
|
||||
91
src/display.go
Normal file
91
src/display.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/basicfont"
|
||||
"golang.org/x/image/math/fixed"
|
||||
|
||||
// "periph.io/x/periph/conn/i2c/i2creg"
|
||||
"periph.io/x/periph/devices/ssd1306"
|
||||
)
|
||||
|
||||
func addLabel(img *image.RGBA, x, y int, label string) {
|
||||
col := color.RGBA{200, 100, 0, 255}
|
||||
point := fixed.Point26_6{fixed.Int26_6(x * 64), fixed.Int26_6(y * 64)}
|
||||
|
||||
d := &font.Drawer{
|
||||
Dst: img,
|
||||
Src: image.NewUniform(col),
|
||||
Face: basicfont.Face7x13,
|
||||
Dot: point,
|
||||
}
|
||||
d.DrawString(label)
|
||||
}
|
||||
|
||||
func getIPs() map[string]string {
|
||||
out := map[string]string{}
|
||||
ifaces, _ := net.Interfaces()
|
||||
// handle err
|
||||
for _, i := range ifaces {
|
||||
if i.Name == "lo" {
|
||||
continue
|
||||
}
|
||||
addrs, _ := i.Addrs()
|
||||
// handle err
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
// process IP address
|
||||
if ip.To4() == nil {
|
||||
continue
|
||||
}
|
||||
if len(out) < 1 {
|
||||
out[i.Name] = ip.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func disp() {
|
||||
|
||||
opts := ssd1306.Opts{
|
||||
W: 128,
|
||||
H: 32,
|
||||
Rotated: false,
|
||||
Sequential: true,
|
||||
SwapTopBottom: false,
|
||||
}
|
||||
|
||||
// Open a handle to a ssd1306 connected on the I²C bus:
|
||||
dev, err := ssd1306.NewI2C(i2cbus, &opts)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, 128, 32))
|
||||
ips := getIPs()
|
||||
lineHeight := 12
|
||||
// posX := 10
|
||||
posX := 12
|
||||
posY := lineHeight
|
||||
// for name, ip := range ips {
|
||||
for _, ip := range ips {
|
||||
// addLabel(img, posX, posY, name)
|
||||
// posY += lineHeight
|
||||
addLabel(img, posX, posY, ip)
|
||||
posY += lineHeight
|
||||
}
|
||||
dev.Draw(img.Bounds(), img, image.Point{})
|
||||
|
||||
}
|
||||
118
src/i2c.go
Normal file
118
src/i2c.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"periph.io/x/periph/conn/i2c"
|
||||
"periph.io/x/periph/conn/i2c/i2creg"
|
||||
"periph.io/x/periph/host"
|
||||
)
|
||||
|
||||
// I2CBoard bla
|
||||
type I2CBoard struct {
|
||||
i2cdev i2c.Dev
|
||||
channels uint
|
||||
data []byte
|
||||
inverted bool
|
||||
}
|
||||
|
||||
// MyBoard bla
|
||||
var MyBoard = I2CBoard{
|
||||
channels: 8,
|
||||
inverted: true,
|
||||
}
|
||||
|
||||
var i2cbus i2c.Bus
|
||||
|
||||
func (b I2CBoard) channelStatus(ch uint) (bool, error) {
|
||||
if b.channels <= 0 {
|
||||
return false, errors.New("Board without channels")
|
||||
}
|
||||
if ch >= b.channels {
|
||||
return false, errors.New("Invalid channel")
|
||||
}
|
||||
write := []byte{0x0A}
|
||||
err := b.i2cdev.Tx(write, b.data)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
byteToConsider := ch / b.channels
|
||||
value := (b.data[byteToConsider] >> (ch % 8) & 1) == 1
|
||||
if b.inverted {
|
||||
value = !value
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (b I2CBoard) channelToggle(ch uint) error {
|
||||
var mask byte
|
||||
|
||||
if b.channels <= 0 {
|
||||
return errors.New("Board without channels")
|
||||
}
|
||||
if ch >= b.channels {
|
||||
return errors.New("Invalid channel")
|
||||
}
|
||||
|
||||
// if b.data == nil {
|
||||
_, _ = b.channelStatus(ch)
|
||||
// }
|
||||
|
||||
byteToConsider := ch / b.channels
|
||||
mask = 1 << (ch % 8)
|
||||
v := b.data[byteToConsider]
|
||||
v ^= mask
|
||||
b.data[byteToConsider] = v
|
||||
|
||||
write := append([]byte{0x09}, b.data...)
|
||||
_, err := b.i2cdev.Write(write)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initI2C() {
|
||||
var err error
|
||||
// Make sure periph is initialized.
|
||||
if _, err = host.Init(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use i2creg I²C bus registry to find the first available I²C bus.
|
||||
// i2cbus, err = i2creg.Open("/dev/i2c-1")
|
||||
i2cbus, err = i2creg.Open("")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// defer i2cbus.Close()
|
||||
|
||||
// bus 0
|
||||
// Dev is a valid conn.Conn.
|
||||
mydevice := &i2c.Dev{Addr: 0x27, Bus: i2cbus}
|
||||
|
||||
// Send a command 0x10 and expect a 5 bytes reply.
|
||||
// write := []byte{0x10}
|
||||
write := []byte{0x0, 0x0}
|
||||
// read := make([]byte, 5)
|
||||
// if err := d.Tx(write, read); err != nil {
|
||||
if _, err := mydevice.Write(write); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
42
src/main.go
Normal file
42
src/main.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
// "encoding/json"
|
||||
"log"
|
||||
|
||||
// "periph.io/x/periph"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Dictionary aaa
|
||||
type Dictionary map[string]interface{}
|
||||
|
||||
func main() {
|
||||
|
||||
readConfig()
|
||||
|
||||
nam := viper.Get("hostname")
|
||||
log.Printf("hostname: %v\n", nam)
|
||||
|
||||
initI2C()
|
||||
|
||||
go disp()
|
||||
|
||||
startServer()
|
||||
}
|
||||
|
||||
// https://github.com/ColorlibHQ/AdminLTE/archive/v2.4.17.tar.gz
|
||||
|
||||
/* TODO
|
||||
|
||||
- config reset gpio
|
||||
- classi per board
|
||||
- classi per outlet
|
||||
- fai funzionare toggle
|
||||
- scan i2c
|
||||
- impostazioni log
|
||||
- impostazioni mqtt
|
||||
|
||||
|
||||
*/
|
||||
33
src/mqtt.go
Normal file
33
src/mqtt.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// set the protocol, ip and port of the broker.
|
||||
opts := MQTT.NewClientOptions().AddBroker("tcp://localhost:1883")
|
||||
|
||||
// set the id to the client.
|
||||
opts.SetClientID("Device-pub")
|
||||
|
||||
// create a new client.
|
||||
c := MQTT.NewClient(opts)
|
||||
token := c.Connect()
|
||||
token.Wait()
|
||||
if token.Error() != nil {
|
||||
fmt.Println(token.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
message := "hello this is the trial message"
|
||||
c.Publish("some_topic", 0, false, message)
|
||||
|
||||
//c.Subscribe("some_topic", 0, nil);
|
||||
c.Disconnect(250)
|
||||
}
|
||||
|
||||
// https://girishjoshi.io/post/golang-paho-mqtt/
|
||||
24
src/syslog.go
Normal file
24
src/syslog.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
syslog "github.com/RackSec/srslog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
w, err := syslog.Dial("", "", syslog.LOG_ERR, "testtag")
|
||||
if err != nil {
|
||||
log.Fatal("failed to connect to syslog:", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
w.Alert("this is an alert")
|
||||
w.Crit("this is critical")
|
||||
w.Err("this is an error")
|
||||
w.Warning("this is a warning")
|
||||
w.Notice("this is a notice")
|
||||
w.Info("this is info")
|
||||
w.Debug("this is debug")
|
||||
w.Write([]byte("these are some bytes"))
|
||||
}
|
||||
113
src/webui.go
Normal file
113
src/webui.go
Normal file
@@ -0,0 +1,113 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user