Fixed broken hostname change redirect, Split admin page, Added basic Wifi configuration.
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 20 Mar 2018 00:32:18 +0000 (17:32 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 20 Mar 2018 00:32:18 +0000 (17:32 -0700)
16 files changed:
CHANGELOG.md
jshint.json
scripts/config-wifi [new file with mode: 0755]
scripts/jogtest.py [deleted file]
setup.py
src/jade/index.jade
src/jade/templates/admin-general-view.jade [new file with mode: 0644]
src/jade/templates/admin-network-view.jade [new file with mode: 0644]
src/jade/templates/admin-view.jade [deleted file]
src/jade/templates/control-view.jade
src/js/admin-general-view.js [new file with mode: 0644]
src/js/admin-network-view.js [new file with mode: 0644]
src/js/admin-view.js [deleted file]
src/js/app.js
src/py/bbctrl/Web.py
src/stylus/style.styl

index 34c3e8c284cb8a6e0ee740d31b4cf4e4a27fd466..c6104b13d4db0ccb8d76fa697fd14716140ef564 100644 (file)
@@ -5,6 +5,10 @@ Buildbotics CNC Controller Firmware Change Log
  - Eliminated drift caused by miscounting half microsteps.
  - Fixed disappearing GCode in Web.
  - More efficient GCode scrolling with very large files.
+ - Fully functional soft-limited jogging.
+ - Added basic Wifi configuration to admin tab.
+ - Fixed broken hostname change redirect.
+ - Split admin page.
 
 ## v0.3.19
  - Fixed stopping problems. #127
index 4a7add21547db8570375d0ac8e3a6352405dcccc..bb0cbb3f05897d9a182459a6d35fb2b03cf1a162 100644 (file)
@@ -9,6 +9,7 @@
     "module": false,
     "Vue": false,
     "SockJS": false,
-    "Gauge": false
+    "Gauge": false,
+    "Clusterize": false
   }
 }
