├── .gitignore ├── README.md ├── app ├── ajax │ ├── display_message.php │ ├── load_config.php │ ├── reboot.php │ └── toggle_relay.php ├── bootstrap.php ├── css │ ├── app.css │ ├── fonts.css │ ├── intro.css │ └── main.css ├── dashboard.php ├── displays.php ├── fonts │ ├── montserrat-v14-latin-100.eot │ ├── montserrat-v14-latin-100.svg │ ├── montserrat-v14-latin-100.ttf │ ├── montserrat-v14-latin-100.woff │ ├── montserrat-v14-latin-100.woff2 │ ├── montserrat-v14-latin-200.eot │ ├── montserrat-v14-latin-200.svg │ ├── montserrat-v14-latin-200.ttf │ ├── montserrat-v14-latin-200.woff │ ├── montserrat-v14-latin-200.woff2 │ ├── montserrat-v14-latin-300.eot │ ├── montserrat-v14-latin-300.svg │ ├── montserrat-v14-latin-300.ttf │ ├── montserrat-v14-latin-300.woff │ ├── montserrat-v14-latin-300.woff2 │ ├── montserrat-v14-latin-500.eot │ ├── montserrat-v14-latin-500.svg │ ├── montserrat-v14-latin-500.ttf │ ├── montserrat-v14-latin-500.woff │ ├── montserrat-v14-latin-500.woff2 │ ├── montserrat-v14-latin-600.eot │ ├── montserrat-v14-latin-600.svg │ ├── montserrat-v14-latin-600.ttf │ ├── montserrat-v14-latin-600.woff │ ├── montserrat-v14-latin-600.woff2 │ ├── montserrat-v14-latin-800.eot │ ├── montserrat-v14-latin-800.svg │ ├── montserrat-v14-latin-800.ttf │ ├── montserrat-v14-latin-800.woff │ ├── montserrat-v14-latin-800.woff2 │ ├── montserrat-v14-latin-regular.eot │ ├── montserrat-v14-latin-regular.svg │ ├── montserrat-v14-latin-regular.ttf │ ├── montserrat-v14-latin-regular.woff │ ├── montserrat-v14-latin-regular.woff2 │ ├── open-sans-v17-latin-300.eot │ ├── open-sans-v17-latin-300.svg │ ├── open-sans-v17-latin-300.ttf │ ├── open-sans-v17-latin-300.woff │ ├── open-sans-v17-latin-300.woff2 │ ├── open-sans-v17-latin-600.eot │ ├── open-sans-v17-latin-600.svg │ ├── open-sans-v17-latin-600.ttf │ ├── open-sans-v17-latin-600.woff │ ├── open-sans-v17-latin-600.woff2 │ ├── open-sans-v17-latin-700.eot │ ├── open-sans-v17-latin-700.svg │ ├── open-sans-v17-latin-700.ttf │ ├── open-sans-v17-latin-700.woff │ ├── open-sans-v17-latin-700.woff2 │ ├── open-sans-v17-latin-800.eot │ ├── open-sans-v17-latin-800.svg │ ├── open-sans-v17-latin-800.ttf │ ├── open-sans-v17-latin-800.woff │ ├── open-sans-v17-latin-800.woff2 │ ├── open-sans-v17-latin-regular.eot │ ├── open-sans-v17-latin-regular.svg │ ├── open-sans-v17-latin-regular.ttf │ ├── open-sans-v17-latin-regular.woff │ └── open-sans-v17-latin-regular.woff2 ├── img │ ├── lock.svg │ ├── mudpi_logo_flat.png │ ├── mudpi_logo_grad.png │ ├── mudpi_logo_large_flat.png │ ├── mudpi_logo_large_grad.png │ ├── mudpi_logo_small_flat.png │ ├── mudpi_logo_small_grad.png │ └── mudpi_logo_white.png ├── index.php ├── js │ ├── account.js │ ├── app.js │ ├── displays.js │ ├── intro.js │ ├── relays.js │ ├── sensor_readings.js │ └── update_check.js ├── logs.php ├── templates │ ├── dashboard.php │ ├── displays.php │ ├── logs.php │ ├── modal.php │ ├── new_account.php │ ├── partials │ │ └── navigation.php │ ├── toggles.php │ └── welcome.php └── toggles.php ├── composer.json ├── configs └── mudpi_ui.conf └── includes ├── autoloader.php ├── config.php └── helpers.php /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | scripts/ 3 | tests/ 4 | vendor/ 5 | configs/includes/ 6 | package-lock.json 7 | mudpi.config 8 | 9 | # Virtual Environments 10 | .env 11 | .venv 12 | env/ 13 | venv/ 14 | ENV/ 15 | env.bak/ 16 | venv.bak/ 17 | 18 | # IDE configs 19 | .idea/ 20 | .DS_Store 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MudPi Smart Garden 2 | 3 | # MudPi UI 4 | > A lightweight web dashboard to monitor MudPi. 5 | 6 | MudPi UI is a web application that provides an interface to monitor MudPi. The app can read data stored in redis by MudPi and will display the mose recent reading. The dashboard can be accessed from any device on the same network as the pi. 7 | 8 | ## Documentation 9 | For full documentation visit [mudpi.app](https://mudpi.app/docs/setup-assistant) 10 | 11 | 12 | ## Contributing 13 | Any contributions you can make will be greatly appreciated. If you are interested in contributing please get in touch with me and submit a pull request. There is much more I would like to add support for, however being a single developer limits my scope. 14 | 15 | 16 | ## Versioning 17 | Breaking.Major.Minor 18 | 19 | 20 | ## Authors 21 | * Eric Davisson - [Website](http://ericdavisson.com) 22 | * Twitter.com/theDavisson 23 | 24 | 25 | ## License 26 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 27 | 28 | 29 | MudPi Smart Garden 30 | 31 | -------------------------------------------------------------------------------- /app/ajax/display_message.php: -------------------------------------------------------------------------------- 1 | "Message", //Clear, ClearQueue, Message 27 | "data" => [ 28 | "time" => date("Y-m-d H:i:s"), 29 | "message" => $_POST["message"], 30 | "duration" => $_POST["duration"] 31 | ], 32 | "topic" => $_POST["topic"] 33 | ); 34 | 35 | //Connecting to Redis server on localhost 36 | $redis = new \Redis(); 37 | $redis->connect(MUDPI_REDIS_HOST, MUDPI_REDIS_PORT); 38 | 39 | if($redis->publish($data["topic"], json_encode($data))) { 40 | echo json_encode(['status' => 'OK', 41 | 'message' => 'Successfully Sent Message to Queue', 42 | 'data' => $data]); 43 | } 44 | else { 45 | response_error('Problem Sending Message to Queue'); 46 | } 47 | 48 | ?> 49 | 50 | -------------------------------------------------------------------------------- /app/ajax/load_config.php: -------------------------------------------------------------------------------- 1 | $reading) { 28 | if (!isset($reading["value"])) { 29 | response_error('Reading Value was not set or invalid.'); 30 | } 31 | 32 | if (!isset($reading["parsed"]) || empty($reading["parsed"])) { 33 | //response_error('parsed was not set or invalid.'); 34 | $reading["parsed"] = null; 35 | } 36 | 37 | if (!isset($reading["sensor"]) || empty($reading["sensor"])) { 38 | //response_error('sensor was not set or invalid.'); 39 | $reading["sensor"] = "unknown"; 40 | } 41 | 42 | $reading['boots'] = $_POST["boots"]; 43 | $reading['source'] = $_POST["source"]; 44 | } 45 | 46 | 47 | $data = array( 48 | "time" => date("Y-m-d H:i:s"), 49 | "value" => $_POST["value"], 50 | "parsed" => $_POST["parsed"], 51 | "boots" => $_POST["boots"], 52 | "source" => $_POST["source"] 53 | ); 54 | 55 | $old_data = unserialize(file_get_contents("/home/mudpi/sprout.txt")); 56 | $old_data[] = $data; 57 | 58 | if(file_put_contents('/home/mudpi/sprout.txt', serialize($old_data))) { 59 | echo json_encode(['status' => 'OK', 'message' => 'Successfully Saved Readings']); 60 | } 61 | else { 62 | response_error('Problem Saving the Readings to File'); 63 | } 64 | 65 | ?> 66 | 67 | -------------------------------------------------------------------------------- /app/ajax/reboot.php: -------------------------------------------------------------------------------- 1 | '.'.$_POST["key"].".toggle" 18 | ); 19 | 20 | //Connecting to Redis server on localhost 21 | $redis = new \Redis(); 22 | $redis->connect(MUDPI_REDIS_HOST, MUDPI_REDIS_PORT); 23 | 24 | if($redis->publish('action_call', json_encode($data))) { 25 | echo json_encode(['status' => 'OK', 'message' => 'Successfully Toggled '.$_POST["key"]]); 26 | } 27 | else { 28 | response_error('Problem Toggling the Relay or No Relay Listening'); 29 | } 30 | 31 | ?> 32 | 33 | -------------------------------------------------------------------------------- /app/bootstrap.php: -------------------------------------------------------------------------------- 1 | "", 16 | "humidity" => "%", 17 | "temperature" => "°", 18 | "soil" => "%", 19 | "moisture" => "%", 20 | "float" => "", 21 | "rain" => "%", 22 | "altitude" => '\'', 23 | "pressure" => 'hPa', 24 | "gas" => 'Ω' 25 | ]; 26 | 27 | //Connecting to Redis server on localhost 28 | $redis = new Redis(); 29 | $redis->connect(MUDPI_REDIS_HOST, MUDPI_REDIS_PORT); 30 | 31 | $started_at = strtotime($redis->get("started_at")); 32 | 33 | // Get the stored keys and print it 34 | $redis_keys = $redis->keys("*"); 35 | 36 | $config = json_decode(file_get_contents(MUDPI_PATH_CORE."/".MUDPI_CONFIG_FILE)); 37 | 38 | if (!empty($config->sensor)){ 39 | foreach($config->sensor as $sensor) { 40 | if(empty($sensor->name)) { 41 | $sensor->name = ucwords(str_replace("_", " ", $sensor->key)); 42 | } 43 | if(empty($sensor->classifier)) { 44 | $sensor->classifier = "general"; 45 | } 46 | try { 47 | $state = $redis->get($sensor->key.'.state'); 48 | if (!empty($state)) { 49 | $sensor->state = json_decode($state); 50 | if (is_json($sensor->state->state)) { 51 | $sensor->state->state = $sensor->state->state; 52 | } 53 | } 54 | else { 55 | throw new Exception('No State Found'); 56 | } 57 | } catch (Exception $e) { 58 | $sensor->state = (object)['component_id' => $sensor->key, 59 | 'state' => 0, 60 | 'updated_at' => '', 61 | 'metadata' => '']; 62 | } 63 | } 64 | } 65 | 66 | 67 | 68 | include 'templates/dashboard.php'; 69 | 70 | ?> -------------------------------------------------------------------------------- /app/displays.php: -------------------------------------------------------------------------------- 1 | connect(MUDPI_REDIS_HOST, MUDPI_REDIS_PORT); 15 | 16 | $started_at = strtotime($redis->get("started_at")); 17 | 18 | $config = json_decode(file_get_contents(MUDPI_PATH_CORE."/".MUDPI_CONFIG_FILE)); 19 | 20 | $displays = $config->char_display; 21 | foreach($displays as $display) { 22 | if(empty($display->name)) { 23 | $display->name = ucwords(str_replace("_", " ", $display->key)); 24 | } 25 | if(empty($display->topic)) { 26 | $display->topic = "char_display/".$display->key; 27 | } 28 | 29 | } 30 | 31 | include 'templates/displays.php'; 32 | 33 | ?> -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-100.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-100.eot -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-100.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-100.ttf -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-100.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-100.woff -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-100.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-100.woff2 -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-200.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-200.eot -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-200.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-200.ttf -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-200.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-200.woff -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-200.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-200.woff2 -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-300.eot -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-300.ttf -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-300.woff -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-300.woff2 -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-500.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-500.eot -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-500.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-500.ttf -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-500.woff -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-500.woff2 -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-600.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-600.eot -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-600.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 15 | 17 | 19 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 40 | 41 | 43 | 45 | 46 | 49 | 51 | 53 | 55 | 56 | 57 | 58 | 60 | 63 | 64 | 66 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 82 | 83 | 85 | 86 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 103 | 105 | 107 | 109 | 111 | 112 | 114 | 115 | 116 | 118 | 119 | 120 | 122 | 123 | 125 | 127 | 129 | 130 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 141 | 142 | 144 | 146 | 147 | 148 | 150 | 151 | 153 | 154 | 155 | 158 | 160 | 163 | 165 | 166 | 167 | 168 | 171 | 172 | 174 | 175 | 177 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 187 | 188 | 189 | 191 | 193 | 195 | 196 | 197 | 198 | 200 | 202 | 204 | 205 | 208 | 209 | 210 | 211 | 213 | 214 | 215 | 216 | 218 | 219 | 221 | 223 | 225 | 227 | 230 | 233 | 234 | 236 | 237 | 238 | 239 | 241 | 242 | 243 | 245 | 247 | 249 | 251 | 254 | 257 | 260 | 263 | 265 | 267 | 269 | 271 | 274 | 275 | 276 | 277 | 279 | 281 | 283 | 285 | 287 | 289 | 292 | 295 | 297 | 299 | 300 | 301 | 302 | 304 | 306 | 308 | 310 | 311 | 312 | 313 | 314 | 315 | 317 | 319 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-600.ttf -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-600.woff -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-600.woff2 -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-800.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-800.eot -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-800.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 15 | 17 | 19 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 39 | 41 | 42 | 44 | 46 | 47 | 50 | 52 | 54 | 56 | 57 | 58 | 59 | 61 | 64 | 65 | 67 | 69 | 70 | 71 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 83 | 84 | 86 | 87 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 104 | 106 | 108 | 110 | 112 | 113 | 115 | 116 | 117 | 119 | 120 | 121 | 123 | 124 | 126 | 128 | 130 | 131 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 142 | 143 | 145 | 147 | 148 | 149 | 151 | 152 | 154 | 155 | 156 | 159 | 161 | 164 | 166 | 167 | 168 | 169 | 172 | 173 | 175 | 176 | 178 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 188 | 189 | 190 | 192 | 194 | 196 | 197 | 198 | 199 | 201 | 203 | 205 | 206 | 209 | 210 | 211 | 212 | 214 | 215 | 216 | 217 | 219 | 220 | 222 | 224 | 226 | 228 | 231 | 234 | 235 | 237 | 238 | 239 | 240 | 242 | 243 | 244 | 246 | 248 | 250 | 252 | 255 | 258 | 261 | 264 | 266 | 268 | 270 | 272 | 275 | 276 | 277 | 278 | 280 | 282 | 284 | 286 | 288 | 290 | 293 | 296 | 298 | 300 | 301 | 302 | 303 | 305 | 307 | 309 | 311 | 312 | 313 | 314 | 315 | 316 | 318 | 320 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-800.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-800.ttf -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-800.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-800.woff -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-800.woff2 -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-regular.eot -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-regular.ttf -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-regular.woff -------------------------------------------------------------------------------- /app/fonts/montserrat-v14-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/montserrat-v14-latin-regular.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-300.eot -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-300.ttf -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-300.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-300.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-600.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-600.eot -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-600.ttf -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-600.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-600.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-700.eot -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-700.ttf -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-700.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-700.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-800.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-800.eot -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-800.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-800.ttf -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-800.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-800.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-800.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-regular.eot -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-regular.ttf -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-regular.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v17-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/fonts/open-sans-v17-latin-regular.woff2 -------------------------------------------------------------------------------- /app/img/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/img/mudpi_logo_flat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/img/mudpi_logo_flat.png -------------------------------------------------------------------------------- /app/img/mudpi_logo_grad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/img/mudpi_logo_grad.png -------------------------------------------------------------------------------- /app/img/mudpi_logo_large_flat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/img/mudpi_logo_large_flat.png -------------------------------------------------------------------------------- /app/img/mudpi_logo_large_grad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/img/mudpi_logo_large_grad.png -------------------------------------------------------------------------------- /app/img/mudpi_logo_small_flat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/img/mudpi_logo_small_flat.png -------------------------------------------------------------------------------- /app/img/mudpi_logo_small_grad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/img/mudpi_logo_small_grad.png -------------------------------------------------------------------------------- /app/img/mudpi_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudpi/ui/63bc31b8c4f1f52a3b1501e717a026c3571c2815/app/img/mudpi_logo_white.png -------------------------------------------------------------------------------- /app/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/js/account.js: -------------------------------------------------------------------------------- 1 | var app = document.getElementById("app"); 2 | var overlay = document.querySelector('.overlay'); 3 | var button = document.getElementById("create"); 4 | // var button_continue = document.getElementById("continue"); 5 | var token_tag = document.getElementById("token"); 6 | var errors_list = document.getElementById("errors"); 7 | var name_input = document.querySelector('[name="name"]'); 8 | var email_input = document.querySelector('[name="email"]'); 9 | var pass_input = document.querySelector('[name="password"]'); 10 | var pass_confirm_input = document.querySelector('[name="password_confirmation"]'); 11 | 12 | setTimeout(function () { 13 | app.classList.remove('transition-out'); 14 | overlay.classList.add('transition-out'); 15 | }, 300); 16 | 17 | button.addEventListener('click', function() { 18 | var formdata = new FormData(); 19 | formdata.append("name", name_input.value); 20 | formdata.append("email", email_input.value); 21 | formdata.append("password", pass_input.value); 22 | formdata.append("password_confirmation", pass_confirm_input.value); 23 | makeRequest('http://mudapi.test/api/register', 'POST', formdata); 24 | }); 25 | 26 | function makeRequest(url, type = 'POST' , data = null, callback = null) { 27 | request = new XMLHttpRequest(); 28 | 29 | if (!request) { 30 | //Problem making request 31 | return false; 32 | } 33 | if (data === null) { 34 | console.log("Defaulting form data."); 35 | data = new FormData(); 36 | } 37 | 38 | button.textContent = "Creating Account..."; 39 | button.disabled = true; 40 | button.classList.add('is-grey'); 41 | button.classList.remove('is-white'); 42 | errors_list.classList.remove("bg-red-light"); 43 | errors_list.classList.remove("p-2"); 44 | 45 | if (callback === null) { 46 | request.onreadystatechange = handleResponse; 47 | } 48 | else { 49 | request.onreadystatechange = callback; 50 | } 51 | request.open(type, url); 52 | request.setRequestHeader('Accept', 'application/json'); //or application/json;charset=UTF-8 53 | request.send(data); 54 | } 55 | 56 | function handleResponse() { 57 | if (request.readyState === XMLHttpRequest.DONE) { 58 | if (request.status === 200 || request.status === 201) { 59 | var response = JSON.parse(request.responseText).data; 60 | console.log(response); 61 | //Request successful 62 | button.textContent = "Continue"; 63 | button.disabled = false; 64 | button.classList.remove('is-grey'); 65 | button.classList.add('is-primary'); 66 | token_tag.innerHTML = response.token; 67 | document.getElementById('name').innerHTML = response.name; 68 | document.getElementById('email').innerHTML = response.email; 69 | document.getElementById('form').classList.add('transition-out'); 70 | setTimeout(function() { 71 | document.getElementById('account').classList.remove('transition-out'); 72 | }, 400); 73 | 74 | } else { 75 | var response = JSON.parse(request.responseText); 76 | errors_list.classList.add("bg-red-light"); 77 | errors_list.classList.add("p-2"); 78 | let errors = `

