From b78efe951c3f3bbf89f366f6e8e305d3a835f1e8 Mon Sep 17 00:00:00 2001 From: Bernhard Tittelbach Date: Fri, 23 Aug 2013 04:23:24 +0000 Subject: [PATCH] r3 xmpp bot --- r3-netstatus/main.go | 172 ++++++++++++++++ r3-netstatus/make_deploy.zsh | 4 + r3-netstatus/r3xmppbot/r3xmppbot.go | 367 +++++++++++++++++++++++++++++++++++ r3-netstatus/spaceapi/spaceapi.go | 242 +++++++++++++++++++++++ 4 files changed, 785 insertions(+) create mode 100644 r3-netstatus/main.go create mode 100644 r3-netstatus/make_deploy.zsh create mode 100644 r3-netstatus/r3xmppbot/r3xmppbot.go create mode 100644 r3-netstatus/spaceapi/spaceapi.go diff --git a/r3-netstatus/main.go b/r3-netstatus/main.go new file mode 100644 index 0000000..77dd32a --- /dev/null +++ b/r3-netstatus/main.go @@ -0,0 +1,172 @@ +package main + +import ( + "./spaceapi" + "./r3xmppbot" + "bufio" + "fmt" + "net" + "net/http" + "net/url" + "regexp" + "time" + "strconv" +) + +type SpaceState struct { + present bool + buttonpress_until int64 + door_locked bool + door_shut bool +} + +var ( + re_presence_ *regexp.Regexp = regexp.MustCompile("Presence: (yes|no)(?:, (opened|closed), (.+))?") + re_status_ *regexp.Regexp = regexp.MustCompile("Status: (closed|opened), (opening|waiting|closing|idle), (ajar|shut).*") + re_button_ *regexp.Regexp = regexp.MustCompile("PanicButton|button\\d?") + re_temp_ *regexp.Regexp = regexp.MustCompile("temp0: (\\d+\\.\\d+)") + re_photo_ *regexp.Regexp = regexp.MustCompile("photo0: (\\d+)") + re_querystresc_ *regexp.Regexp = regexp.MustCompile("[^\x30-\x39\x41-\x7E]") + spaceapidata spaceapi.SpaceInfo = spaceapi.NewSpaceInfo("realraum", "http://realraum.at", "http://realraum.at/logo-red_250x250.png", "http://realraum.at/logo-re_open_100x100.png", "http://realraum.at/logo-re_empty_100x100.png",47.065779129, 15.442322614).AddSpaceAddress("Jakoministr. 16 ground level left, 8010 Graz, Austria") + statusstate *SpaceState = new(SpaceState) + xmpp_presence_events_chan_ chan interface{} +) + +//------- + +func updateStatusString() { + var spacestatus string + if statusstate.present { + if statusstate.buttonpress_until > time.Now().Unix() { + spacestatus = "Panic! Present&Bored" + } else { + spacestatus = "Leute Anwesend" + } + } else { + spacestatus = "Keiner Da" + } + spaceapidata.SetStatus(statusstate.present, spacestatus) +} + +func publishStateToWeb() { + updateStatusString() + jsondata_b, err := spaceapidata.MakeJSON() + if err != nil { + fmt.Println("Error:", err) + return + } + //jsondata_b := re_querystresc_.ReplaceAllFunc(jsondata_b, func(in []byte) []byte { + // out := make([]byte, 4) + // out[0] = '%' + // copy(out[1:], []byte(strconv.FormatInt(int64(in[0]), 16))) + // return out + //}) + jsondata := url.QueryEscape(string(jsondata_b)) + resp, err := http.Get("http://www.realraum.at/cgi/status.cgi?pass=jako16&set=" + jsondata) + if err != nil { + fmt.Println("Error publishing realraum info", err) + return + } + defer resp.Body.Close() +} + +func parseSocketInputLine(line string) { + match_presence := re_presence_.FindStringSubmatch(line) + match_status := re_status_.FindStringSubmatch(line) + match_button := re_button_.FindStringSubmatch(line) + match_temp := re_temp_.FindStringSubmatch(line) + match_photo := re_photo_.FindStringSubmatch(line) + + if match_presence != nil { + statusstate.present = (match_presence[1] == "yes") + statusstate.door_locked = (match_presence[2] == "closed") + //spaceapidata.MergeInSensor(spaceapi.MakeDoorLockSensor("Torwaechter", "Front Door", match_presence[2] == "closed")) + spaceapidata.MergeInSensor(spaceapi.MakeDoorLockSensor("TorwaechterLock", "Türschloß", statusstate.door_locked)) + publishStateToWeb() + xmpp_presence_events_chan_ <- r3xmppbot.PresenceEvent{statusstate.present, statusstate.door_locked, statusstate.door_shut} + } else if match_status != nil { + statusstate.door_locked = (match_status[1] == "closed") + statusstate.door_shut = (match_status[3] == "shut") + spaceapidata.MergeInSensor(spaceapi.MakeDoorLockSensor("TorwaechterLock", "Türschloß", statusstate.door_locked)) + spaceapidata.MergeInSensor(spaceapi.MakeDoorLockSensor("TorwaechterAjarSensor", "Türkontakt", statusstate.door_shut)) + //spaceapidata.MergeInSensor(spaceapi.MakeDoorAjarSensor("Torwaechter", "Front Door", match_presence[3] == "shut")) + publishStateToWeb() + xmpp_presence_events_chan_ <- r3xmppbot.PresenceEvent{statusstate.present, statusstate.door_locked, statusstate.door_shut} + } else if match_button != nil { + statusstate.buttonpress_until = time.Now().Unix() + 3600 + spaceapidata.AddSpaceEvent("PanicButton", "check-in", "The button has been pressed") + publishStateToWeb() + xmpp_presence_events_chan_ <- "The button has been pressed ! Propably someone is bored and need company ! ;-)" + } else if match_temp != nil { + newtemp, err := strconv.ParseFloat((match_temp[1]), 32) + if err == nil { + spaceapidata.MergeInSensor(spaceapi.MakeTempCSensor("Temp0","Decke",newtemp)) + } + } else if match_photo != nil { + newphoto, err := strconv.ParseInt(match_photo[1], 10, 32) + if err == nil { + spaceapidata.MergeInSensor(spaceapi.MakeIlluminationSensor("Photodiode","Decke","1024V/5V",newphoto)) + } + } +} + +func readFromUSocket(path string, c chan string) { +ReOpenSocket: + for { + presence_socket, err := net.Dial("unix", path) + if err != nil { + //Waiting on Socket + time.Sleep(5 * time.Second) + continue ReOpenSocket + } + presence_reader := bufio.NewReader(presence_socket) + for { + line, err := presence_reader.ReadString('\n') + if err != nil { + //Socket closed + presence_socket.Close() + continue ReOpenSocket + } + c <- line + } + } +} + +func main() { + spaceapidata.AddSpaceFeed("calendar", "http://grical.realraum.at/s/?query=!realraum&view=rss") + spaceapidata.AddSpaceFeed("blog", "https://plus.google.com/113737596421797426873") + spaceapidata.AddSpaceFeed("wiki", "http://realraum.at/wiki") + spaceapidata.AddSpaceContactInfo("+43780700888524", "irc://irc.oftc.net/#realraum", "realraum@realraum.at", "realraum@realraum.at", "realraum@realraum.at", "vorstand@realraum.at") + + var err error + var bot *r3xmppbot.XmppBot + bot, xmpp_presence_events_chan_, err = r3xmppbot.NewStartedBot("realrauminfo@realraum.at/Tuer", "d7ynC6Dg", "r3alraumOLGAXMPPInfos", true) + if err != nil { + fmt.Println(err) + return + } + defer bot.StopBot() + + //~ presence_events <- PresenceEvent{true, true, true} + //~ presence_events <- PresenceEvent{true, true, false} + //~ presence_events <- PresenceEvent{true, false, false} + + + eventqueue := make(chan string) + defer close(eventqueue) + + ticker := time.NewTicker(time.Duration(7) * time.Minute) + go readFromUSocket("/var/run/tuer/presence.socket", eventqueue) + for { + select { + case e := <-eventqueue: + parseSocketInputLine(e) + case <-ticker.C: + publishStateToWeb() + } + } +} + +/* TODO: +* Read config from an .ini file + */ diff --git a/r3-netstatus/make_deploy.zsh b/r3-netstatus/make_deploy.zsh new file mode 100644 index 0000000..5e2523f --- /dev/null +++ b/r3-netstatus/make_deploy.zsh @@ -0,0 +1,4 @@ +#!/bin/zsh +go-linux-386 build -ldflags "-s -d" +strip ${PWD:t} +rsync -v ${PWD:t} wuzzler.realraum.at:/flash/tuer/ \ No newline at end of file diff --git a/r3-netstatus/r3xmppbot/r3xmppbot.go b/r3-netstatus/r3xmppbot/r3xmppbot.go new file mode 100644 index 0000000..465ee95 --- /dev/null +++ b/r3-netstatus/r3xmppbot/r3xmppbot.go @@ -0,0 +1,367 @@ +package r3xmppbot + +import ( + xmpp "code.google.com/p/goexmpp" + "fmt" + "log" + "crypto/tls" + "os" + "time" + "encoding/json" + "path" +) + +//~ type StdLogger struct { +//~ } + +//~ func (s *StdLogger) Log(v ...interface{}) { + //~ log.Println(v...) +//~ } + +//~ func (s *StdLogger) Logf(fmt string, v ...interface{}) { + //~ log.Printf(fmt, v...) +//~ } + +func (botdata *XmppBot) makeXMPPMessage(to, message string, subject interface{}) *xmpp.Message { + xmppmsgheader := xmpp.Header{To: to, + From: botdata.my_jid_, + Id: <-xmpp.Id, + Type: "chat", + Lang: "", + Innerxml: "", + Error: nil, + Nested: make([]interface{},0)} + msgsubject := xmpp.Generic{} + if subject != nil { + msgsubject.Chardata = subject.(string) + } + return &xmpp.Message{Header: xmppmsgheader , Subject: &msgsubject, Body: &xmpp.Generic{Chardata:message}, Thread: &xmpp.Generic{}} +} + +func (botdata *XmppBot) makeXMPPPresence(to, ptype string) *xmpp.Presence { + xmppmsgheader := xmpp.Header{To: to, + From: botdata.my_jid_, + Id: <-xmpp.Id, + Type: ptype, + Lang: "", + Innerxml: "", + Error: nil, + Nested: make([]interface{},0)} + return &xmpp.Presence{Header: xmppmsgheader} +} + +type R3JIDDesire int + +const ( + R3NoChange R3JIDDesire = -1 + R3NoInfo R3JIDDesire = iota // ignore first value by assigning to blank identifier + R3NoOfflineInfo + R3AllInfo + R3DebugInfo +) + +type JidData struct { + Online bool + Wants R3JIDDesire +} + +type JabberEvent struct { + JID string + Online bool + Wants R3JIDDesire +} + +type PresenceEvent struct { + Present bool + DoorLock bool + DoorAjar bool +} + +type RealraumXmppNotifierConfig map[string]JidData + +type XmppBot struct { + jid_lastauthtime_ map[string]int64 + realraum_jids_ RealraumXmppNotifierConfig + password_ string + auth_cmd_ string + my_jid_ string + auth_timeout_ int64 + config_file_ string + my_login_password_ string + xmppclient_ *xmpp.Client + presence_events_ *chan interface{} +} + + +func (data RealraumXmppNotifierConfig) saveTo(filepath string) () { + fh, err := os.Create(filepath) + if err != nil { + log.Println(err) + return + } + defer fh.Close() + enc := json.NewEncoder(fh) + if err = enc.Encode(&data); err != nil { + log.Println(err) + return + } +} + +func (data RealraumXmppNotifierConfig) loadFrom(filepath string) () { + fh, err := os.Open(filepath) + if err != nil { + log.Println(err) + return + } + defer fh.Close() + dec := json.NewDecoder(fh) + if err = dec.Decode(&data); err != nil { + log.Println(err) + return + } + for to, jiddata := range data { + jiddata.Online = false + data[to]=jiddata + } +} + + +func init() { + //~ logger := &StdLogger{} + //~ xmpp.Debug = logger + //~ xmpp.Info = logger + //~ xmpp.Warn = logger +} + +func (botdata *XmppBot) handleEventsforXMPP(xmppout chan <- xmpp.Stanza, presence_events <- chan interface{}, jabber_events <- chan JabberEvent) { + var msg, presence_str, lock_str, ajar_str string + last_presence := false + var debug_msg bool + + for { + debug_msg = false + select { + case pe := <-presence_events: + switch pec := pe.(type) { + case xmpp.Stanza: + xmppout <- pec + continue + case string: + msg = pec + case PresenceEvent: + if pec.Present { + presence_str = "Somebody is present !" + } else { + presence_str = "Everybody left." + } + if pec.DoorLock { + lock_str = "locked" + } else { + lock_str = "unlocked" + } + if pec.DoorAjar { + ajar_str = "ajar" + } else { + ajar_str = "shut" + } + msg = fmt.Sprintf("%s (Door is %s and %s)", presence_str, lock_str, ajar_str) + if last_presence == pec.Present { + debug_msg = true + } else { + last_presence = pec.Present + } + default: + break + } + + for to, jiddata := range botdata.realraum_jids_ { + if debug_msg && jiddata.Wants < R3DebugInfo { + continue + } + if (jiddata.Wants == R3NoOfflineInfo && jiddata.Online) || jiddata.Wants > R3NoOfflineInfo { + xmppout <- botdata.makeXMPPMessage(to, msg, nil) + } //else { + //~ fmt.Println("Not sending to ", to, jiddata) + //~ } + } + + case je := <-jabber_events: + jid_data, jid_in_map := botdata.realraum_jids_[je.JID] + if jid_in_map { + jid_data.Online = je.Online + if je.Wants > R3NoChange { + jid_data.Wants = je.Wants + } + botdata.realraum_jids_[je.JID] = jid_data + botdata.realraum_jids_.saveTo(botdata.config_file_) + } else if je.Wants > R3NoChange { + botdata.realraum_jids_[je.JID] = JidData{je.Online, je.Wants} + botdata.realraum_jids_.saveTo(botdata.config_file_) + } + } + } +} + +func removeJIDResource(jid string) string { + var jidjid xmpp.JID + jidjid.Set(jid) + jidjid.Resource = "" + return jidjid.String() +} + +func (botdata *XmppBot) isAuthenticated(jid string) bool { + authtime, in_map := botdata.jid_lastauthtime_[jid] + //~ log.Println("isAuthenticated", in_map, authtime, time.Now().Unix(), auth_timeout_, time.Now().Unix() - authtime > auth_timeout_) + return in_map && time.Now().Unix() - authtime < botdata.auth_timeout_ +} + +const help_text_ string = "\nauth \n .... enables you to use the other commands\non\n .... you will be notified of r3 status changes\noff\n .... you will no longer recieve notifications\non_while_offline\n .... you will be notified of r3 status changes even if you are offline" + +//~ var re_msg_auth_ *regexp.Regexp = regexp.MustCompile("auth\s+(\S+)") + +func (botdata *XmppBot) handleIncomingMessageDialog(inmsg xmpp.Message, xmppout chan<- xmpp.Stanza, jabber_events chan JabberEvent) { + if inmsg.Body == nil || inmsg.GetHeader() == nil { + return + } + bodytext :=inmsg.Body.Chardata + //~ log.Println("Message Body:", bodytext) + if botdata.isAuthenticated(inmsg.GetHeader().From) { + switch bodytext { + case "on": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3NoOfflineInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Receive r3 information while online" , "Your New Status") + case "off": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3NoInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Do not receive r3 information" , "Your New Status") + case "on_while_offline": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3AllInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Receive r3 information even while offline" , "Your New Status") + case "debug": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3DebugInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Debug mode enabled" , "Your New Status") + case "bye", "Bye", "quit", "logout": + botdata.jid_lastauthtime_[inmsg.GetHeader().From] = 0 + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Bye Bye !" ,nil) + default: + //~ auth_match = re_msg_auth_.FindStringSubmatch(inmsg.Body.Chardata) + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, help_text_, "Available Commands") + } + } else { + switch bodytext { + case "Hilfe","hilfe","help","Help","?","hallo","Hallo","Yes","yes","ja","ja bitte","bitte","sowieso": + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, help_text_, "Available Commands") + case botdata.auth_cmd_: + botdata.jid_lastauthtime_[inmsg.GetHeader().From] = time.Now().Unix() + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "You are now authorized to use commands" , "Authorized") + default: + //~ auth_match = re_msg_auth_.FindStringSubmatch(inmsg.Body.Chardata) + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "A nice day to you too !\nDo you need \"help\" ?", nil) + } + } +} + +func (botdata *XmppBot) handleIncomingXMPPStanzas(xmppin <- chan xmpp.Stanza, xmppout chan<- xmpp.Stanza, jabber_events chan JabberEvent) { + var incoming_stanza interface{} + for incoming_stanza = range xmppin { + switch stanza := incoming_stanza.(type) { + case *xmpp.Message: + botdata.handleIncomingMessageDialog(*stanza, xmppout, jabber_events) + case *xmpp.Presence: + if stanza.GetHeader() == nil { + continue + } + if stanza.GetHeader().Type == "subscribe" { + xmppout <- botdata.makeXMPPPresence(stanza.GetHeader().From, "subscribed") + } + jabber_events <- JabberEvent{stanza.GetHeader().From, stanza.GetHeader().Type != "unavailable", R3NoChange} + case *xmpp.Iq: + if stanza.GetHeader() == nil { + continue + } + } + } +} + +var default_state_save_dir_ string = "/flash/var/lib/r3netstatus/" + +func NewStartedBot(loginjid, loginpwd, password string, insecuretls bool) (*XmppBot, chan interface{}, error) { + var err error + botdata := new(XmppBot) + + botdata.realraum_jids_ = make(map[string]JidData, 1) + botdata.jid_lastauthtime_ = make(map[string]int64,1) + botdata.auth_cmd_ = "auth " + password + botdata.my_jid_ = loginjid + botdata.my_login_password_ = loginpwd + botdata.auth_timeout_ = 1200 + + botdata.config_file_ = path.Join(default_state_save_dir_, "r3xmpp."+removeJIDResource(loginjid)+".json") + + //~ log.Println(botdata.config_file_) + + //~ logger := &StdLogger{} + //~ xmpp.Debug = logger + //~ xmpp.Info = logger + //~ xmpp.Warn = logger + + xmpp.TlsConfig = tls.Config{InsecureSkipVerify: insecuretls} + botdata.realraum_jids_.loadFrom(botdata.config_file_) + + client_jid := new(xmpp.JID) + client_jid.Set(botdata.my_jid_) + botdata.xmppclient_, err = xmpp.NewClient(client_jid, botdata.my_login_password_, nil) + if err != nil { + log.Println("Error connecting to xmpp server", err) + return nil, nil, err + } + + err = botdata.xmppclient_.StartSession(false, &xmpp.Presence{}) + if err != nil { + log.Println("'Error StartSession:", err) + return nil, nil, err + } + + presence_events := make(chan interface{},1) + jabber_events := make(chan JabberEvent,1) + + go botdata.handleEventsforXMPP(botdata.xmppclient_.Out, presence_events, jabber_events) + go botdata.handleIncomingXMPPStanzas(botdata.xmppclient_.In, botdata.xmppclient_.Out, jabber_events) + + botdata.presence_events_ = &presence_events + + return botdata, presence_events, nil +} + +func (botdata *XmppBot) StopBot() { + if botdata.xmppclient_ != nil { + close(botdata.xmppclient_.Out) + } + if botdata.presence_events_ != nil { + *botdata.presence_events_ <- false + close(*botdata.presence_events_) + } +} + +//~ func main() { + //~ bot, presence_events, err := NewStartedBot("realrauminfo@realraum.at/Tuer", "d7ynC6Dg", "r3alraumOLGAXMPPInfos", true) + + //~ if err != nil { + //~ log.Println(err) + //~ os.Exit(1) + //~ } + + //~ presence_events <- PresenceEvent{true, true, true} + //~ presence_events <- PresenceEvent{true, true, false} + //~ presence_events <- PresenceEvent{true, false, false} + + //~ p := make([]byte, 1024) + //~ for + //~ { + //~ nr, _ := os.Stdin.Read(p) + //~ if nr == 0 { + //~ break + //~ } + //~ } + + //~ bot.StopBot() +//~ } diff --git a/r3-netstatus/spaceapi/spaceapi.go b/r3-netstatus/spaceapi/spaceapi.go new file mode 100644 index 0000000..4162295 --- /dev/null +++ b/r3-netstatus/spaceapi/spaceapi.go @@ -0,0 +1,242 @@ +// spaceapi.go +package spaceapi + +import ( + "encoding/json" + "time" +) + +const max_num_events int = 4 + +type SpaceInfo map[string]interface{} + +type SpaceDoorLockSensor struct { + value bool + location string + name string + description string +} + +type SpaceDoorAjarSensor struct { + value bool + location string + name string + description string +} + +func MakeTempSensor(name, where, unit string, value float64) SpaceInfo { + listofwhats := make([]SpaceInfo, 1) + listofwhats[0] = SpaceInfo{ + "value": value, + "unit": unit, + "location": where, + "name": name, + "description": ""} + return SpaceInfo{"temperature": listofwhats} +} + +func MakeTempCSensor(name, where string, value float64) SpaceInfo { + return MakeTempSensor(name,where,"\u00b0C",value) +} + +func MakeIlluminationSensor(name, where, unit string, value int64) SpaceInfo { + listofwhats := make([]SpaceInfo, 1) + listofwhats[0] = SpaceInfo{ + "value": value, + "unit": unit, + "location": where, + "name": name, + "description": ""} + return SpaceInfo{"ext_illumination": listofwhats} +} + +func MakePowerConsumptionSensor(name, where, unit string, value int64) SpaceInfo { + listofwhats := make([]SpaceInfo, 1) + listofwhats[0] = SpaceInfo{ + "value": value, + "unit": unit, + "location": where, + "name": name, + "description": ""} + return SpaceInfo{"power_consumption": listofwhats} +} + +func MakeNetworkConnectionsSensor(name, where, nettype string, value, machines int64) SpaceInfo { + listofwhats := make([]SpaceInfo, 1) + listofwhats[0] = SpaceInfo{ + "value": value, + "type": nettype, + "machines": machines, + "location": where, + "name": name, + "description": ""} + return SpaceInfo{"network_connections": listofwhats} +} + +func MakeMemberCountSensor(name, where string, value int64) SpaceInfo { + listofwhats := make([]SpaceInfo, 1) + listofwhats[0] = SpaceInfo{ + "value": value, + "location": where, + "name": name, + "description": ""} + return SpaceInfo{"total_member_count": listofwhats} +} + +func MakeDoorLockSensor(name, where string, value bool) SpaceInfo { + listofwhats := make([]SpaceInfo, 1) + listofwhats[0] = SpaceInfo{ + "value": value, + "location": where, + "name": name, + "description": ""} + return SpaceInfo{"door_locked": listofwhats} +} + +func MakeDoorAjarSensor(name, where string, value bool) SpaceInfo { + listofwhats := make([]SpaceInfo, 1) + listofwhats[0] = SpaceInfo{ + "value": value, + "location": where, + "name": name, + "description": ""} + return SpaceInfo{"ext_door_ajar": listofwhats} +} + +func (nsi SpaceInfo) MergeInSensor(sensorinfo SpaceInfo) { + if nsi["sensors"] == nil { + nsi["sensors"] = SpaceInfo{} + //~ listofwhats := make([]SpaceInfo, 1) + //~ listofwhats[0] = sensortype.(SpaceInfo) + //~ sensorobj := SpaceInfo{what: listofwhats} + //~ nsi["sensors"] = sensorobj + } + sensorobj := nsi["sensors"].(SpaceInfo) + for what, subsensorobjlist := range sensorinfo { + if sensorobj[what] == nil { + sensorobj[what] = subsensorobjlist + } else { + existingsensorobjslist := sensorobj[what].([]SpaceInfo) + for _, newsensorobj := range subsensorobjlist.([]SpaceInfo) { + foundandsubstituted := false + for i:=0; i< len(existingsensorobjslist); i++ { + if existingsensorobjslist[i]["name"] == newsensorobj["name"] { + existingsensorobjslist[i] = newsensorobj + foundandsubstituted = true + } + } + if foundandsubstituted == false { + sensorobj[what] = append(sensorobj[what].([]SpaceInfo), newsensorobj) + //note that we do not change existingsensorobjslist here but directly sensorobj[what] !! + //the implications being that, if we have several newsensorobj in the list: + // a) optimisation: we only check them against the existing ones and spare ourselves the work of checking a newsensorobj's name against a just added other newsensorobjs's name + // b) if the array sensorinfo[what] has several objects with the same name, nsi["sensors"] will also end up with these name conflicts + } + } + } + } +} + +func (nsi SpaceInfo) AddSpaceContactInfo(phone, irc, email, ml, jabber, issuemail string) SpaceInfo { + nsi["contact"] = SpaceInfo{ + "phone": phone, + "email": email, + "ml": ml, + "jabber": jabber, + "issue_mail": issuemail} + nsi["issue_report_channels"] = [3]string{"issue_mail","email","ml"} + return nsi +} + +func (nsi SpaceInfo) AddSpaceFeed(feedtype, url string) SpaceInfo { + newfeed := SpaceInfo{"url": url} + if nsi["feeds"] == nil { + nsi["feeds"] = SpaceInfo{feedtype: newfeed} + } else { + feedobj, ok := nsi["feeds"].(SpaceInfo) //type assertion (panics if false) + if ok { + feedobj[feedtype] = newfeed + } else { + panic("Wrong Type of feedobj: Should never happen") + } + } + return nsi +} + +func (nsi SpaceInfo) AddSpaceEvent(name, eventtype, extra string) SpaceInfo { + newevent := SpaceInfo{"name": name, "type": eventtype, "timestamp": time.Now().Unix(), "extra": extra} + if nsi["events"] == nil { + eventlist := make([]SpaceInfo, 1) + eventlist[0] = newevent + nsi["events"] = eventlist + } else { + eventlist, ok := nsi["events"].([]SpaceInfo) //type assertion + if ok { + if len(eventlist) >= max_num_events { + eventlist = eventlist[1:] + } + nsi["events"] = append(eventlist, newevent) + } else { + panic("Wrong Type of eventlist: Should never happen") + } + } + return nsi +} + +func (nsi SpaceInfo) AddSpaceAddress(address string) SpaceInfo { + nsi["address"] = address + if nsi["location"] != nil { + location, ok := nsi["location"].(SpaceInfo) + if ok { + location["address"] = address + } + } + return nsi +} + +func (nsi SpaceInfo) SetStatus(open bool, status string) { + nsi["status"] = status + nsi["open"] = open + nsi["lastchange"] = time.Now().Unix() + state, ok := nsi["state"].(SpaceInfo) + if ok { + state["message"] = status + state["open"] = open + state["lastchange"] = nsi["lastchange"] + } +} + +func NewSpaceInfo(space string, url string, logo string, open_icon string, closed_icon string, lat float64, lon float64) SpaceInfo { + nsi := map[string]interface{}{ + "api": "0.13", + "space": space, + "url": url, + "logo": logo, + "open": false, + "lastchange": time.Now().Unix(), + "icon": SpaceInfo{ + "open": open_icon, + "closed": closed_icon, + }, + "state": SpaceInfo{ + "open": false, + "lastchange":time.Now().Unix(), + "icon": SpaceInfo{ + "open": open_icon, + "closed": closed_icon}, + }, + "location": SpaceInfo{ + "lat": lat, + "lon": lon}, + "contact" : SpaceInfo {}, + } + return nsi +} + +func (data SpaceInfo) MakeJSON() ([]byte, error) { + msg, err := json.Marshal(data) + if err == nil { + return msg, nil + } + return nil, err +} -- 1.7.10.4