diff --git a/scripts/config-wifi b/scripts/config-wifi
new file mode 100755 (executable)
index 0000000..4b6f9a4
--- /dev/null
@@ -0,0 +1,240 @@
+#!/bin/bash -e
+
+AP=false
+SSID=
+PASS=
+CHANNEL=7
+REBOOT=false
+
+WLAN0_CFG=/etc/network/interfaces.d/wlan0
+HOSTAPD_CFG=/etc/hostapd/hostapd.conf
+DNSMASQ_CFG=/etc/dnsmasq.conf
+DHCPCD_CFG=/etc/dhcpcd.conf
+WPA_CFG=/etc/wpa_supplicant/wpa_supplicant.conf
+
+
+function query_ssid() {
+    if [ -e $WLAN0_CFG ]; then
+        grep wpa-ssid $WLAN0_CFG | sed 's/^[[:space:]]*wpa-ssid "\([^"]*\)"/\1/'
+    else
+        if [ -e $HOSTAPD_CFG ]; then
+            grep ^ssid $HOSTAPD_CFG | sed 's/^ssid="\(.*\)$"/\1/'
+        fi
+    fi
+}
+
+
+function configure_wlan0() {
+    echo "auto wlan0"
+    echo "allow-hotplug wlan0"
+    echo "iface wlan0 inet dhcp"
+    echo "  wpa-scan-ssid 1"
+    echo "  wpa-ap-scan 1"
+    echo "  wpa-key-mgmt WPA-PSK"
+    echo "  wpa-proto RSN WPA"
+    echo "  wpa-pairwise CCMP TKIP"
+    echo "  wpa-group CCMP TKIP"
+    echo "  wpa-ssid \"$SSID\""
+    if [ ${#PASS} -ne 0 ]; then
+        echo "  wpa-psk \"$PASS\""
+    fi
+}
+
+
+function configure_wpa() {
+    echo "country=US"
+    echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev"
+    echo "update_config=1"
+
+    if [ ${#PASS} -eq 0 ]; then
+        echo "network={"
+        echo "  ssid=\"$SSID\""
+        echo "  key_mgmt=NONE"
+        echo "}"
+    fi
+}
+
+
+function configure_dhcpcd() {
+    echo "hostname"
+    echo "clientid"
+    echo "persistent"
+    echo "option rapid_commit"
+    echo "option domain_name_servers, domain_name, domain_search, host_name"
+    echo "option classless_static_routes"
+    echo "option ntp_servers"
+    echo "option interface_mtu"
+    echo "require dhcp_server_identifier"
+    echo "slaac private"
+
+    if $AP; then
+        echo
+        echo "interface wlan0"
+        echo "  static ip_address=192.168.43.1/24"
+    fi
+}
+
+
+function configure_wifi() {
+    # Disable AP
+    service hostapd stop || true
+    rm -f /etc/default/hostapd
+
+    configure_wlan0 > $WLAN0_CFG
+    configure_wpa > $WPA_CFG
+    configure_dhcpcd  > $DHCPCD_CFG
+    service dhcpcd restart
+}
+
+
+function configure_dnsmasq() {
+    echo "interface=wlan0"
+    echo "domain-needed"
+    echo "bogus-priv"
+    echo "dhcp-range=192.168.43.2,192.168.43.20,255.255.255.0,12h"
+}
+
+
+function configure_hostapd() {
+    echo "interface=wlan0"
+    echo "driver=nl80211"
+    echo "ssid=$SSID"
+    echo "hw_mode=g"
+    echo "channel=$CHANNEL"
+    echo "wmm_enabled=0"
+    echo "macaddr_acl=0"
+    echo "auth_algs=1"
+    echo "ignore_broadcast_ssid=0"
+    echo "wpa=2"
+    echo "wpa_passphrase=$PASS"
+    echo "wpa_key_mgmt=WPA-PSK"
+    echo "wpa_pairwise=TKIP"
+    echo "rsn_pairwise=CCMP"
+}
+
+
+function is_installed() {
+    dpkg-query -W --showformat='${Status}' $1 |
+        grep "install ok installed" >/dev/null
+    if [ $? -eq 0 ]; then echo true; else echo false; fi
+}
+
+
+function configure_ap() {
+    # Disable wifi config
+    rm -f $WLAN0_CFG
+
+    # Install packages
+    (
+        $(is_installed dnsmasq) &&
+        $(is_installed hostapd) &&
+        $(is_installed iptables-persistent)
+
+    ) || (
+        export DEBIAN_FRONTEND=noninteractive
+        apt-get update
+        apt-get install -yq dnsmasq hostapd iptables-persistent
+    )
+
+    configure_dhcpcd  > $DHCPCD_CFG
+    configure_dnsmasq > $DNSMASQ_CFG
+    configure_hostapd > $HOSTAPD_CFG
+
+    echo "DAEMON_CONF=\"/etc/hostapd/hostapd.conf\"" > /etc/default/hostapd
+
+    # Enable IP forwarding
+    sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
+    echo 1 > /proc/sys/net/ipv4/ip_forward
+
+    # Enable IP masquerading
+    iptables -t nat -A  POSTROUTING -o eth0 -j MASQUERADE
+    iptables-save > /etc/iptables/rules.v4
+
+    # Start services
+    service dhcpcd restart
+    service dnsmasq restart
+    service hostapd restart
+}
+
+
+function usage() {
+    echo "Usage: config-wifi [OPTIONS]"
+    echo
+    echo "Configure wifi as either a client or access point."
+    echo
+    echo "OPTIONS:"
+    echo
+    echo "  -a             Configure access point."
+    echo "  -r             Reboot when done."
+    echo "  -s <SSID>      Set SSID."
+    echo "  -p <PASS>      Set password."
+    echo "  -c <CHANNEL>   Set wifi channel."
+    echo "  -g             Report SSID and exit."
+    echo
+}
+
+
+# Parse args
+while [ $# -ne 0 ]; do
+    case "$1" in
+        -a) AP=true ;;
+        -r) REBOOT=true; ;;
+        -s) SSID="$2"; shift ;;
+        -p) PASS="$2"; shift ;;
+        -c) CHANNEL="$2"; shift ;;
+        -g) query_ssid; exit 0 ;;
+
+        -h)
+            usage
+            exit 0
+            ;;
+
+        *)
+            usage
+            echo "Unknown argument '$1'"
+            exit 1
+    esac
+
+    shift
+done
+
+
+# Check args
+function clean_str() {
+    echo "$1" | tr -d '\n\r"'
+}
+
+SSID=$(clean_str "$SSID")
+PASS=$(clean_str "$PASS")
+
+LANG=C LC_ALL=C # For correct string byte length
+
+if [ ${#SSID} -eq 0 -o 32 -lt ${#SSID} ]; then
+    echo "Invalid or missing SSID '$SSID'"
+    exit 1
+fi
+
+if [ ${#PASS} -lt 8 -o 128 -lt ${#PASS} ]; then
+    echo "Invalid passsword"
+    exit 1
+fi
+
+echo "$CHANNEL" | grep '^[0-9]\{1,2\}' > /dev/null
+if [ $? -ne 0 ]; then
+    echo "Invalid channel '$CHANNEL'"
+    exit 1
+fi
+
+
+# Execute
+if $AP; then
+    echo "Configuring Wifi access point"
+    configure_ap
+
+else
+    echo "Configuring Wifi"
+    configure_wifi
+fi
+
+
+if $REBOOT; then nohup reboot & fi
diff --git a/scripts/jogtest.py b/scripts/jogtest.py
deleted file mode 100755 (executable)
index 709156b..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import tornado.ioloop
-
-from inevent import InEvent, JogHandler
-from inevent.Constants import *
-
-
-class Handler(JogHandler):
-  def changed(self):
-    scale = 1.0
-    if self.speed == 1: scale = 1.0 / 128.0
-    if self.speed == 2: scale = 1.0 / 32.0
-    if self.speed == 3: scale = 1.0 / 4.0
-
-    print(', '.join(list(map(lambda x: '%.3f' % (x * scale), self.axes))))
-    sys.stdout.flush()
-
-
-if __name__ == "__main__":
-  # Load config
-  config = {
-      "Logitech Logitech RumblePad 2 USB": {
-          "deadband": 0.1,
-          "axes":     [ABS_X, ABS_Y, ABS_RZ, ABS_Z],
-          "dir":      [1, -1, -1, 1],
-          "arrows":   [ABS_HAT0X, ABS_HAT0Y],
-          "speed":    [0x120, 0x121, 0x122, 0x123],
-          "lock":     [0x124, 0x125],
-          },
-
-      "default": {
-          "deadband": 0.1,
-          "axes":     [ABS_X, ABS_Y, ABS_RY, ABS_RX],
-          "dir":      [1, -1, -1, 1],
-          "arrows":   [ABS_HAT0X, ABS_HAT0Y],
-          "speed":    [0x133, 0x130, 0x131, 0x134],
-          "lock":     [0x136, 0x137],
-          }
-      }
-
-  # Create ioloop
-  ioloop = tornado.ioloop.IOLoop.current()
-
-  # Listen for input events
-  handler = Handler(config)
-  events = InEvent(ioloop, handler, types = "js kbd".split())
-
-  ioloop.start()
index 561b52b7223f76944bf8c9c19c26c2093d14653c..2c83c270ef72289af51b04212e89950e2a166548 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -29,6 +29,7 @@ setup(
         'scripts/upgrade-bbctrl',
         'scripts/sethostname',
         'scripts/reset-video',
+        'scripts/config-wifi',
         ],
     install_requires = 'tornado sockjs-tornado pyserial pyudev smbus2'.split(),
     zip_safe = False,
index c25fac1bdd881d315c12264671db44e7bf77d251..633687d00eaa865f0c5fcae50c230e46548f434b 100644 (file)
@@ -81,7 +81,13 @@ html(lang="en")
               a.pure-menu-link(href="#gcode") Gcode
 
             li.pure-menu-heading
-              a.pure-menu-link(href="#admin") Admin
+              a.pure-menu-link(href="#admin-general") Admin
+
+            li.pure-menu-item
+              a.pure-menu-link(href="#admin-general") General
+
+            li.pure-menu-item
+              a.pure-menu-link(href="#admin-network") Network
 
             li.pure-menu-heading
               a.pure-menu-link(href="#cheat-sheet") Cheat Sheet
diff --git a/src/jade/templates/admin-general-view.jade b/src/jade/templates/admin-general-view.jade
new file mode 100644 (file)
index 0000000..14afba4
--- /dev/null
@@ -0,0 +1,67 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-              This file is part of the Buildbotics firmware.               //
+//-                                                                           //
+//-                Copyright (c) 2015 - 2018, Buildbotics LLC                 //
+//-                           All rights reserved.                            //
+//-                                                                           //
+//-   This file ("the software") is free software: you can redistribute it    //
+//-   and/or modify it under the terms of the GNU General Public License,     //
+//-    version 2 as published by the Free Software Foundation. You should     //
+//-    have received a copy of the GNU General Public License, version 2      //
+//-   along with the software. If not, see <http://www.gnu.org/licenses/>.    //
+//-                                                                           //
+//-   The software is distributed in the hope that it will be useful, but     //
+//-        WITHOUT ANY WARRANTY; without even the implied warranty of         //
+//-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      //
+//-             Lesser General Public License for more details.               //
+//-                                                                           //
+//-     You should have received a copy of the GNU Lesser General Public      //
+//-              License along with the software.  If not, see                //
+//-                     <http://www.gnu.org/licenses/>.                       //
+//-                                                                           //
+//-              For information regarding this software email:               //
+//-                "Joseph Coffland" <joseph@buildbotics.com>                 //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#admin-general-view-template(type="text/x-template")
+  #admin-general
+    h2 Firmware
+    button.pure-button.pure-button-primary(@click="check") Check
+    button.pure-button.pure-button-primary(@click="upgrade") Upgrade
+    label.pure-button.pure-button-primary.file-upload
+      input(type="file", accept=".tar.bz2", @change="upload")
+      | Upload
+
+    p
+      input(type="checkbox", v-model="autoCheckUpgrade",
+        @change="change_auto_check_upgrade")
+      label(for="auto-check-upgrade") &nbsp; Automatically check for upgrades
+
+    h2 Configuration
+    button.pure-button.pure-button-primary(@click="backup") Backup
+
+    label.pure-button.pure-button-primary.file-upload
+      input(type="file", accept=".json", @change="restore")
+      | Restore
+    message(:show.sync="configRestored")
+      h3(slot="header") Success
+      p(slot="body") Configuration restored.
+
+    button.pure-button.pure-button-primary(@click="confirmReset = true")
+      | Reset
+    message(:show.sync="confirmReset")
+      h3(slot="header") Reset to default configuration?
+      p(slot="body") All configuration changes will be lost.
+      div(slot="footer")
+        button.pure-button(@click="confirmReset = false") Cancel
+        button.pure-button.button-success(@click="reset") OK
+
+    message(:show.sync="configReset")
+      h3(slot="header") Success
+      p(slot="body") Configuration reset.
+
+    h2 Debugging
+    a(href="/api/log", target="_blank")
+      button.pure-button.pure-button-primary View Log
diff --git a/src/jade/templates/admin-network-view.jade b/src/jade/templates/admin-network-view.jade
new file mode 100644 (file)
index 0000000..e31cb3a
--- /dev/null
@@ -0,0 +1,103 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-              This file is part of the Buildbotics firmware.               //
+//-                                                                           //
+//-                Copyright (c) 2015 - 2018, Buildbotics LLC                 //
+//-                           All rights reserved.                            //
+//-                                                                           //
+//-   This file ("the software") is free software: you can redistribute it    //
+//-   and/or modify it under the terms of the GNU General Public License,     //
+//-    version 2 as published by the Free Software Foundation. You should     //
+//-    have received a copy of the GNU General Public License, version 2      //
+//-   along with the software. If not, see <http://www.gnu.org/licenses/>.    //
+//-                                                                           //
+//-   The software is distributed in the hope that it will be useful, but     //
+//-        WITHOUT ANY WARRANTY; without even the implied warranty of         //
+//-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      //
+//-             Lesser General Public License for more details.               //
+//-                                                                           //
+//-     You should have received a copy of the GNU Lesser General Public      //
+//-              License along with the software.  If not, see                //
+//-                     <http://www.gnu.org/licenses/>.                       //
+//-                                                                           //
+//-              For information regarding this software email:               //
+//-                "Joseph Coffland" <joseph@buildbotics.com>                 //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#admin-network-view-template(type="text/x-template")
+  #admin-network
+    h2 Hostname
+    .pure-form.pure-form-aligned
+      .pure-control-group
+        label(for="hostname") Hostname
+        input(name="hostname", v-model="hostname", @keyup.enter="set_hostname")
+        button.pure-button.pure-button-primary(@click="set_hostname") Set
+
+    message(:show.sync="hostnameSet")
+      h3(slot="header") Hostname Set
+      div(slot="body")
+        p Hostname was successfuly set to <strong>{{hostname}}</strong>.
+        p Rebooting to apply changes.
+        p Redirecting to new hostname in {{redirectTimeout}} seconds.
+      div(slot="footer")
+
+    h2 Remote SSH User
+    .pure-form.pure-form-aligned
+      .pure-control-group
+        label(for="username") Username
+        input(name="username", v-model="username", @keyup.enter="set_username")
+        button.pure-button.pure-button-primary(@click="set_username") Set
+
+    .pure-form.pure-form-aligned
+      .pure-control-group
+        label(for="current") Current Password
+        input(name="current", v-model="current", type="password")
+      .pure-control-group
+        label(for="pass1") New Password
+        input(name="pass1", v-model="password", type="password")
+      .pure-control-group
+        label(for="pass2") New Password
+        input(name="pass2", v-model="password2", type="password")
+        button.pure-button.pure-button-primary(@click="set_password") Set
+
+    message(:show.sync="passwordSet")
+      h3(slot="header") Password Set
+      p(slot="body")
+
+    message(:show.sync="usernameSet")
+      h3(slot="header") Username Set
+      p(slot="body")
+
+    h2 Wifi Setup
+    .pure-form.pure-form-aligned
+      .pure-control-group
+        label(for="ssid") SSID
+        input(name="ssid", v-model="wifi_ssid")
+      .pure-control-group
+        label(for="wifi_pass") Password
+        input(name="wifi_pass", v-model="wifi_pass", type="password")
+        button.pure-button.pure-button-primary(@click="wifiConfirm = true") Set
+
+    message(:show.sync="wifiConfirm")
+      h3(slot="header") Configure Wifi and reboot?
+      div(slot="body")
+        p
+          | After configuring the Wifi settings the controller will
+          | automatically reboot.
+        table
+          tr
+            th SSID
+            td &nbsp;{{wifi_ssid}}
+          tr
+            th Auth
+            td &nbsp;{{wifi_pass ? 'Password' : 'Open'}}
+
+      div(slot="footer")
+        button.pure-button(@click="wifiConfirm = false") Cancel
+        button.pure-button.button-success(@click="config_wifi") OK
+
+    message(:show.sync="rebooting")
+      h3(slot="header") Rebooting
+      p(slot="body") Please wait...
+      div(slot="footer")
diff --git a/src/jade/templates/admin-view.jade b/src/jade/templates/admin-view.jade
deleted file mode 100644 (file)
index ff0a9ac..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-//-/////////////////////////////////////////////////////////////////////////////
-//-                                                                           //
-//-              This file is part of the Buildbotics firmware.               //
-//-                                                                           //
-//-                Copyright (c) 2015 - 2018, Buildbotics LLC                 //
-//-                           All rights reserved.                            //
-//-                                                                           //
-//-   This file ("the software") is free software: you can redistribute it    //
-//-   and/or modify it under the terms of the GNU General Public License,     //
-//-    version 2 as published by the Free Software Foundation. You should     //
-//-    have received a copy of the GNU General Public License, version 2      //
-//-   along with the software. If not, see <http://www.gnu.org/licenses/>.    //
-//-                                                                           //
-//-   The software is distributed in the hope that it will be useful, but     //
-//-        WITHOUT ANY WARRANTY; without even the implied warranty of         //
-//-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      //
-//-             Lesser General Public License for more details.               //
-//-                                                                           //
-//-     You should have received a copy of the GNU Lesser General Public      //
-//-              License along with the software.  If not, see                //
-//-                     <http://www.gnu.org/licenses/>.                       //
-//-                                                                           //
-//-              For information regarding this software email:               //
-//-                "Joseph Coffland" <joseph@buildbotics.com>                 //
-//-                                                                           //
-//-/////////////////////////////////////////////////////////////////////////////
-
-script#admin-view-template(type="text/x-template")
-  #admin
-    h2 Hostname
-    .pure-form.pure-form-aligned
-      .pure-control-group
-        label(for="hostname") Hostname
-        input(name="hostname", v-model="hostname", @keyup.enter="set_hostname")
-        button.pure-button.pure-button-primary(@click="set_hostname") Set
-
-    h2 Remote SSH User
-    .pure-form.pure-form-aligned
-      .pure-control-group
-        label(for="username") Username
-        input(name="username", v-model="username", @keyup.enter="set_username")
-        button.pure-button.pure-button-primary(@click="set_username") Set
-
-    .pure-form.pure-form-aligned
-      .pure-control-group
-        label(for="current") Current Password
-        input(name="current", v-model="current", type="password")
-      .pure-control-group
-        label(for="pass1") New Password
-        input(name="pass1", v-model="password", type="password")
-      .pure-control-group
-        label(for="pass2") New Password
-        input(name="pass2", v-model="password2", type="password")
-        button.pure-button.pure-button-primary(@click="set_password") Set
-
-    h2 Debugging
-    a(href="/api/log", target="_blank")
-      button.pure-button.pure-button-primary View Log
-
-    h2 Configuration
-    button.pure-button.pure-button-primary(@click="backup") Backup
-
-    label.pure-button.pure-button-primary.file-upload
-      input(type="file", accept=".json", @change="restore")
-      | Restore
-    message(:show.sync="configRestored")
-      h3(slot="header") Success
-      p(slot="body") Configuration restored.
-
-    button.pure-button.pure-button-primary(@click="confirmReset = true")
-      | Reset
-    message(:show.sync="confirmReset")
-      h3(slot="header") Reset to default configuration?
-      p(slot="body") All configuration changes will be lost.
-      div(slot="footer")
-        button.pure-button(@click="confirmReset = false") Cancel
-        button.pure-button.button-success(@click="reset") OK
-
-    message(:show.sync="configReset")
-      h3(slot="header") Success
-      p(slot="body") Configuration reset.
-
-    h2 Firmware
-    button.pure-button.pure-button-primary(@click="check") Check
-    button.pure-button.pure-button-primary(@click="upgrade") Upgrade
-    label.pure-button.pure-button-primary.file-upload
-      input(type="file", accept=".tar.bz2", @change="upload")
-      | Upload
-
-    p
-      input(type="checkbox", v-model="autoCheckUpgrade",
-        @change="change_auto_check_upgrade")
-      label(for="auto-check-upgrade") &nbsp; Automatically check for upgrades
-
-    message(:show.sync="hostnameSet")
-      h3(slot="header") Hostname Set
-      div(slot="body")
-        p Hostname was successfuly set to <strong>{{hostname}}</strong>.
-        p Rebooting to apply changes.
-        p Redirecting to new hostname in {{redirectTimeout}} seconds.
-      div(slot="footer")
-
-    message(:show.sync="passwordSet")
-      h3(slot="header") Password Set
-      p(slot="body")
-
-    message(:show.sync="usernameSet")
-      h3(slot="header") Username Set
-      p(slot="body")
index 90880ee953e49c37063c623a423979e963317ef7..cb61a9d028328861fb06cdd23603c0538b878aad 100644 (file)
@@ -209,7 +209,8 @@ script#control-view-template(type="text/x-template")
             style="display:none", accept=".nc,.gcode,.gc,.ngc")
 
           button.pure-button(title="Delete current GCode program.",
-            @click="deleteGCode = true", :disabled="!state.selected")
+            @click="deleteGCode = true",
+            :disabled="!state.selected || is_running || is_stopping")
             .fa.fa-trash
 
           message(:show.sync="deleteGCode")
diff --git a/src/js/admin-general-view.js b/src/js/admin-general-view.js
new file mode 100644 (file)
index 0000000..7ae1af0
--- /dev/null
@@ -0,0 +1,122 @@
+/******************************************************************************\
+
+                 This file is part of the Buildbotics firmware.
+
+                   Copyright (c) 2015 - 2018, Buildbotics LLC
+                              All rights reserved.
+
+      This file ("the software") is free software: you can redistribute it
+      and/or modify it under the terms of the GNU General Public License,
+       version 2 as published by the Free Software Foundation. You should
+       have received a copy of the GNU General Public License, version 2
+      along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+      The software is distributed in the hope that it will be useful, but
+           WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+                Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public
+                 License along with the software.  If not, see
+                        <http://www.gnu.org/licenses/>.
+
+                 For information regarding this software email:
+                   "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+'use strict'
+
+
+var api = require('./api');
+
+
+module.exports = {
+  template: '#admin-general-view-template',
+  props: ['config', 'state'],
+
+
+  data: function () {
+    return {
+      configRestored: false,
+      confirmReset: false,
+      configReset: false,
+      latest: '',
+      autoCheckUpgrade: true
+    }
+  },
+
+
+  events: {
+    latest_version: function (version) {this.latest = version}
+  },
+
+
+  ready: function () {
+    this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']
+  },
+
+
+  methods: {
+    backup: function () {
+      document.getElementById('download-target').src = '/api/config/download';
+    },
+
+
+    restore: function (e) {
+      var files = e.target.files || e.dataTransfer.files;
+      if (!files.length) return;
+
+      var fr = new FileReader();
+      fr.onload = function (e) {
+        var config;
+
+        try {
+          config = JSON.parse(e.target.result);
+        } catch (ex) {
+          alert("Invalid config file");
+          return;
+        }
+
+        api.put('config/save', config).done(function (data) {
+          this.$dispatch('update');
+          this.configRestored = true;
+
+        }.bind(this)).fail(function (error) {
+          alert('Restore failed: ' + error);
+        })
+      }.bind(this);
+
+      fr.readAsText(files[0]);
+    },
+
+
+    reset: function () {
+      this.confirmReset = false;
+      api.put('config/reset').done(function () {
+        this.$dispatch('update');
+        this.configReset = true;
+
+      }.bind(this)).fail(function (error) {
+        alert('Reset failed: ' + error);
+      });
+    },
+
+
+    check: function () {this.$dispatch('check')},
+    upgrade: function () {this.$dispatch('upgrade')},
+
+
+    upload: function (e) {
+      var files = e.target.files || e.dataTransfer.files;
+      if (!files.length) return;
+      this.$dispatch('upload', files[0]);
+    },
+
+
+    change_auto_check_upgrade: function () {
+      this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade;
+      this.$dispatch('config-changed');
+    }
+  }
+}
diff --git a/src/js/admin-network-view.js b/src/js/admin-network-view.js
new file mode 100644 (file)
index 0000000..143b157
--- /dev/null
@@ -0,0 +1,146 @@
+/******************************************************************************\
+
+                 This file is part of the Buildbotics firmware.
+
+                   Copyright (c) 2015 - 2018, Buildbotics LLC
+                              All rights reserved.
+
+      This file ("the software") is free software: you can redistribute it
+      and/or modify it under the terms of the GNU General Public License,
+       version 2 as published by the Free Software Foundation. You should
+       have received a copy of the GNU General Public License, version 2
+      along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+      The software is distributed in the hope that it will be useful, but
+           WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+                Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public
+                 License along with the software.  If not, see
+                        <http://www.gnu.org/licenses/>.
+
+                 For information regarding this software email:
+                   "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+'use strict'
+
+
+var api = require('./api');
+
+
+module.exports = {
+  template: '#admin-network-view-template',
+  props: ['config', 'state'],
+
+
+  data: function () {
+    return {
+      hostnameSet: false,
+      usernameSet: false,
+      passwordSet: false,
+      redirectTimeout: 0,
+      hostname: '',
+      username: '',
+      current: '',
+      password: '',
+      password2: '',
+      wifi_ssid: '',
+      wifi_pass: '',
+      wifiConfirm: false,
+      rebooting: false
+    }
+  },
+
+
+  ready: function () {
+    api.get('hostname').done(function (hostname) {
+      this.hostname = hostname;
+    }.bind(this));
+
+    api.get('remote/username').done(function (username) {
+      this.username = username;
+    }.bind(this));
+
+    api.get('wifi').done(function (config) {
+      this.wifi_ssid = config.ssid;
+    }.bind(this));
+  },
+
+
+  methods: {
+    redirect: function (hostname) {
+      if (0 < this.redirectTimeout) {
+        this.redirectTimeout -= 1;
+        setTimeout(function () {this.redirect(hostname)}.bind(this), 1000);
+
+      } else location.hostname = hostname;
+    },
+
+
+    set_hostname: function () {
+      api.put('hostname', {hostname: this.hostname}).done(function () {
+        this.redirectTimeout = 45;
+        this.hostnameSet = true;
+
+        api.put('reboot').always(function () {
+          var hostname = this.hostname;
+          if (String(location.hostname).endsWith('.local'))
+            hostname += '.local'
+          this.$dispatch('hostname-changed', hostname);
+          this.redirect(hostname);
+        }.bind(this));
+
+      }.bind(this)).fail(function (error) {
+        alert('Set hostname failed: ' + JSON.stringify(error));
+      })
+    },
+
+
+    set_username: function () {
+      api.put('remote/username', {username: this.username}).done(function () {
+        this.usernameSet = true;
+      }.bind(this)).fail(function (error) {
+        alert('Set username failed: ' + JSON.stringify(error));
+      })
+    },
+
+
+    set_password: function () {
+      if (this.password != this.password2) {
+        alert('Passwords to not match');
+        return;
+      }
+
+      if (this.password.length < 6) {
+        alert('Password too short');
+        return;
+      }
+
+      api.put('remote/password', {
+        current: this.current,
+        password: this.password
+      }).done(function () {
+        this.passwordSet = true;
+      }.bind(this)).fail(function (error) {
+        alert('Set password failed: ' + JSON.stringify(error));
+      })
+    },
+
+
+    config_wifi: function () {
+      this.wifiConfirm = false;
+      this.rebooting = true;
+
+      api.put('wifi', {
+        ssid: this.wifi_ssid,
+        pass: this.wifi_pass
+
+      }).fail(function (error) {
+        alert('Failed to configure WiFi: ' + JSON.stringify(error));
+      })
+    }
+  }
+}
diff --git a/src/js/admin-view.js b/src/js/admin-view.js
deleted file mode 100644 (file)
index 0be0e04..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/******************************************************************************\
-
-                 This file is part of the Buildbotics firmware.
-
-                   Copyright (c) 2015 - 2018, Buildbotics LLC
-                              All rights reserved.
-
-      This file ("the software") is free software: you can redistribute it
-      and/or modify it under the terms of the GNU General Public License,
-       version 2 as published by the Free Software Foundation. You should
-       have received a copy of the GNU General Public License, version 2
-      along with the software. If not, see <http://www.gnu.org/licenses/>.
-
-      The software is distributed in the hope that it will be useful, but
-           WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-                Lesser General Public License for more details.
-
-        You should have received a copy of the GNU Lesser General Public
-                 License along with the software.  If not, see
-                        <http://www.gnu.org/licenses/>.
-
-                 For information regarding this software email:
-                   "Joseph Coffland" <joseph@buildbotics.com>
-
-\******************************************************************************/
-
-'use strict'
-
-
-var api = require('./api');
-
-
-module.exports = {
-  template: '#admin-view-template',
-  props: ['config', 'state'],
-
-
-  data: function () {
-    return {
-      configRestored: false,
-      confirmReset: false,
-      configReset: false,
-      hostnameSet: false,
-      usernameSet: false,
-      passwordSet: false,
-      redirectTimeout: 0,
-      latest: '',
-      hostname: '',
-      username: '',
-      current: '',
-      password: '',
-      password2: '',
-      autoCheckUpgrade: true,
-    }
-  },
-
-
-  events: {
-    latest_version: function (version) {this.latest = version}
-  },
-
-
-  ready: function () {
-    this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']
-
-    api.get('hostname').done(function (hostname) {
-      this.hostname = hostname;
-    }.bind(this));
-
-    api.get('remote/username').done(function (username) {
-      this.username = username;
-    }.bind(this));
-  },
-
-
-  methods: {
-    redirect: function (hostname) {
-      if (0 < this.redirectTimeout) {
-        this.redirectTimeout -= 1;
-        setTimeout(function () {this.redirect(hostname)}.bind(this), 1000);
-
-      } else {
-        location.hostname = hostname;
-        this.hostnameSet = false;
-      }
-    },
-
-
-    set_hostname: function () {
-      api.put('hostname', {hostname: this.hostname}).done(function () {
-        this.redirectTimeout = 45;
-        this.hostnameSet = true;
-
-        api.put('reboot').always(function () {
-          var hostname = this.hostname;
-          if (String(location.hostname).endsWith('.local'))
-            hostname += '.local';
-          this.redirect(hostname);
-        }.bind(this));
-
-      }.bind(this)).fail(function (error) {
-        alert('Set hostname failed: ' + JSON.stringify(error));
-      })
-    },
-
-
-    set_username: function () {
-      api.put('remote/username', {username: this.username}).done(function () {
-        this.usernameSet = true;
-      }.bind(this)).fail(function (error) {
-        alert('Set username failed: ' + JSON.stringify(error));
-      })
-    },
-
-
-    set_password: function () {
-      if (this.password != this.password2) {
-        alert('Passwords to not match');
-        return;
-      }
-
-      if (this.password.length < 6) {
-        alert('Password too short');
-        return;
-      }
-
-      api.put('remote/password', {
-        current: this.current,
-        password: this.password
-      }).done(function () {
-        this.passwordSet = true;
-      }.bind(this)).fail(function (error) {
-        alert('Set password failed: ' + JSON.stringify(error));
-      })
-    },
-
-
-    backup: function () {
-      document.getElementById('download-target').src = '/api/config/download';
-    },
-
-
-    restore: function (e) {
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-
-      var fr = new FileReader();
-      fr.onload = function (e) {
-        var config;
-
-        try {
-          config = JSON.parse(e.target.result);
-        } catch (ex) {
-          alert("Invalid config file");
-          return;
-        }
-
-        api.put('config/save', config).done(function (data) {
-          this.$dispatch('update');
-          this.configRestored = true;
-
-        }.bind(this)).fail(function (error) {
-          alert('Restore failed: ' + error);
-        })
-      }.bind(this);
-
-      fr.readAsText(files[0]);
-    },
-
-
-    reset: function () {
-      this.confirmReset = false;
-      api.put('config/reset').done(function () {
-        this.$dispatch('update');
-        this.configReset = true;
-
-      }.bind(this)).fail(function (error) {
-        alert('Reset failed: ' + error);
-      });
-    },
-
-
-    check: function () {this.$dispatch('check')},
-    upgrade: function () {this.$dispatch('upgrade')},
-
-
-    upload: function (e) {
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-      this.$dispatch('upload', files[0]);
-    },
-
-
-    change_auto_check_upgrade: function () {
-      this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade;
-      this.$dispatch('config-changed');
-    }
-  }
-}
index 704520907a36ce8d0b29ec74cb738274ccfe35a8..78fecc5309d36793f5243ea1824fbc02e5f333be 100644 (file)
@@ -114,7 +114,8 @@ module.exports = new Vue({
     'tool-view': require('./tool-view'),
     'io-view': require('./io-view'),
     'gcode-view': require('./gcode-view'),
-    'admin-view': require('./admin-view'),
+    'admin-general-view': require('./admin-general-view'),
+    'admin-network-view': require('./admin-network-view'),
     'help-view': {template: '#help-view-template'},
     'cheat-sheet-view': {
       template: '#cheat-sheet-view-template',
@@ -125,7 +126,7 @@ module.exports = new Vue({
 
   events: {
     'config-changed': function () {this.modified = true;},
-
+    'hostname-changed': function (hostname) {this.hostname = hostname},
 
     send: function (msg) {
       if (this.status == 'connected') {
@@ -136,8 +137,11 @@ module.exports = new Vue({
 
 
     connected: function () {
-      if (this.reloadOnConnect) location.reload(true);
-      else this.update();
+      if (this.reloadOnConnect) {
+        if (typeof this.hostname != 'undefined')
+          location.hostname = this.hostname;
+        location.reload(true);
+      } else this.update();
     },
 
 
index 9f7c301197c698a90689e596ea275269b9474902..a6d32ff0e18738d596ddcd7affbae26b55bbab97 100644 (file)
@@ -115,6 +115,24 @@ class HostnameHandler(bbctrl.APIHandler):
         raise HTTPError(400, 'Failed to set hostname')
 
 
+class WifiHandler(bbctrl.APIHandler):
+    def get(self):
+        ssid = ''
+        try:
+            ssid = call_get_output(['config-wifi', '-g'])
+        except: pass
+        self.write_json({'ssid': ssid})
+
+    def put(self):
+        if 'ssid' in self.json and 'pass' in self.json:
+            if subprocess.call(['config-wifi', '-s', self.json['ssid'],
+                                '-p', self.json['pass']]) == 0:
+                self.write_json('ok')
+                return
+
+        raise HTTPError(400, 'Failed to configure wifi')
+
+
 class UsernameHandler(bbctrl.APIHandler):
     def get(self): self.write_json(get_username())
 
@@ -334,6 +352,7 @@ class Web(tornado.web.Application):
             (r'/api/log', LogHandler),
             (r'/api/reboot', RebootHandler),
             (r'/api/hostname', HostnameHandler),
+            (r'/api/wifi', WifiHandler),
             (r'/api/remote/username', UsernameHandler),
             (r'/api/remote/password', PasswordHandler),
             (r'/api/config/load', ConfigLoadHandler),
index 7f560357c4bc2209be2ee8a89303dbcb1adad136..f8ab2dd6a89b307b3ca763383ba71e2ea6eecf6d 100644 (file)
@@ -531,6 +531,11 @@ body
     padding 0.5em
 
 
+.admin-general-view, .admin-network-view
+  h2:not(:first-of-type)
+    margin-top 2em
+
+
 .upgrade-version
   display inline-block
   border-radius 4px