${response.message}

`; 83 | errors_list.innerHTML = errors; 84 | button.disabled = false; 85 | button.textContent = "Create Account"; 86 | button.classList.remove('is-grey'); 87 | button.classList.add('is-white'); 88 | //Problem with the request (500 error) 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /app/js/app.js: -------------------------------------------------------------------------------- 1 | //Ajax for loading in data 2 | var request; 3 | var networks; 4 | var selected_network; 5 | var general_data = new FormData(); 6 | var loader = document.getElementById('loader'); 7 | var button = document.getElementById("rescan"); 8 | var app = document.getElementById("app"); 9 | var overlay = document.querySelector('.overlay'); 10 | 11 | var modal = document.getElementById("modal"); 12 | var ssid_input = document.querySelector('[name="ssid"]'); 13 | var passphrase_input = document.querySelector('[name="passphrase1"]'); 14 | var passphrase_block = document.getElementById('passphrase_block'); 15 | var passphrase_confirm_block = document.getElementById('passphrase_confirm_block'); 16 | var button_connect = document.getElementById("connect"); 17 | var button_connect_confirm = document.getElementById("connect_confirm"); 18 | 19 | document.onkeydown = function(e) { 20 | if (e.key === 'Escape') { 21 | closeModal(); 22 | } 23 | } 24 | 25 | setTimeout(function () { 26 | app.classList.remove('transition-out'); 27 | overlay.classList.add('transition-out'); 28 | }, 300); 29 | 30 | //document.querySelector("#rescan").addEventListener('click', makeRequest); 31 | button.addEventListener('click', function(){ makeRequest('ajax/get_nearby_networks.php', 'POST', general_data); }); 32 | 33 | button_connect.addEventListener('click', function() { 34 | if (( selected_network.protocol.localeCompare("Open") != 0 ) && ( passphrase_input.value.length < 8 || passphrase_input.value.length > 64 )) { 35 | passphrase_input.classList.add("b-red-light"); 36 | passphrase_input.classList.add("b-1"); 37 | document.getElementById("help_message").classList.add("text-red"); 38 | document.getElementById("help_message").classList.remove("text-grey"); 39 | } 40 | else { 41 | closeModal(); 42 | document.getElementById('passphrase').innerHTML = passphrase_input.value; 43 | document.getElementById('ssid').innerHTML = ssid_input.value; 44 | openModal('modal_confirm'); 45 | } 46 | 47 | }); 48 | 49 | button_connect_confirm.addEventListener('click', function() { 50 | var network_data = new FormData(); 51 | network_data.append('ssid', ssid_input.value); 52 | if ( selected_network.protocol.localeCompare("Open") != 0 ) { 53 | network_data.append('passphrase', passphrase_input.value); 54 | } 55 | selected_network.passphrase = passphrase_input.value; 56 | network_data.append('network', JSON.stringify(selected_network)); 57 | makeRequest('ajax/connect_to_network.php', 'POST', network_data, handleSaveFileResponse); 58 | }); 59 | 60 | function closeModal(id = null) { 61 | let m = null; 62 | if(!id) { 63 | m = document.getElementById("modal"); 64 | } 65 | else { 66 | m = document.getElementById(id); 67 | } 68 | 69 | if (m.classList.contains('open')) { 70 | m.classList.toggle('open'); 71 | app.classList.toggle('bg-grey-darkest'); 72 | app.classList.toggle('opacity-25'); 73 | } 74 | } 75 | 76 | function openModal(id = null) { 77 | let m = null; 78 | if(!id) { 79 | m = document.getElementById("modal"); 80 | passphrase_input.classList.remove("b-red-light"); 81 | passphrase_input.classList.remove("b-1"); 82 | document.getElementById("help_message").classList.remove("text-red"); 83 | document.getElementById("help_message").classList.add("text-grey"); 84 | } 85 | else { 86 | m = document.getElementById(id); 87 | } 88 | if (!m.classList.contains('open')) { 89 | m.classList.toggle('open'); 90 | app.classList.toggle('bg-grey-darkest'); 91 | app.classList.toggle('opacity-25'); 92 | 93 | } 94 | } 95 | 96 | function makeRequest(url, type = 'POST' , data = null, callback = null) { 97 | request = new XMLHttpRequest(); 98 | 99 | let old_networks = document.querySelectorAll(`.network`); 100 | old_networks.forEach(net => net.classList.add('transition-out')); 101 | setTimeout(function() { 102 | document.querySelector('#networks').innerHTML = ''; 103 | }, 300) 104 | 105 | if (!request) { 106 | //Problem making request 107 | return false; 108 | } 109 | if (data === null) { 110 | console.log("Defaulting form data."); 111 | data = new FormData(); 112 | } 113 | 114 | loader.classList.remove('hidden'); 115 | button.textContent = "Scanning..."; 116 | button.disabled = true; 117 | button.classList.add('is-grey'); 118 | button.classList.remove('is-primary'); 119 | 120 | if (callback === null) { 121 | request.onreadystatechange = handleResponse; 122 | } 123 | else { 124 | request.onreadystatechange = callback; 125 | } 126 | request.open(type, url); 127 | // request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //or application/json;charset=UTF-8 128 | setCSRFHeader(request, type, data); 129 | request.send(data); 130 | } 131 | 132 | function handleResponse() { 133 | if (request.readyState === XMLHttpRequest.DONE) { 134 | if (request.status === 200) { 135 | var response = JSON.parse(request.responseText); 136 | networks = response; 137 | //Request successful 138 | loader.classList.add('hidden'); 139 | button.textContent = "Rescan"; 140 | button.disabled = false; 141 | button.classList.remove('is-grey'); 142 | button.classList.add('is-primary'); 143 | addResults(response); 144 | 145 | } else { 146 | loader.classList.add('opacity-0'); 147 | button.disabled = false; 148 | button.textContent = "Scan Failed. Refresh Page."; 149 | button.classList.remove('is-grey'); 150 | button.classList.add('text-white'); 151 | button.classList.add('bg-red'); 152 | //Problem with the request (500 error) 153 | } 154 | } 155 | } 156 | 157 | function handleSaveFileResponse() { 158 | if (request.readyState === XMLHttpRequest.DONE) { 159 | if (request.status === 200) { 160 | var response = JSON.parse(request.responseText); 161 | //Request successful 162 | closeModal('modal_confirm'); 163 | // alert(response.message); 164 | 165 | overlay.classList.remove('transition-out'); 166 | loader.classList.add('hidden'); 167 | setTimeout(function() { 168 | window.location.href = 'wifi_confirm.php'; 169 | }, 700); 170 | 171 | 172 | } else { 173 | alert(request.responseText); 174 | loader.classList.add('hidden'); 175 | button_connect_confirm.textContent = "File Save Failed!"; 176 | //Problem with the request (500 error) 177 | } 178 | } 179 | } 180 | 181 | function setCSRFHeader(xhr, type, d) { 182 | var csrfToken = document.querySelector('meta[name=csrf_token]').getAttribute('content'); 183 | if (/^(POST|PATCH|PUT|DELETE)$/i.test(type)) { 184 | d.append("csrf_token", csrfToken); 185 | xhr.setRequestHeader("X-CSRF-Token", csrfToken); 186 | } 187 | } 188 | 189 | function addResults(results, elm = null) { 190 | if (!results) { 191 | return; 192 | } 193 | 194 | if (!elm) { 195 | elm = document.querySelector('#networks'); 196 | } 197 | elm.innerHTML = ''; 198 | 199 | let count = 1; 200 | for (var network in results) { 201 | let row = document.createElement('div'); 202 | row.id = `network-${count}` 203 | row.dataset.name = results[network]['ssid']; 204 | row.classList.add('network'); 205 | row.classList.add('transition-out'); 206 | row.classList.add('box'); 207 | row.classList.add('mb-3'); 208 | row.classList.add('py-2'); 209 | row.classList.add('rounded-3'); 210 | row.innerHTML = `
211 |
212 |

