package main import ( "bufio" _ "expvar" "flag" "fmt" "log" "math/rand" "net/http" "os" "os/signal" "regexp" "syscall" "time" mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/tarm/serial" ) const ( wait_TellerDemandDal uint32 = iota wait_TellerDemandPiek wait_TellerSupplyDal wait_TellerSupplyPiek wait_Tariff wait_DemandWatt wait_SupplyWatt wait_Volt ) // selection of data that we're interested in var re_Teller1Demand = regexp.MustCompile(`^1-0:1\.8\.1\(([\d\.]+)\*kWh\)`) var re_Teller2Demand = regexp.MustCompile(`^1-0:1\.8\.2\(([\d\.]+)\*kWh\)`) var re_Teller1Supply = regexp.MustCompile(`^1-0:2\.8\.1\(([\d\.]+)\*kWh\)`) var re_Teller2Supply = regexp.MustCompile(`^1-0:2\.8\.2\(([\d\.]+)\*kWh\)`) var re_Tariff = regexp.MustCompile(`^0-0:96\.14\.0\(([\d]+)\)`) var re_DemandWatt = regexp.MustCompile(`^1-0:1\.7\.0\(([\d\.]+)\*kW\)`) var re_SupplyWatt = regexp.MustCompile(`^1-0:2\.7\.0\(([\d\.]+)\*kW\)`) var re_Volt = regexp.MustCompile(`^1-0:32\.7\.0\(([\d\.]+)\*V\)`) var state = wait_TellerDemandDal var heartbeat uint64 = 1 var currentDemandWatt string = "" var currentSupplyWatt string = "" var currentVolt string = "" var currentTeller1 string = "" var currentTeller2 string = "" var currentSupply1 string = "" var currentSupply2 string = "" var currentTariff string = "" // MQTT_SERVER = "tcp://192.168.178.8:1883" var broker = flag.String("mqtt", os.Getenv("MQTT_SERVER"), "the broker protocol://ip:port; protocol can be 'ws' or 'tcp'.") var prefix = flag.String("pref", "/pm", "the 'home' prefix to the smr5 p1 message updates.") var sendRaw = flag.Bool("raw", false, "send all P1 lines to /smr5/raw.") var sendheartbeat = flag.Bool("hb", false, "send heartbeat.") func main() { flag.Parse() rand.Seed(time.Now().UTC().UnixNano()) opts := mqtt.NewClientOptions() opts.AddBroker(*broker) opts.SetClientID(string(20000 + rand.Intn(10000))) opts.SetCleanSession(true) opts = opts.SetOnConnectHandler(func(client mqtt.Client) { log.Printf("MQTT connected to: " + *broker + ".\n") }) opts = opts.SetConnectionLostHandler(func(client mqtt.Client, err error) { log.Printf("MQTT connection lost: %v.\n", err) }) // "expvar" memory stats server at: http://:5160/debug/vars go func() { http.ListenAndServe(":5160", nil) }() // wait a while; seems mosquitto needs time to get ready after startup time.Sleep(time.Second) client := mqtt.NewClient(opts) if token := client.Connect(); token.Wait() && token.Error() != nil { panic(token.Error()) } go readSerial(client) go tellerUpdate(client) sig := make(chan os.Signal) signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) s := <-sig fmt.Printf("\x1b[2KSignal: %v\n", s) client.Disconnect(250) } // SMR5_SERIAL = /dev/ttyUSB0 func readSerial(client mqtt.Client) { sportstring := os.Getenv("SMR5_SERIAL") log.Printf("Serial connection to: %s\n",sportstring) c := &serial.Config{Name: sportstring, Baud: 115200, Size: 8, Parity: serial.ParityNone, StopBits: serial.Stop1} sport, err := serial.OpenPort(c) if err != nil { log.Fatal(err) } defer sport.Close() reader := bufio.NewReader(sport) for { s, _ := reader.ReadString('\n') handleStatement(client, s) } } func tellerUpdate(client mqtt.Client) { for { time.Sleep(60 * time.Second) client.Publish(*prefix+"/smr5/tariff", 0, false, currentTariff) client.Publish(*prefix+"/smr5/kwh_demand_dal", 0, false, currentTeller1) client.Publish(*prefix+"/smr5/kwh_demand_piek", 0, false, currentTeller2) client.Publish(*prefix+"/smr5/kwh_supply_dal", 0, false, currentSupply1) client.Publish(*prefix+"/smr5/kwh_supply_piek", 0, false, currentSupply2) } } func handleStatement(client mqtt.Client, message string) { if *sendRaw { client.Publish(*prefix+"/smr5/raw", 0, false, message) } switch state { case wait_TellerDemandDal: found := re_Teller1Demand.FindStringSubmatch(message) if found != nil { currentTeller1 = found[1] state = wait_TellerDemandPiek } case wait_TellerDemandPiek: found := re_Teller2Demand.FindStringSubmatch(message) if found != nil { currentTeller2 = found[1] state = wait_TellerSupplyDal } case wait_TellerSupplyDal: found := re_Teller1Supply.FindStringSubmatch(message) if found != nil { currentSupply1 = found[1] state = wait_TellerSupplyPiek } case wait_TellerSupplyPiek: found := re_Teller2Supply.FindStringSubmatch(message) if found != nil { currentSupply2 = found[1] state = wait_Tariff } case wait_Tariff: found := re_Tariff.FindStringSubmatch(message) if found != nil { currentTariff = found[1] state = wait_DemandWatt } case wait_DemandWatt: found := re_DemandWatt.FindStringSubmatch(message) if found != nil { if currentDemandWatt != found[1] { client.Publish(*prefix+"/smr5/watt_demand", 0, false, found[1]) client.Publish(*prefix+"/smr5/watt_supply", 0, false, "00.000") currentDemandWatt = found[1] } state = wait_SupplyWatt } case wait_SupplyWatt: found := re_SupplyWatt.FindStringSubmatch(message) if found != nil { if currentSupplyWatt != found[1] { client.Publish(*prefix+"/smr5/watt_demand", 0, false, "00.000") client.Publish(*prefix+"/smr5/watt_supply", 0, false, found[1]) currentSupplyWatt = found[1] } state = wait_Volt } case wait_Volt: found := re_Volt.FindStringSubmatch(message) if found != nil { if *sendheartbeat { heartbeat = heartbeat + 1 client.Publish(*prefix+"/smr5/heartbeat", 0, false, fmt.Sprintf("%d", heartbeat)) } if currentVolt != found[1] { client.Publish(*prefix+"/smr5/volt", 0, false, found[1]) currentVolt = found[1] } state = wait_TellerDemandDal } } } // SMR5 'telegram': // # /XMX5LGBBLB2410018242 // # 1-3:0.2.8(50) // # 0-0:1.0.0(171229103442W) // # 0-0:96.1.1(4530303335303033373439313036343136) // # 1-0:1.8.1(001268.417*kWh) # afgenomen stroom daltarief // # 1-0:1.8.2(001258.762*kWh) # afgenomen stroom piektarief // # 1-0:2.8.1(000000.000*kWh) # gesaldeerde stroom daltarief // # 1-0:2.8.2(000000.000*kWh) # gesaldeerde stroom piektarief // # 0-0:96.14.0(0002) # actueel tarief: piek = 2, dal = 1 // # 1-0:1.7.0(00.268*kW) # actueel verbruik // # 1-0:2.7.0(00.000*kW) # actuele saldering // # 0-0:96.7.21(00003) // # 0-0:96.7.9(00000) // # 1-0:99.97.0(0)(0-0:96.7.19) // # 1-0:32.32.0(00001) // # 1-0:32.36.0(00000) // # 0-0:96.13.0() // # 1-0:32.7.0(231.0*V) # actueel voltage // # 1-0:31.7.0(001*A) // # 1-0:21.7.0(00.268*kW) // # 1-0:22.7.0(00.000*kW) // # !8A43