diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/plugin.json b/mattermost/plugin.json index ba3a186..872bd8c 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -10,8 +10,13 @@ "settings_schema": { "settings": [ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/plugin.json b/mattermost/plugin.json index ba3a186..872bd8c 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -10,8 +10,13 @@ "settings_schema": { "settings": [ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index 634b204..fad5981 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -13,9 +13,14 @@ #ifdef fsnotify {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, #else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/plugin.json b/mattermost/plugin.json index ba3a186..872bd8c 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -10,8 +10,13 @@ "settings_schema": { "settings": [ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index 634b204..fad5981 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -13,9 +13,14 @@ #ifdef fsnotify {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, #else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index c478ac9..673a918 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/plugin.json b/mattermost/plugin.json index ba3a186..872bd8c 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -10,8 +10,13 @@ "settings_schema": { "settings": [ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index 634b204..fad5981 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -13,9 +13,14 @@ #ifdef fsnotify {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, #else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index c478ac9..673a918 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index b3512ab..7637e45 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -28,6 +28,7 @@ "crypto/sha1" "path" "encoding/base64" + _ "math" ) const DO_LOGGING = false @@ -77,6 +78,16 @@ adminUsers []string KeyPath string + + DoorNames string + doorNames []string + + InfoIcon string + UnknownIcon string + OpenIcon string + ClosedIcon string + + settingsJson map[string]interface{} } type BathroomMonitorPlugin struct { @@ -218,6 +229,19 @@ func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + if r.URL.Path == "/settings" { + p.configLock.Lock() + defer p.configLock.Unlock() + + bytes, err := json.Marshal(p.config.settingsJson) + if err == nil { + fmt.Fprint(w, string(bytes)) + } else { + p.API.LogError(fmt.Sprintf("Unable to json Marshal settings %s", err.Error())) + } + + return + } if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -480,6 +504,36 @@ } } + newConfig.doorNames = make([]string, 0, 4) + + split = strings.Split(newConfig.DoorNames, "|") + for _, un := range split { + trimmed := strings.Trim(un, ", \t\n") + if trimmed != "" { + newConfig.doorNames = append(newConfig.doorNames, trimmed) + } + } + + min := func(a uint8, b uint8) uint8 { + if a < b { + return a + } + return b + } + + doorsJson := make(map[string]string) + for i := uint8(0); i < min(uint8(len(newConfig.doorNames)), newConfig.numDoors); i++ { + doorsJson[fmt.Sprintf("%d", i + 1)] = newConfig.doorNames[i] + } + + newConfig.settingsJson = map[string]interface{} { + "info_icon": newConfig.InfoIcon, + "unknown_icon": newConfig.UnknownIcon, + "open_icon": newConfig.OpenIcon, + "closed_icon": newConfig.ClosedIcon, + "doors": doorsJson, + } + p.config = newConfig p.configUpdates++ diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/plugin.json b/mattermost/plugin.json index ba3a186..872bd8c 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -10,8 +10,13 @@ "settings_schema": { "settings": [ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index 634b204..fad5981 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -13,9 +13,14 @@ #ifdef fsnotify {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, #else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index c478ac9..673a918 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index b3512ab..7637e45 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -28,6 +28,7 @@ "crypto/sha1" "path" "encoding/base64" + _ "math" ) const DO_LOGGING = false @@ -77,6 +78,16 @@ adminUsers []string KeyPath string + + DoorNames string + doorNames []string + + InfoIcon string + UnknownIcon string + OpenIcon string + ClosedIcon string + + settingsJson map[string]interface{} } type BathroomMonitorPlugin struct { @@ -218,6 +229,19 @@ func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + if r.URL.Path == "/settings" { + p.configLock.Lock() + defer p.configLock.Unlock() + + bytes, err := json.Marshal(p.config.settingsJson) + if err == nil { + fmt.Fprint(w, string(bytes)) + } else { + p.API.LogError(fmt.Sprintf("Unable to json Marshal settings %s", err.Error())) + } + + return + } if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -480,6 +504,36 @@ } } + newConfig.doorNames = make([]string, 0, 4) + + split = strings.Split(newConfig.DoorNames, "|") + for _, un := range split { + trimmed := strings.Trim(un, ", \t\n") + if trimmed != "" { + newConfig.doorNames = append(newConfig.doorNames, trimmed) + } + } + + min := func(a uint8, b uint8) uint8 { + if a < b { + return a + } + return b + } + + doorsJson := make(map[string]string) + for i := uint8(0); i < min(uint8(len(newConfig.doorNames)), newConfig.numDoors); i++ { + doorsJson[fmt.Sprintf("%d", i + 1)] = newConfig.doorNames[i] + } + + newConfig.settingsJson = map[string]interface{} { + "info_icon": newConfig.InfoIcon, + "unknown_icon": newConfig.UnknownIcon, + "open_icon": newConfig.OpenIcon, + "closed_icon": newConfig.ClosedIcon, + "doors": doorsJson, + } + p.config = newConfig p.configUpdates++ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 4cca171..adb6cdf 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -106,9 +106,24 @@ this.state = { doors: {} }; + this.settings = {}; } componentDidMount() { + this.getSettings(); + } + + async getSettings() { + try { + var url = window.location.origin + "/plugins/com.mattermost.bathroom/settings"; + var res = await fetch(url); + this.settings = await res.json(); + } catch (e) { + console.log(e); + setTimeout(this.getSettings.bind(this), 5000); + return; + } + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); } @@ -118,22 +133,40 @@ } async updateDoors(msg) { - var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; - var res = await fetch(url); - var json = await res.json(); - this.setState({ - "doors": json - }); + //var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + try { + var url = window.location.origin + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } catch (e) { + console.log(e); + setTimeout(this.updateDoors.bind(this), 3000); + } } render() { if (!this.state.doors) return; var columns = 6; var keys = Object.keys(this.state.doors).sort(); - var width = Math.floor(100 / columns) + "%"; + var width = Math.floor(100 / (columns + 1)) + "%"; var widthPx = "23px"; var elems = []; var row = []; + var rows = 0; + + var IconImg = props => _react.default.createElement("img", { + src: props.src, + width: "100%", + style: { + "padding": "2px" + }, + title: props.title + }); + + var totalRows = Math.floor(keys.length / columns) + 1; for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/ @@ -141,31 +174,16 @@ var img = null; if (i < keys.length) { - var door = this.state.doors[keys[i]]; - var imgFile = null; + var key = keys[i]; + var door = this.state.doors[key]; + var imgFile = this.settings[door + "_icon"]; - switch (door) { - default: - case "unknown": - imgFile = "/static/emoji/2753.png"; - break; - - case "open": - imgFile = "/static/emoji/1f6bd.png"; - break; - - case "closed": - imgFile = "/static/emoji/26d4-fe0f.png"; - break; + if (imgFile) { + img = _react.default.createElement(IconImg, { + src: imgFile, + title: this.settings.doors[key] + }); //img = } - - img = _react.default.createElement("img", { - src: imgFile, - width: "100%", - style: { - "padding": "2px" - } - }); } row.push(_react.default.createElement("td", { @@ -173,8 +191,23 @@ }, img)); if (i % columns == columns - 1 || i == keys.length - 1) { - elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + if (rows == 0) { + row.unshift( // + _react.default.createElement("td", { + rowspan: totalRows, + width: widthPx, + style: { + "color": this.props.theme.sidebarText, + "padding-right": "5px" + } + }, _react.default.createElement(IconImg, { + src: this.settings.info_icon + }), ":")); + } + + elems.push(_react.default.createElement("tr", null, [...row])); row.length = 0; + rows++; } } @@ -188,7 +221,8 @@ }, _react.default.createElement("table", { style: { "margin-left": "auto", - "margin-right": "auto" + "margin-right": "auto", + "border": "1px solid " + this.props.theme.sidebarText } }, elems)) ); diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/plugin.json b/mattermost/plugin.json index ba3a186..872bd8c 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -10,8 +10,13 @@ "settings_schema": { "settings": [ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index 634b204..fad5981 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -13,9 +13,14 @@ #ifdef fsnotify {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, #else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index c478ac9..673a918 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index b3512ab..7637e45 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -28,6 +28,7 @@ "crypto/sha1" "path" "encoding/base64" + _ "math" ) const DO_LOGGING = false @@ -77,6 +78,16 @@ adminUsers []string KeyPath string + + DoorNames string + doorNames []string + + InfoIcon string + UnknownIcon string + OpenIcon string + ClosedIcon string + + settingsJson map[string]interface{} } type BathroomMonitorPlugin struct { @@ -218,6 +229,19 @@ func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + if r.URL.Path == "/settings" { + p.configLock.Lock() + defer p.configLock.Unlock() + + bytes, err := json.Marshal(p.config.settingsJson) + if err == nil { + fmt.Fprint(w, string(bytes)) + } else { + p.API.LogError(fmt.Sprintf("Unable to json Marshal settings %s", err.Error())) + } + + return + } if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -480,6 +504,36 @@ } } + newConfig.doorNames = make([]string, 0, 4) + + split = strings.Split(newConfig.DoorNames, "|") + for _, un := range split { + trimmed := strings.Trim(un, ", \t\n") + if trimmed != "" { + newConfig.doorNames = append(newConfig.doorNames, trimmed) + } + } + + min := func(a uint8, b uint8) uint8 { + if a < b { + return a + } + return b + } + + doorsJson := make(map[string]string) + for i := uint8(0); i < min(uint8(len(newConfig.doorNames)), newConfig.numDoors); i++ { + doorsJson[fmt.Sprintf("%d", i + 1)] = newConfig.doorNames[i] + } + + newConfig.settingsJson = map[string]interface{} { + "info_icon": newConfig.InfoIcon, + "unknown_icon": newConfig.UnknownIcon, + "open_icon": newConfig.OpenIcon, + "closed_icon": newConfig.ClosedIcon, + "doors": doorsJson, + } + p.config = newConfig p.configUpdates++ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 4cca171..adb6cdf 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -106,9 +106,24 @@ this.state = { doors: {} }; + this.settings = {}; } componentDidMount() { + this.getSettings(); + } + + async getSettings() { + try { + var url = window.location.origin + "/plugins/com.mattermost.bathroom/settings"; + var res = await fetch(url); + this.settings = await res.json(); + } catch (e) { + console.log(e); + setTimeout(this.getSettings.bind(this), 5000); + return; + } + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); } @@ -118,22 +133,40 @@ } async updateDoors(msg) { - var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; - var res = await fetch(url); - var json = await res.json(); - this.setState({ - "doors": json - }); + //var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + try { + var url = window.location.origin + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } catch (e) { + console.log(e); + setTimeout(this.updateDoors.bind(this), 3000); + } } render() { if (!this.state.doors) return; var columns = 6; var keys = Object.keys(this.state.doors).sort(); - var width = Math.floor(100 / columns) + "%"; + var width = Math.floor(100 / (columns + 1)) + "%"; var widthPx = "23px"; var elems = []; var row = []; + var rows = 0; + + var IconImg = props => _react.default.createElement("img", { + src: props.src, + width: "100%", + style: { + "padding": "2px" + }, + title: props.title + }); + + var totalRows = Math.floor(keys.length / columns) + 1; for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/ @@ -141,31 +174,16 @@ var img = null; if (i < keys.length) { - var door = this.state.doors[keys[i]]; - var imgFile = null; + var key = keys[i]; + var door = this.state.doors[key]; + var imgFile = this.settings[door + "_icon"]; - switch (door) { - default: - case "unknown": - imgFile = "/static/emoji/2753.png"; - break; - - case "open": - imgFile = "/static/emoji/1f6bd.png"; - break; - - case "closed": - imgFile = "/static/emoji/26d4-fe0f.png"; - break; + if (imgFile) { + img = _react.default.createElement(IconImg, { + src: imgFile, + title: this.settings.doors[key] + }); //img = } - - img = _react.default.createElement("img", { - src: imgFile, - width: "100%", - style: { - "padding": "2px" - } - }); } row.push(_react.default.createElement("td", { @@ -173,8 +191,23 @@ }, img)); if (i % columns == columns - 1 || i == keys.length - 1) { - elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + if (rows == 0) { + row.unshift( // + _react.default.createElement("td", { + rowspan: totalRows, + width: widthPx, + style: { + "color": this.props.theme.sidebarText, + "padding-right": "5px" + } + }, _react.default.createElement(IconImg, { + src: this.settings.info_icon + }), ":")); + } + + elems.push(_react.default.createElement("tr", null, [...row])); row.length = 0; + rows++; } } @@ -188,7 +221,8 @@ }, _react.default.createElement("table", { style: { "margin-left": "auto", - "margin-right": "auto" + "margin-right": "auto", + "border": "1px solid " + this.props.theme.sidebarText } }, elems)) ); diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map index 26114e4..99ecff9 100644 --- a/mattermost/webapp/dist/main.js.map +++ b/mattermost/webapp/dist/main.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","componentDidMount","Registry","registerWebSocketEventHandler","updateDoors","bind","componentWillUnmount","unregisterWebSocketEventHandler","msg","url","Store","getState","entities","general","config","SiteURL","res","fetch","json","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","i","length","img","door","imgFile","push","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","window","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n }\n componentDidMount()\n {\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = (Math.floor(100 / columns)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var door = this.state.doors[keys[i]];\n var imgFile = null;\n switch (door)\n {\n default:\n case \"unknown\":\n imgFile = \"/static/emoji/2753.png\";\n break;\n case \"open\":\n imgFile = \"/static/emoji/1f6bd.png\";\n break;\n case \"closed\":\n imgFile = \"/static/emoji/26d4-fe0f.png\";\n break;\n }\n img = \n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n elems.push({[...row]});\n row.length = 0;\n }\n }\n return (\n //\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","settings","componentDidMount","getSettings","url","window","location","origin","res","fetch","json","e","console","log","setTimeout","bind","Registry","registerWebSocketEventHandler","updateDoors","componentWillUnmount","unregisterWebSocketEventHandler","msg","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","rows","IconImg","src","title","totalRows","length","i","img","key","door","imgFile","push","unshift","theme","sidebarText","info_icon","Store","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n this.settings = {};\n }\n componentDidMount()\n {\n this.getSettings();\n }\n async getSettings()\n {\n try\n {\n\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/settings\";\n var res = await fetch(url);\n this.settings = await res.json();\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.getSettings.bind(this), 5000);\n return;\n }\n\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n //var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n try\n {\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.updateDoors.bind(this), 3000);\n }\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = Math.floor(100 / (columns + 1)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n var rows = 0;\n var IconImg = props => ;\n var totalRows = Math.floor(keys.length / columns) + 1;\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var key = keys[i];\n var door = this.state.doors[key];\n var imgFile = this.settings[door + \"_icon\"];\n if (imgFile)\n {\n img = ;\n //img = \n }\n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n if (rows == 0)\n {\n row.unshift(\n //\n :\n );\n }\n elems.push({[...row]});\n row.length = 0;\n rows++;\n }\n }\n return (\n //\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 246e3c9..0b66090 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/package.sh b/mattermost/package.sh old mode 100644 new mode 100755 diff --git a/mattermost/plugin.json b/mattermost/plugin.json index ba3a186..872bd8c 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -10,8 +10,13 @@ "settings_schema": { "settings": [ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index 634b204..fad5981 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -13,9 +13,14 @@ #ifdef fsnotify {"key":"WatchPath", "display_name":"Pi status folder", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, #else - {"key":"KeyPath", "display_name":"", "type":"text", "default":"./", "help_text":"Path to watch for doorX files updated from PHP"}, + {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif - {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"} + {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, + {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, + {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, + {"key":"OpenIcon", "display_name":"Open icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are open"}, + {"key":"ClosedIcon", "display_name":"Closed icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors that are closed"} ] } } diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index c478ac9..673a918 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index b3512ab..7637e45 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -28,6 +28,7 @@ "crypto/sha1" "path" "encoding/base64" + _ "math" ) const DO_LOGGING = false @@ -77,6 +78,16 @@ adminUsers []string KeyPath string + + DoorNames string + doorNames []string + + InfoIcon string + UnknownIcon string + OpenIcon string + ClosedIcon string + + settingsJson map[string]interface{} } type BathroomMonitorPlugin struct { @@ -218,6 +229,19 @@ func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + if r.URL.Path == "/settings" { + p.configLock.Lock() + defer p.configLock.Unlock() + + bytes, err := json.Marshal(p.config.settingsJson) + if err == nil { + fmt.Fprint(w, string(bytes)) + } else { + p.API.LogError(fmt.Sprintf("Unable to json Marshal settings %s", err.Error())) + } + + return + } if r.URL.Path == "/status" { p.doorLock.Lock() defer p.doorLock.Unlock() @@ -480,6 +504,36 @@ } } + newConfig.doorNames = make([]string, 0, 4) + + split = strings.Split(newConfig.DoorNames, "|") + for _, un := range split { + trimmed := strings.Trim(un, ", \t\n") + if trimmed != "" { + newConfig.doorNames = append(newConfig.doorNames, trimmed) + } + } + + min := func(a uint8, b uint8) uint8 { + if a < b { + return a + } + return b + } + + doorsJson := make(map[string]string) + for i := uint8(0); i < min(uint8(len(newConfig.doorNames)), newConfig.numDoors); i++ { + doorsJson[fmt.Sprintf("%d", i + 1)] = newConfig.doorNames[i] + } + + newConfig.settingsJson = map[string]interface{} { + "info_icon": newConfig.InfoIcon, + "unknown_icon": newConfig.UnknownIcon, + "open_icon": newConfig.OpenIcon, + "closed_icon": newConfig.ClosedIcon, + "doors": doorsJson, + } + p.config = newConfig p.configUpdates++ diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index 4cca171..adb6cdf 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -106,9 +106,24 @@ this.state = { doors: {} }; + this.settings = {}; } componentDidMount() { + this.getSettings(); + } + + async getSettings() { + try { + var url = window.location.origin + "/plugins/com.mattermost.bathroom/settings"; + var res = await fetch(url); + this.settings = await res.json(); + } catch (e) { + console.log(e); + setTimeout(this.getSettings.bind(this), 5000); + return; + } + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); } @@ -118,22 +133,40 @@ } async updateDoors(msg) { - var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; - var res = await fetch(url); - var json = await res.json(); - this.setState({ - "doors": json - }); + //var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + try { + var url = window.location.origin + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({ + "doors": json + }); + } catch (e) { + console.log(e); + setTimeout(this.updateDoors.bind(this), 3000); + } } render() { if (!this.state.doors) return; var columns = 6; var keys = Object.keys(this.state.doors).sort(); - var width = Math.floor(100 / columns) + "%"; + var width = Math.floor(100 / (columns + 1)) + "%"; var widthPx = "23px"; var elems = []; var row = []; + var rows = 0; + + var IconImg = props => _react.default.createElement("img", { + src: props.src, + width: "100%", + style: { + "padding": "2px" + }, + title: props.title + }); + + var totalRows = Math.floor(keys.length / columns) + 1; for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/ @@ -141,31 +174,16 @@ var img = null; if (i < keys.length) { - var door = this.state.doors[keys[i]]; - var imgFile = null; + var key = keys[i]; + var door = this.state.doors[key]; + var imgFile = this.settings[door + "_icon"]; - switch (door) { - default: - case "unknown": - imgFile = "/static/emoji/2753.png"; - break; - - case "open": - imgFile = "/static/emoji/1f6bd.png"; - break; - - case "closed": - imgFile = "/static/emoji/26d4-fe0f.png"; - break; + if (imgFile) { + img = _react.default.createElement(IconImg, { + src: imgFile, + title: this.settings.doors[key] + }); //img = } - - img = _react.default.createElement("img", { - src: imgFile, - width: "100%", - style: { - "padding": "2px" - } - }); } row.push(_react.default.createElement("td", { @@ -173,8 +191,23 @@ }, img)); if (i % columns == columns - 1 || i == keys.length - 1) { - elems.push(_react.default.createElement(_react.default.Fragment, null, [...row])); + if (rows == 0) { + row.unshift( // + _react.default.createElement("td", { + rowspan: totalRows, + width: widthPx, + style: { + "color": this.props.theme.sidebarText, + "padding-right": "5px" + } + }, _react.default.createElement(IconImg, { + src: this.settings.info_icon + }), ":")); + } + + elems.push(_react.default.createElement("tr", null, [...row])); row.length = 0; + rows++; } } @@ -188,7 +221,8 @@ }, _react.default.createElement("table", { style: { "margin-left": "auto", - "margin-right": "auto" + "margin-right": "auto", + "border": "1px solid " + this.props.theme.sidebarText } }, elems)) ); diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map index 26114e4..99ecff9 100644 --- a/mattermost/webapp/dist/main.js.map +++ b/mattermost/webapp/dist/main.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","componentDidMount","Registry","registerWebSocketEventHandler","updateDoors","bind","componentWillUnmount","unregisterWebSocketEventHandler","msg","url","Store","getState","entities","general","config","SiteURL","res","fetch","json","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","i","length","img","door","imgFile","push","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","window","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n }\n componentDidMount()\n {\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = (Math.floor(100 / columns)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var door = this.state.doors[keys[i]];\n var imgFile = null;\n switch (door)\n {\n default:\n case \"unknown\":\n imgFile = \"/static/emoji/2753.png\";\n break;\n case \"open\":\n imgFile = \"/static/emoji/1f6bd.png\";\n break;\n case \"closed\":\n imgFile = \"/static/emoji/26d4-fe0f.png\";\n break;\n }\n img = \n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n elems.push({[...row]});\n row.length = 0;\n }\n }\n return (\n //\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","state","doors","settings","componentDidMount","getSettings","url","window","location","origin","res","fetch","json","e","console","log","setTimeout","bind","Registry","registerWebSocketEventHandler","updateDoors","componentWillUnmount","unregisterWebSocketEventHandler","msg","setState","render","columns","keys","Object","sort","width","Math","floor","widthPx","elems","row","rows","IconImg","src","title","totalRows","length","i","img","key","door","imgFile","push","unshift","theme","sidebarText","info_icon","Store","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n this.state = {doors:{}};\n this.settings = {};\n }\n componentDidMount()\n {\n this.getSettings();\n }\n async getSettings()\n {\n try\n {\n\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/settings\";\n var res = await fetch(url);\n this.settings = await res.json();\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.getSettings.bind(this), 5000);\n return;\n }\n\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n //var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n try\n {\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.updateDoors.bind(this), 3000);\n }\n }\n render()\n {\n if (!this.state.doors) return;\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = Math.floor(100 / (columns + 1)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n var rows = 0;\n var IconImg = props => ;\n var totalRows = Math.floor(keys.length / columns) + 1;\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var key = keys[i];\n var door = this.state.doors[key];\n var imgFile = this.settings[door + \"_icon\"];\n if (imgFile)\n {\n img = ;\n //img = \n }\n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n if (rows == 0)\n {\n row.unshift(\n //\n :\n );\n }\n elems.push({[...row]});\n row.length = 0;\n rows++;\n }\n }\n return (\n //\n
\n
\n {elems}\n
\n \n );\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/mattermost/webapp/src/index.jsx b/mattermost/webapp/src/index.jsx index 0868e33..17189b2 100644 --- a/mattermost/webapp/src/index.jsx +++ b/mattermost/webapp/src/index.jsx @@ -6,9 +6,28 @@ { super(props); this.state = {doors:{}}; + this.settings = {}; } componentDidMount() { + this.getSettings(); + } + async getSettings() + { + try + { + + var url = window.location.origin + "/plugins/com.mattermost.bathroom/settings"; + var res = await fetch(url); + this.settings = await res.json(); + } + catch (e) + { + console.log(e); + setTimeout(this.getSettings.bind(this), 5000); + return; + } + Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); } @@ -18,53 +37,65 @@ } async updateDoors(msg) { - var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; - var res = await fetch(url); - var json = await res.json(); - this.setState({"doors":json}); + //var url = Store.getState().entities.general.config.SiteURL + "/plugins/com.mattermost.bathroom/status"; + try + { + var url = window.location.origin + "/plugins/com.mattermost.bathroom/status"; + var res = await fetch(url); + var json = await res.json(); + this.setState({"doors":json}); + } + catch (e) + { + console.log(e); + setTimeout(this.updateDoors.bind(this), 3000); + } } render() { if (!this.state.doors) return; var columns = 6; var keys = Object.keys(this.state.doors).sort(); - var width = (Math.floor(100 / columns)) + "%"; + var width = Math.floor(100 / (columns + 1)) + "%"; var widthPx = "23px"; var elems = []; var row = []; + var rows = 0; + var IconImg = props => ; + var totalRows = Math.floor(keys.length / columns) + 1; for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++) { var img = null; if (i < keys.length) { - var door = this.state.doors[keys[i]]; - var imgFile = null; - switch (door) + var key = keys[i]; + var door = this.state.doors[key]; + var imgFile = this.settings[door + "_icon"]; + if (imgFile) { - default: - case "unknown": - imgFile = "/static/emoji/2753.png"; - break; - case "open": - imgFile = "/static/emoji/1f6bd.png"; - break; - case "closed": - imgFile = "/static/emoji/26d4-fe0f.png"; - break; + img = ; + //img = } - img = } row.push({img}); if (i % columns == columns - 1 || i == keys.length - 1) { - elems.push({[...row]}); + if (rows == 0) + { + row.unshift( + // + : + ); + } + elems.push({[...row]}); row.length = 0; + rows++; } } return ( //
-
+
{elems}