${results[network]['ssid'].includes('\\x00') ? 'HIDDEN' : results[network]['ssid']}

213 | ${results[network]['macAddress']} 214 |
215 |
216 | ${results[network]['protocol'] == 'Open' ? '' : ''} 217 |
218 | 219 | 220 | 221 | 222 |
223 |
224 |
`; 225 | // 226 | elm.append(row); 227 | count++; 228 | } 229 | 230 | setTimeout(function() { 231 | let nets = document.querySelectorAll(`.network`); 232 | nets.forEach(net => net.classList.remove('transition-out')); 233 | 234 | setTimeout(function() { 235 | let bars = document.querySelectorAll(`.wifi-strength .bar`); 236 | bars.forEach(bar => bar.classList.remove('inactive')); 237 | }, 400); 238 | }, 200); 239 | 240 | 241 | var buttons = document.querySelectorAll("#networks .network"); 242 | 243 | buttons.forEach(button => button.addEventListener('click', function(e) { 244 | selected_network = networks[this.dataset.name]; 245 | ssid_input.value = selected_network.ssid ? selected_network.ssid : ''; 246 | passphrase_input.value = selected_network.passphrase ? selected_network.passphrase : ''; 247 | if(selected_network.protocol.localeCompare("Open") == 0) { 248 | passphrase_block.style.display = "none"; 249 | passphrase_confirm_block.style.display = "none"; 250 | } 251 | else { 252 | passphrase_block.style.display = ""; 253 | passphrase_confirm_block.style.display = ""; 254 | } 255 | openModal(null); 256 | })); 257 | 258 | } -------------------------------------------------------------------------------- /app/js/displays.js: -------------------------------------------------------------------------------- 1 | var app = document.getElementById("app"); 2 | var errors_list = document.getElementById("errors"); 3 | 4 | var displays = document.querySelectorAll('.display'); 5 | 6 | displays.forEach(display => { 7 | display.querySelector(".send_message").addEventListener('click', function() { 8 | var formdata = new FormData(); 9 | formdata.append("message", display.querySelector('[name="message"]').value); 10 | formdata.append("duration", display.querySelector('[name="duration"]').value); 11 | formdata.append("topic", display.querySelector('[name="topic"]').value); 12 | makeRequest('ajax/display_message.php', 'POST', formdata, display.querySelector('.send_message')); 13 | }); 14 | }); 15 | 16 | function makeRequest(url, type = 'POST' , data = null, button_element = null) { 17 | request = new XMLHttpRequest(); 18 | 19 | if (!request) { 20 | //Problem making request 21 | return false; 22 | } 23 | if (data === null) { 24 | console.log("Defaulting form data."); 25 | data = new FormData(); 26 | } 27 | 28 | button_element.textContent = "Sending..."; 29 | button_element.disabled = true; 30 | button_element.classList.add('is-grey'); 31 | button_element.classList.remove('is-primary'); 32 | errors_list.classList.remove("bg-red-light"); 33 | errors_list.classList.remove("p-2"); 34 | 35 | request.onreadystatechange = handle_response(button_element); 36 | 37 | request.open(type, url); 38 | // request.setRequestHeader('Accept', 'application/json'); //or application/json;charset=UTF-8 39 | setCSRFHeader(request, type, data); 40 | request.send(data); 41 | } 42 | 43 | function handle_response(button = null) { 44 | return function() { 45 | if (request.readyState === XMLHttpRequest.DONE) { 46 | if (request.status === 200 || request.status === 201) { 47 | var response = JSON.parse(request.responseText).data; 48 | console.log(response); 49 | //Request successful 50 | button.textContent = "Send Message"; 51 | button.disabled = false; 52 | button.classList.remove('is-grey'); 53 | button.classList.add('is-primary'); 54 | 55 | } else { 56 | var response = JSON.parse(request.responseText); 57 | errors_list.classList.add("bg-red-light"); 58 | errors_list.classList.add("p-2"); 59 | let errors = `

${response.message}

`; 64 | errors_list.innerHTML = errors; 65 | button.disabled = false; 66 | button.textContent = "Create Account"; 67 | button.classList.remove('is-grey'); 68 | button.classList.add('is-white'); 69 | //Problem with the request (500 error) 70 | } 71 | } 72 | }; 73 | } 74 | 75 | function setCSRFHeader(xhr, type, d) { 76 | var csrfToken = document.querySelector('meta[name=csrf_token]').getAttribute('content'); 77 | if (/^(POST|PATCH|PUT|DELETE)$/i.test(type)) { 78 | d.append("csrf_token", csrfToken); 79 | xhr.setRequestHeader("X-CSRF-Token", csrfToken); 80 | } 81 | } -------------------------------------------------------------------------------- /app/js/intro.js: -------------------------------------------------------------------------------- 1 | var app = document.getElementById("app"); 2 | var button = document.getElementById("continue"); 3 | var message = document.getElementById("message"); 4 | var title = document.getElementById("title"); 5 | var content = document.querySelector(".content"); 6 | var overlay = document.querySelector(".overlay"); 7 | let step = 0; 8 | let steps = [ 9 | 'In the next few steps you will be guided through the MudPi setup process.', 10 | '' 11 | 12 | ]; 13 | let titles = [ 14 | 'Welcome!', 15 | 'Lets Get Started!' 16 | ]; 17 | 18 | setTimeout(function () { 19 | content.classList.remove('transition-out'); 20 | app.classList.add('step-1'); 21 | }, 600); 22 | setTimeout(function () { 23 | button.classList.remove('transition-out'); 24 | }, 2000); 25 | 26 | button.addEventListener('click', function() { 27 | content.classList.add('transition-out'); 28 | button.classList.add('transition-out'); 29 | if (step >= 1) { 30 | overlay.classList.remove('transition-out'); 31 | setTimeout(function() { 32 | window.location.href = 'dashboard.php'; 33 | }, 600); 34 | } 35 | else { 36 | app.classList.remove(`step-${step+1}`); 37 | step++; 38 | app.classList.add(`step-${step+1}`); 39 | setTimeout(function() { 40 | message.innerHTML = steps[step]; 41 | title.innerHTML = titles[step]; 42 | content.classList.remove('transition-out'); 43 | setTimeout(function() { 44 | button.classList.remove('transition-out'); 45 | }, 600); 46 | }, 400); 47 | } 48 | 49 | }); -------------------------------------------------------------------------------- /app/js/relays.js: -------------------------------------------------------------------------------- 1 | var app = document.getElementById("app"); 2 | var errors_list = document.getElementById("errors"); 3 | 4 | var relays = document.querySelectorAll('.relay'); 5 | 6 | relays.forEach(relay => { 7 | relay.addEventListener('click', function() { 8 | var formdata = new FormData(); 9 | formdata.append("key", relay.dataset.key); 10 | formdata.append("topic", relay.dataset.topic); 11 | makeRequest('ajax/toggle_relay.php', 'POST', formdata); 12 | }); 13 | }); 14 | 15 | 16 | function makeRequest(url, type = 'POST' , data = null, callback = null) { 17 | request = new XMLHttpRequest(); 18 | 19 | if (!request) { 20 | //Problem making request 21 | return false; 22 | } 23 | if (data === null) { 24 | console.log("Defaulting form data."); 25 | data = new FormData(); 26 | } 27 | 28 | errors_list.classList.remove("bg-red-light"); 29 | errors_list.classList.remove("p-2"); 30 | 31 | if (callback === null) { 32 | request.onreadystatechange = handleResponse; 33 | } 34 | else { 35 | request.onreadystatechange = callback; 36 | } 37 | request.open(type, url); 38 | // request.setRequestHeader('Accept', 'application/json'); //or application/json;charset=UTF-8 39 | setCSRFHeader(request, type, data); 40 | request.send(data); 41 | } 42 | 43 | function handleResponse() { 44 | if (request.readyState === XMLHttpRequest.DONE) { 45 | if (request.status === 200 || request.status === 201) { 46 | var response = JSON.parse(request.responseText).data; 47 | console.log(response); 48 | //Request successful 49 | setTimeout(() => { location.reload() }, 1500); 50 | 51 | } else { 52 | var response = JSON.parse(request.responseText); 53 | errors_list.classList.add("bg-red-light"); 54 | errors_list.classList.add("p-2"); 55 | let errors = `

${response.message}

`; 60 | errors_list.innerHTML = errors; 61 | //Problem with the request (500 error) 62 | } 63 | } 64 | } 65 | 66 | function setCSRFHeader(xhr, type, d) { 67 | var csrfToken = document.querySelector('meta[name=csrf_token]').getAttribute('content'); 68 | if (/^(POST|PATCH|PUT|DELETE)$/i.test(type)) { 69 | d.append("csrf_token", csrfToken); 70 | xhr.setRequestHeader("X-CSRF-Token", csrfToken); 71 | } 72 | } -------------------------------------------------------------------------------- /app/js/sensor_readings.js: -------------------------------------------------------------------------------- 1 | var app = document.getElementById("app"); 2 | var overlay = document.querySelector('.overlay'); 3 | var button = document.getElementById("create"); 4 | // var button_continue = document.getElementById("continue"); 5 | var token_tag = document.getElementById("token"); 6 | var errors_list = document.getElementById("errors"); 7 | var name_input = document.querySelector('[name="name"]'); 8 | var email_input = document.querySelector('[name="email"]'); 9 | var pass_input = document.querySelector('[name="password"]'); 10 | var pass_confirm_input = document.querySelector('[name="password_confirmation"]'); 11 | 12 | setTimeout(function () { 13 | app.classList.remove('transition-out'); 14 | overlay.classList.add('transition-out'); 15 | }, 300); 16 | 17 | button.addEventListener('click', function() { 18 | var formdata = new FormData(); 19 | formdata.append("name", name_input.value); 20 | formdata.append("email", email_input.value); 21 | formdata.append("password", pass_input.value); 22 | formdata.append("password_confirmation", pass_confirm_input.value); 23 | makeRequest('http://mudapi.test/api/register', 'POST', formdata); 24 | }); 25 | 26 | function makeRequest(url, type = 'POST' , data = null, callback = null) { 27 | request = new XMLHttpRequest(); 28 | 29 | if (!request) { 30 | //Problem making request 31 | return false; 32 | } 33 | if (data === null) { 34 | console.log("Defaulting form data."); 35 | data = new FormData(); 36 | } 37 | 38 | button.textContent = "Creating Account..."; 39 | button.disabled = true; 40 | button.classList.add('is-grey'); 41 | button.classList.remove('is-white'); 42 | errors_list.classList.remove("bg-red-light"); 43 | errors_list.classList.remove("p-2"); 44 | 45 | if (callback === null) { 46 | request.onreadystatechange = handleResponse; 47 | } 48 | else { 49 | request.onreadystatechange = callback; 50 | } 51 | request.open(type, url); 52 | request.setRequestHeader('Accept', 'application/json'); //or application/json;charset=UTF-8 53 | request.send(data); 54 | } 55 | 56 | function handleResponse() { 57 | if (request.readyState === XMLHttpRequest.DONE) { 58 | if (request.status === 200 || request.status === 201) { 59 | var response = JSON.parse(request.responseText).data; 60 | console.log(response); 61 | //Request successful 62 | button.textContent = "Continue"; 63 | button.disabled = false; 64 | button.classList.remove('is-grey'); 65 | button.classList.add('is-primary'); 66 | token_tag.innerHTML = response.token; 67 | document.getElementById('name').innerHTML = response.name; 68 | document.getElementById('email').innerHTML = response.email; 69 | document.getElementById('form').classList.add('transition-out'); 70 | setTimeout(function() { 71 | document.getElementById('account').classList.remove('transition-out'); 72 | }, 400); 73 | 74 | } else { 75 | var response = JSON.parse(request.responseText); 76 | errors_list.classList.add("bg-red-light"); 77 | errors_list.classList.add("p-2"); 78 | let errors = `

${response.message}

`; 83 | errors_list.innerHTML = errors; 84 | button.disabled = false; 85 | button.textContent = "Create Account"; 86 | button.classList.remove('is-grey'); 87 | button.classList.add('is-white'); 88 | //Problem with the request (500 error) 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /app/js/update_check.js: -------------------------------------------------------------------------------- 1 | var app = document.getElementById("app"); 2 | var overlay = document.querySelector('.overlay'); 3 | var button = document.getElementById("update"); 4 | var button_continue = document.getElementById("continue"); 5 | var version_tag = document.getElementById("version"); 6 | var update_title = document.querySelector('#step-2 h2'); 7 | 8 | button.addEventListener("click", function() { 9 | document.querySelector('#step-1').classList.add('transition-out'); 10 | button.textContent = "Checking for Updates..."; 11 | button.disabled = true; 12 | button.classList.add('is-grey'); 13 | button.classList.remove('is-white'); 14 | setTimeout(function() { 15 | document.querySelector('#step-1').classList.add('hidden'); 16 | document.getElementById('step-2').classList.remove('hidden'); 17 | document.getElementById('step-2').classList.remove('transition-out'); 18 | makeRequest('ajax/check_for_updates.php') 19 | }, 400); 20 | }); 21 | 22 | button_continue.addEventListener('click', function() { 23 | app.classList.add('transition-out'); 24 | overlay.classList.remove('transition-out'); 25 | setTimeout(function() { 26 | window.location.href = 'new_account.php'; 27 | }, 400); 28 | }); 29 | 30 | setTimeout(function () { 31 | app.classList.remove('transition-out'); 32 | overlay.classList.add('transition-out'); 33 | }, 300); 34 | 35 | function makeRequest(url, type = 'GET' , callback = null) { 36 | request = new XMLHttpRequest(); 37 | 38 | if (!request) { 39 | //Problem making request 40 | return false; 41 | } 42 | 43 | if (callback === null) { 44 | request.onreadystatechange = handleResponse; 45 | } 46 | else { 47 | request.onreadystatechange = callback; 48 | } 49 | request.open(type, url); 50 | request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //or application/json;charset=UTF-8 51 | request.send(); 52 | } 53 | 54 | function handleResponse() { 55 | if (request.readyState === XMLHttpRequest.DONE) { 56 | if (request.status === 200) { 57 | var response = JSON.parse(request.responseText); 58 | //Request successful 59 | console.log(response); 60 | version_tag.innerHTML = response.available; 61 | if(response.has_updates) { 62 | update_title.innerHTML = "Updates Found!"; 63 | setTimeout(function() { 64 | update_title.innerHTML = "Installing Updates..."; 65 | makeRequest('ajax/install_updates.php', 'GET', handleUpdateResponse); 66 | }, 3000); 67 | } 68 | else { 69 | update_title.innerHTML = "Already Up to Date!"; 70 | setTimeout(function() { 71 | document.getElementById('step-2').classList.add('transition-out'); 72 | setTimeout(function() { 73 | document.getElementById('step-2').classList.add('hidden'); 74 | document.getElementById('step-3').classList.remove('hidden'); 75 | document.getElementById('step-3').classList.remove('transition-out'); 76 | }, 500); 77 | }, 3000); 78 | } 79 | 80 | } else { 81 | document.querySelector('#step-2').classList.add('transition-out'); 82 | document.querySelector('#step-1').classList.remove('transition-out'); 83 | button.disabled = false; 84 | button.textContent = "Update Check Failed. Try Again."; 85 | button.classList.remove('is-grey'); 86 | button.classList.add('text-white'); 87 | button.classList.add('bg-red'); 88 | //Problem with the request (500 error) 89 | } 90 | } 91 | } 92 | 93 | function handleUpdateResponse() { 94 | if (request.readyState === XMLHttpRequest.DONE) { 95 | if (request.status === 200) { 96 | var response = JSON.parse(request.responseText); 97 | //Request successful 98 | console.log(response); 99 | update_title.innerHTML = "Finishing Updates..."; 100 | setTimeout(function() { 101 | document.getElementById('step-2').classList.add('transition-out'); 102 | setTimeout(function() { 103 | document.getElementById('step-2').classList.add('hidden'); 104 | document.getElementById('step-3').classList.remove('hidden'); 105 | document.getElementById('step-3').classList.remove('transition-out'); 106 | }, 500); 107 | }, 200); 108 | 109 | } else { 110 | document.querySelector('#step-2').classList.add('transition-out'); 111 | document.querySelector('#step-1').classList.remove('transition-out'); 112 | button.disabled = false; 113 | button.textContent = "Update Check Failed. Try Again."; 114 | button.classList.remove('is-grey'); 115 | button.classList.add('text-white'); 116 | button.classList.add('bg-red'); 117 | //Problem with the request (500 error) 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /app/logs.php: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /app/templates/dashboard.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MudPi Dashboard 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 |
23 |

Dashboard

24 |

System booted ago.

25 | 26 | 27 |

Sensors

28 |
29 | sensor as $sensor) { ?> 30 |
31 |
32 |

name ?? ''; ?>

33 | interface; ?> - 34 | pin)) { ?> 35 | Pin: pin; ?> 36 | 37 | address)) { ?> 38 | 0xaddress; ?> 39 | 40 |
41 | state->state)) { foreach($sensor->state->state as $key => $value) { ?> 42 |
43 |

44 |

45 |
46 | 47 |
48 |

state->state ?>classifier]; ?>

49 |

classifier); ?>

50 |
51 | 52 |
53 |
54 |
55 |

Updated: state->updated_at) && $sensor->state->updated_at ) ? timeForHumans(DateTime::createFromFormat('Y-m-d H:i:s', $sensor->state->updated_at)->format('U')) : 'never'?> ago

56 |
57 |
58 |
59 |
60 | 61 |
62 | 63 |
64 | 65 | 66 |
67 |
68 |
69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /app/templates/displays.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MudPi Displays 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 |
43 |

Displays

44 |

Control and monitor displays attached to MudPi.

45 |
46 | 47 |
48 | 49 |
50 | $display) { ?> 51 |
52 |
53 |
54 |
55 |

name); ?>

56 |

rows ?? "2"; ?> x columns ?? "16"; ?>

57 |

interface ?>

58 |
59 |
60 |

topic ?? ''; ?>

61 | address)) { ?> 62 | Address: 0xaddress; ?> 63 | 64 |
65 |
66 | 67 | 68 |
69 |
70 | 71 | 72 |
73 |
74 | 75 | 76 |
77 | 78 |
79 | 80 | key; ?>" placeholder="mudpi/char_dislay" type="hidden" name="topic"> 81 | 82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 | 90 |
91 | 92 | 93 |
94 |
95 |
96 |
97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/templates/logs.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MudPi UI - Logs 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 |
23 |

Logs

24 |

View the contents of MudPi log Files below.

25 | 26 |

MudPi Logs

27 |
28 | 29 |

MudPi Errors

30 |
31 | 32 |

Auto AP Mode

33 |
34 | 35 |

Wifi Config

36 |
37 | 38 |

Sprout

39 |
 $reading) {
51 | 									if(isset($reading["sensor"])) {
52 | 										echo $reading["sensor"];
53 | 										echo "\n";
54 | 									}
55 | 									echo $reading["value"]."mV";
56 | 									echo "\n";
57 | 									if(isset($reading["parsed"])) {
58 | 										echo $reading["parsed"]."%";
59 | 										echo "\n";
60 | 									}
61 | 									echo "Boot cycle: ". $reading["boots"];
62 | 									echo "\n----\n";
63 | 								}
64 | 								echo "\n\n";
65 | 							}
66 | 						 ?>
67 |
68 | 69 | 70 |
71 |
72 |
73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /app/templates/modal.php: -------------------------------------------------------------------------------- 1 | 28 | 29 | -------------------------------------------------------------------------------- /app/templates/new_account.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MudPi Setup - Create Account 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 46 | 47 | 48 |
49 |
50 |
51 |
52 |
53 |

MudPi Account

54 |

Register a new MudPi account to backup configurations and manage devices online.

55 |
56 | 57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 |
65 | 66 | 67 |
68 | 69 |
70 | 71 | 72 |
73 | 74 |
75 | 76 | 77 |
78 | 79 |
80 | 81 | 82 |
83 | 84 |
85 | 86 |

Skip if you already have an account.

87 | Skip 88 |
89 | 90 | 91 |
92 | 93 |
94 |
95 |
96 |
97 |
98 |
99 |

Welcome

100 |

Shortly an email will be sent to to confirm your account. Save your access token below somewhere safe. It has been encrypted and will only be shown once. This token is needed when configuring new devices on your account.

101 |

Token:

102 |

You may generate new tokens, but each device will need the new token.

103 | 104 |
105 |
106 |
107 |
108 |
109 | 110 |
111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /app/templates/partials/navigation.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/templates/toggles.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MudPi Relays 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 |
23 |

Toggle

24 |

Toggle components attached to MudPi.

25 |
26 | 27 | 28 |

Toggles

29 |
30 | toggle as $toggle) { ?> 31 |
32 |
33 |

name; ?>

34 | pin)) { ?> 35 | interface; ?> - 36 | Pin: pin; ?> 37 | 38 | (topic; ?>) 39 | key)) { ?> 40 |

key); ?>

41 | 42 |
43 |
44 |

state->state, FILTER_VALIDATE_BOOLEAN) ? "ON" : "OFF" ?>

45 |
46 |
47 |
48 |
49 | 50 |
51 | 52 |
53 | 54 | 55 |
56 |
57 |
58 |
59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/templates/welcome.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MudPi UI - Dashboard 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 |
23 |

Welcome!

24 |

In the next few steps you will be guided through the MudPi UI setup process.

25 |
26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/toggles.php: -------------------------------------------------------------------------------- 1 | connect(MUDPI_REDIS_HOST, MUDPI_REDIS_PORT); 15 | 16 | $config = json_decode(file_get_contents(MUDPI_PATH_CORE."/".MUDPI_CONFIG_FILE)); 17 | 18 | foreach($config->toggle as $toggle) { 19 | if(empty($toggle->name)) { 20 | $toggle->name = ucwords(str_replace("_", " ", $toggle->key)); 21 | } 22 | if(empty($toggle->topic)) { 23 | $toggle->topic = "toggle/".$toggle->key; 24 | } 25 | try { 26 | $state = $redis->get($toggle->key.'.state'); 27 | if (!empty($state)) { 28 | $toggle->state = json_decode($state); 29 | } 30 | else { 31 | throw new Exception('No State Found'); 32 | } 33 | } catch (Exception $e) { 34 | $toggle->state = (object)['component_id' => $toggle->key, 35 | 'state' => 0, 36 | 'updated_at' => '', 37 | 'metadata' => '']; 38 | } 39 | } 40 | 41 | 42 | include 'templates/toggles.php'; 43 | 44 | ?> -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mudpi/ui", 3 | "description": "A lightweight web dashboard to monitor MudPi.", 4 | "version": "0.2.0", 5 | "type": "library", 6 | "keywords": ["mudpi", "garden", "ui", "php", "raspberry pi"], 7 | "homepage": "https://mudpi.app", 8 | "time": "2020-01-01", 9 | "authors": [ 10 | { 11 | "name": "Eric Davisson", 12 | "email": "eric@mudpi.app", 13 | "homepage": "http://ericdavisson.com", 14 | "role": "Developer" 15 | } 16 | ], 17 | "support": { 18 | "email": "info@mudpi.app", 19 | "issues": "https://github.com/mudpi/ui/issues", 20 | "source": "https://github.com/mudpi/ui", 21 | "docs": "https://mudpi.app/docs" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "Mudpi\\": "app/", 26 | "Mudpi\\Tools\\": "app/tools/", 27 | "Mudpi\\Ajax\\": "app/ajax/" 28 | }, 29 | "files": ["includes/helpers.php", "includes/config.php"] 30 | }, 31 | "config": { 32 | "sort-packages": true 33 | }, 34 | "minimum-stability": "dev", 35 | "prefer-stable": true 36 | } -------------------------------------------------------------------------------- /configs/mudpi_ui.conf: -------------------------------------------------------------------------------- 1 | # Default server configuration for mudpi ui 2 | server { 3 | listen 80 default_server; 4 | listen [::]:80 default_server; 5 | 6 | root /var/www/html/mudpi/app; 7 | index index.php index.html index.htm; 8 | 9 | server_name _ mudpi.home; 10 | 11 | location / { 12 | try_files $uri $uri/ /index.php?$query_string; 13 | } 14 | 15 | # define error page 16 | error_page 404 = @notfound; 17 | 18 | location @notfound { 19 | return 302 $scheme://mudpi.home; 20 | } 21 | 22 | #Dont log images or noneresources 23 | location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ { 24 | access_log off; 25 | log_not_found off; 26 | expires 360d; 27 | } 28 | 29 | ## Begin - Security 30 | # deny all direct access for these folders 31 | location ~* /(.git|cache|bin|logs|backups|tests)/.*$ { return 403; } 32 | 33 | # deny running scripts inside core system folders 34 | location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } 35 | 36 | # deny running scripts inside user folder 37 | location ~* /user/.*\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } 38 | 39 | # deny access to specific files in the root folder 40 | location ~ /(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess) { return 403; } 41 | 42 | # deny access to .htaccess files, if Apache's document root 43 | # concurs with nginx's one 44 | location ~ /\.ht { 45 | deny all; 46 | } 47 | ## End - Security 48 | 49 | ## Begin - PHP 50 | location ~ \.php$ { 51 | fastcgi_pass unix:/var/run/php/php7.3-fpm.sock; 52 | #fastcgi_pass unix:/run/php/php7.0-fpm.sock; 53 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 54 | fastcgi_index index.php; 55 | fastcgi_intercept_errors on; #captive portal 56 | include fastcgi_params; 57 | fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; 58 | } 59 | } -------------------------------------------------------------------------------- /includes/autoloader.php: -------------------------------------------------------------------------------- 1 | '; 33 | } 34 | 35 | function csrf_meta() { 36 | $token = htmlspecialchars($_SESSION['csrf_token']); 37 | return ''; 38 | } 39 | 40 | function csrf_token() { 41 | $token = htmlspecialchars($_SESSION['csrf_token']); 42 | return $token; 43 | } 44 | 45 | function validate_csrf_token() { 46 | $csrf_token = $_POST['csrf_token'] ?? null; 47 | $header_token = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null; 48 | 49 | //Check if a token is even set 50 | if (empty($csrf_token) && empty($header_token)) { 51 | return false; 52 | } 53 | 54 | //Check to see if there is a post token otherwise use the server token. 55 | $request_token = $csrf_token ?: $header_token; 56 | 57 | return hash_equals($_SESSION['csrf_token'], $request_token); 58 | } 59 | 60 | function handle_invalid_csrf() 61 | { 62 | response_error('Invalid CSRF token or session expired'); 63 | } 64 | 65 | function response_error($error = "There was an error.") 66 | { 67 | header('HTTP/1.1 500 Internal Server Error'); 68 | header('Content-Type: text/plain'); 69 | echo $error; 70 | exit; 71 | } 72 | 73 | //Used to parse general configrations like key value files or lighthttp 74 | function parseConfig($configuration) { 75 | $config = array(); 76 | foreach ($configuration as $line) { 77 | $line = trim($line); 78 | if ($line == "" || $line[0] == "#") { 79 | continue; //skip empty lines and comments 80 | } 81 | //Fancy way to spit config file on the '=' and trim the values 82 | list($option, $value) = array_map("trim", explode("=", $line, 2)); 83 | 84 | if (empty($config[$option])) { 85 | $config[$option] = $value ?: null; 86 | } else { 87 | if (!is_array($config[$option])) { 88 | $config[$option] = [ $config[$option] ]; 89 | } 90 | $config[$option][] = $value; 91 | } 92 | } 93 | return $config; 94 | } 95 | 96 | // Helpers for system commands and fetching system info 97 | function getConnectedNetworkName() { 98 | exec("iwgetid ". MUDPI_WIFI_INTERFACE . ' -r', $name); 99 | return $name; 100 | } 101 | 102 | function getNetworkInfo() { 103 | exec("ls /sys/class/net | grep -v lo", $interfaces); 104 | foreach ($interfaces as $interface) { 105 | exec("ip a show $interface", $$interface); 106 | } 107 | return $interfaces; 108 | } 109 | 110 | function reboot() { 111 | $result = shell_exec("sudo /sbin/reboot"); 112 | return $result; 113 | } 114 | 115 | function shutdown() { 116 | $result = shell_exec("sudo /sbin/shutdown -h now"); 117 | return $result; //Not going to get this message ever... 118 | } 119 | 120 | function getCurrentDirectory() { 121 | exec('pwd', $directory); 122 | return $directory; 123 | } 124 | 125 | function switchDirectory($directory) { 126 | if (isset($directory)) { 127 | exec("cd ".$directory); 128 | } 129 | } 130 | 131 | function makeDirectory($directory, $createParentDirectories = false) { 132 | if (isset($directory)) { 133 | if($createParentDirectories) { 134 | $result = exec("mkdir ".$directory. " -p"); 135 | } 136 | else { 137 | $result = exec("mkdir ".$directory); 138 | } 139 | return $result; 140 | } 141 | return false; 142 | } 143 | 144 | function listFiles($path = null) { 145 | if($path) { 146 | exec("ls ".$path." -lah", $results); 147 | } 148 | else { 149 | exec("ls -lah", $results); 150 | } 151 | return $results; 152 | } 153 | 154 | function timeForHumans ($time) 155 | { 156 | 157 | $time = time() - $time; // to get the time since that moment 158 | $time = ($time<1)? 1 : $time; 159 | $tokens = array ( 160 | 31536000 => 'year', 161 | 2592000 => 'month', 162 | 604800 => 'week', 163 | 86400 => 'day', 164 | 3600 => 'hour', 165 | 60 => 'minute', 166 | 1 => 'second' 167 | ); 168 | 169 | foreach ($tokens as $unit => $text) { 170 | if ($time < $unit) continue; 171 | $numberOfUnits = floor($time / $unit); 172 | return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':''); 173 | } 174 | } 175 | 176 | function slug($string, $spacer = "_"){ 177 | return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', $spacer, $string), $spacer)); 178 | } 179 | 180 | function parseReading($type = "general", $value = null) { 181 | $type = strtolower($type); 182 | switch($type) { 183 | case "humidity": 184 | return json_decode($value); 185 | break; 186 | case "bme680": 187 | // {"temperature": 66.5, "humidity": 59.9, "pressure": 994.01, "gas": 36187, "altitude": 161.457} 188 | return json_decode($value); 189 | break; 190 | case "soil": 191 | return [ "Moisture" => $value ]; 192 | break; 193 | case "float": 194 | return [ "Float" => $value ]; 195 | break; 196 | case "temperature": 197 | return [ "Temperature" => $value ]; 198 | break; 199 | case "rain": 200 | return [ "Rain" => $value ]; 201 | break; 202 | default: 203 | return [ "General" => $value ]; 204 | break; 205 | } 206 | } 207 | 208 | function is_json($string) { 209 | json_decode($string); 210 | return (json_last_error() == JSON_ERROR_NONE); 211 | } --------------------------------------------------------------------------------