Giter Site home page Giter Site logo

oakterm's Introduction

OakTerm

OakTerm is a terminal app for the Digistump Oak that works in a similar fashion to the Arduino Serial Monitor, but data is sent wirelessly via the Particle Cloud rather than the serial port. In place of the Serial.begin(), Serial.print(), Serial.read(), etc. functions in your Arduino sketch, you use the equivalent Particle.* functions instead.

For example, this example sketch will echo back any data sent to it via OakTerm:

char receive_buffer[255]; // Buffer to store received data

void setup() {
  // Initialize cloud serial
  Particle.begin();

  Particle.println("Hello world!");
  Particle.println("Send me data and I will echo it back.");
}

void loop() {
  // Check if there is any data available to be read
  int avail=Particle.available();

  // Our receive buffer is only 255 bytes long, so limit
  // the number of bytes read to 254, to leave room for
  // a null terminator so that Particle.print() knows
  // where to stop printing
  if(avail>254)
    avail=254;

  // If there is data available
  if(avail>0) {
    // Read it into the buffer
    Particle.readBytes(receive_buffer,avail);

    // Add the null terminator
    receive_buffer[avail]=0;

    // Print it back to OakTerm
    Particle.print("You just sent: ");
    Particle.print(receive_buffer);
  }
}

See the examples directory for a full set of examples, demonstrating how to send and receive data and events to and from your Oak, view variable values, and call functions.

To be able to send commands or data to an Oak, it will need to be running a sketch compiled using a version of OakCore not older than this commit. Further, for the "user mode" button to switch from config back to user mode, the Oak needs to be running a config rom also compiled with a version of OakCore not older than that commit.

Test it out here: http://rawgit.com/kh90909/OakTerm/master/index.html

oakterm's People

Contributors

kh90909 avatar emcniece avatar

Stargazers

Broden D'Angio  avatar Robert Wohleb avatar Peter Feerick avatar  avatar Sac avatar David Danziger avatar Andrew Fecheyr avatar Interdigm avatar

Watchers

Interdigm avatar  avatar  avatar  avatar

Forkers

emcniece pfeerick

oakterm's Issues

FF select dropdowns close prematurely

Observed in FF 46.0, OSX El Capitan: select boxes have a lot of padding around them, and the select close event fires while the mouse is travelling between the open/close button and the dropdown choice list.

Add sidebar data interaction

It would be handy if the variables and functions dropdowns could display information pertaining to the selected item. For example, the selected variable might display the last known value in a tag, the device dropdown might show the online status of a device, and the function dropdown might have an input and submit button for sending data directly to that function.

Create settings modal

Modal should contain some settings:

  • Auto scroll
    • Scroll only when page is at bottom
    • Scroll when new events arrive
    • Disabled
  • Line Endings
    • \n
    • \r
    • \r\n
    • none
  • Send data on enter

CDN particle.min.js hangs every once in a while

Just an observation - file comes from https://cdn.jsdelivr.net/particle-api-js/5/particle.min.js and on occasion takes 8 seconds to request. The app waits until it can authorize the token against the API, so during this time the app appears to be unresponsive as neither the terminal nor login page are visible.

One option is to localize the asset, but then we don't get future updates. Another option might be to script it and fallback after a timeout falls through.

Localizing may be the best option, as @kh90909 mentions any API changes are likely to cause conflict anyway so locking this app to a specific version isn't bad.

Variable values are still requested after they are unexposed

A simple way to generate this issue is to switch the Oak to config mode, in which no variables are exposed.

Even though the variables are removed from the sidebar (and thus should be removed from device_vars), their values are still being requested periodically, which results in 404 errors in the console.

Needs further investigation.

Don't set default device, add current_device localstorage

There is a problem right now with login, device init, and subscriber setup. Current process looks like this:

  1. Particle auth login
    1. Populate device list select box
  2. Start pollers (uses current_device, set by device list population)
  3. Update devices, get info, get variables
  4. Subscribe to events for selected device, dump to terminal

On login, this process dumps a device list to the select box, then immediately subscribes to the first device in the list. This subscribe action can be unnecessary if the device is inactive, and it can be counter-productive if a frequent user constantly refreshes the page and finds that the list has reset.

current_device could be stored in localStorage, and it can be auto-set on init. The select box should default to a "-- Select Device --" option, and this will address #10

Convert all forms to submit on enter

Currently there is a mixture of keypress detection and form submission handling. These should be unified: instead of watching for keypresses, each form should handle a submit event and cancel the default action.

This issue might be useful. Some forms have specific functions (#login for example) that need to be called on submission. If the function name is stored in a data-action="do_login" format, we can avoid a switch in the form submission handler and stick with a global form submission statement.

Add note on login page explaining that no login information is saved on our servers

e.g.

"No login information is stored on digistump servers. Your access token for the Particle Cloud is stored locally in your browser only, so that you don't have to log in each time you reload the page".

Optionally, add a "Remember me" checkbox that controls whether the access token is saved to localStorage. Otherwise, tell the user that the logout button will clear the saved token.

Errors from the EventStream object returned by particle.getEventStream are not handled (e.g. if the stream dies, we don't detect it)

This issue can be reproduced in Chrome by temporarily switching network throttling to offline until you see a GET error in the console from https://api.particle.io/v1/devices/:deviceid/events.... If you turn off throttling at this point, events are no longer be received.

I tried adding a stream.on('error', ... handler to display_event():

    activeStream.on('error', function(err) {
      console.log('Subscribed event stream terminated with error. Attempting to restart.');
      activeStream.active = false;
      Promise.resolve()
        .retry(subscribe_events,retry_conditional,1)
        .then(display_event)
        .catch(error_handler);
    });

but this didn't seem to be called at all. It's possible that there's a bug in ParticleJS such that it doesn't actually pass on errors to the EventStream object.

Ensure sidebar is accessible for mobile devices

The current sidebar in #1 is fixed left, which allows the main content to scroll. The sidebar is useful for items that benefit from larger horizontal space, like select elements. This presents a design problem for mobile devices however, as limited screen real estate will force priority consideration.

Ideas for mobile display:

  1. Position sidebar relatively, either above or below terminal content. This means that the header and footer may also have to be positioned relatively, and the user will have to scroll through the entire interface - problematic for long-running content. The terminal output div could be height-limited and overflow scrolled.
  2. Move sidebar controls to a modal. This makes the controls available in an overlay, but scrolling through the control set may be an issue. Not impossible however.
  3. Implement an off-canvas sidebar (like Slidebars). This benefits mobile devices as the sidebar can scroll independently from the terminal content, and the sidebar maintains its fixed-position behavior (better for UI consistency)

I'm in favor of the off-canvas menu (3) but I'm open to ideas!

jQuery scroll backup

If jQuery execution is blocked for whatever reason (debugger pause, tab change, etc) it will continue to queue up animation events, then it executes them once the process is unblocked (debugger play, return to tab). If there are many terminal lines that have come in since the process was blocked, the user cannot scroll until every jQuery scroll animation has completed, synchronously. This takes a while if there are hundreds of animations queued up.

This behavior can be found in other jQuery animations like slideshows and non-CSS animations, and is exhibited in browsers that block non-active tab processes to save resources.

Not sure how to fix this - detect if the tab is active before executing the page scroll animation?

Login form should reveal errors

This might have been a UI rework-induced bug, but as it stands the login form does not show authentication errors. Make it so!

Custom publishEvent action

Might be useful to publish a custom event will all of the fixin's (name, data) to see how a particular device will respond to some data input. This could be integrated into the "Send Data" bar in the footer, perhaps an "advanced" section that allows manipulation of the event name and privacy.

After clarification, this is a modal that is supposed to bind to the "Send Event" button at the top.

Sidebar doesn't scroll when it occupies the full width of the screen (desktop only)

In desktop browsers, when the window is narrow enough that the open sidebar takes up the full width, the sidebar won't scroll. A scrollbar is displayed, but moving it has no effect on the sidebar content. It scrolls the terminal content underneath instead.

With touch scrolling in Chrome's mobile device simulation mode, sidebar scrolling works.

Device list organization

It would be nice to view devices by their names instead of their IDs, perhaps the ID could be a fallback in case a name isn't set yet.

There is also a lot more information in these lists that might be useful, especially for Digistump - in particular, the pinned_build_target, platform_id, and product_id.

It might be worthwhile implementing a filter or sorting method so that we can hone in on Digistump devices faster. If many filters or more visible data is needed, we might need to place this in a modal instead of a dropdown.

The list_devices API call gets all devices on the account, not just Oaks. Here's a sample dump of my own list:

{
  "id": "------------------------",
  "name": "lamp_bulb",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-03-11T15:33:04.174Z",
  "product_id": 0,
  "connected": false,
  "platform_id": 0,
  "cellular": false,
  "pinned_build_target": "0.3.4"
},
{
  "id": "------------------------",
  "name": "normal_hunter",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2015-01-23T05:33:50.918Z",
  "product_id": 0,
  "connected": false,
  "platform_id": 0,
  "cellular": false,
  "pinned_build_target": "0.3.4"
},
{
  "id": "------------------------",
  "name": "cat_dish",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-01-11T04:35:36.618Z",
  "product_id": 0,
  "connected": false,
  "platform_id": 0,
  "cellular": false,
  "status": "normal",
  "pinned_build_target": "0.3.4"
},
{
  "id": "------------------------",
  "name": "bath_sensor",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2015-12-18T21:41:39.851Z",
  "product_id": 0,
  "connected": false,
  "platform_id": 0,
  "cellular": false,
  "status": "normal",
  "pinned_build_target": "0.3.4"
},
{
  "id": "------------------------",
  "name": "gopher_launcher",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-02-19T09:37:09.203Z",
  "product_id": 0,
  "connected": false,
  "platform_id": 0,
  "cellular": false
},
{
  "id": "------------------------",
  "name": "P0",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2015-06-04T02:50:19.236Z",
  "product_id": 6,
  "connected": false,
  "platform_id": 6,
  "cellular": false
},
{
  "id": "------------------------",
  "name": "boomer_vampire",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-04-01T23:07:56.780Z",
  "product_id": 6,
  "connected": true,
  "platform_id": 6,
  "cellular": false,
  "status": "normal"
},
{
  "id": "------------------------",
  "name": "cabinet_lights",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2015-12-20T03:26:38.479Z",
  "product_id": 0,
  "connected": false,
  "platform_id": 0,
  "cellular": false,
  "pinned_build_target": "0.3.4"
},
{
  "id": "------------------------",
  "name": "powerful-kitty",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-02-15T23:18:33.556Z",
  "product_id": 10,
  "connected": true,
  "platform_id": 10,
  "cellular": true,
  "status": "normal",
  "last_iccid": "1234567890123456789",
  "imei": "123456789012345",
  "current_build_target": "0.4.8"
},
{
  "id": "------------------------",
  "name": "morphing_dentist",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-01-03T00:13:09.078Z",
  "product_id": 6,
  "connected": false,
  "platform_id": 6,
  "cellular": false,
  "status": "normal"
},
{
  "id": "------------------------",
  "name": "ranger_hamster",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-02-16T06:52:04.554Z",
  "product_id": 6,
  "connected": false,
  "platform_id": 6,
  "cellular": false,
  "status": "normal"
},
{
  "id": "------------------------",
  "name": "spiffy-bobcat",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-03-14T22:10:27.741Z",
  "product_id": 10,
  "connected": false,
  "platform_id": 10,
  "cellular": true,
  "status": "normal",
  "last_iccid": "1234567890123456789",
  "imei": "123456789012345",
  "current_build_target": "0.4.8"
},
{
  "id": "------------------------",
  "name": "Duo-Uno",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-03-08T16:49:54.619Z",
  "product_id": 88,
  "connected": false,
  "platform_id": 88,
  "cellular": false,
  "status": "normal"
},
{
  "id": "------------------------",
  "name": "Duo-3",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-03-07T18:28:41.202Z",
  "product_id": 88,
  "connected": false,
  "platform_id": 88,
  "cellular": false,
  "status": "normal"
},
{
  "id": "------------------------",
  "name": "Duo-2",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-03-11T04:40:56.329Z",
  "product_id": 88,
  "connected": false,
  "platform_id": 88,
  "cellular": false,
  "status": "normal"
},
{
  "id": "------------------------",
  "name": "Duo-4",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-03-07T03:09:16.526Z",
  "product_id": 88,
  "connected": false,
  "platform_id": 88,
  "cellular": false,
  "status": "normal"
},
{
  "id": "------------------------",
  "name": "Oak B (5402)",
  "last_app": null,
  "last_ip_address": "123.456.789.012",
  "last_heard": "2016-04-02T01:44:50.823Z",
  "product_id": 82,
  "connected": true,
  "platform_id": 82,
  "cellular": false,
  "status": "normal"
}

Responsive data "Send" button doesn't submit

The green "Send Data" button at the bottom works fine when the browser is large, but once the responsive styles kick in the button doesn't do anything.

This is because there are actually 2 buttons that get shown/hidden based on screen size. They currently have the same id="send" so jQuery is only binding events to the first one.

Change the jQuery selector to a class, or better yet: start to implement a data-click="callThisFunc" element pattern.

API queries should resolve before terminal is revealed to user

Adding or removing UI items when users may not expect it (ie. not triggered by user) is sometimes disconcerting. Currently when a user authenticates and hits the terminal, the functions dropdown contains "none registered" initially, then after a second this is removed.

The first step might be to ensure that "none registered" gets added back in when update_devinfo() doesn't find any device functions - this might be accomplished by adding a default to the element, either in HTML or JS.

This issue reveals a larger pattern problem though - when users hit a page, things that change draw their attention. This can be used to an advantage, as Facebook loads their page in blocks as data arrives from sequential requests. It can also be a problem like in this application, where a dropdown's contents change and there is no way to see what the previous value was.

One solution might be to resolve all of the do_login() promises before fading in the terminal screen (might introduce visible lag in mobile platforms). Another solution might be to ensure that the dropdown is populated with a default value.

Selected device changes after update `update_devices`

After selecting a device in the dropdown, the selected device changes. This is likely because the HTML in the select is completely rewritten without re-assigning the selected option.

There is also an error inside update_devinfo():

Uncaught (in promise) TypeError: Cannot read property 'hasOwnProperty' of null       app.js:142

This line is a check originating from the previous loop, which is not needed with the underscorejs conversion.

Stream subscription never ends

Once a device is selected, the app subscribes to its deviceId stream... but this subscription doesn't stop. Subsequent device selections create new subscriptions, but the existing subscription listeners are still active. Not sure how to destroy event stream listeners at this point - should they be stacked in a object for destruction at a later point?

Additional user settings

There are some other interfaces that could benefit from having stored settings:

  • Sidebar dropdown status
  • Max terminal line count

It may be worth changing the localStorage settings to a more generic abstraction layer at this point. An HTML attribute like [data-setting] could bind a click/change/touch event to the element that would trigger save_settings and store all element data with this particular data attribute.

Give the user the option to have stdout data displayed separately

Stderr messages, and especially event and function messages, will generally not fit in with the flow of stdout data. At the very least, we need to delineate them with carriage returns as is done currently, but that is ugly if it occurs in the middle printing to stdout. Therefore, it would be useful to have the option to display stdout in a separate pane from everything else.

Or perhaps this could be implemented as hide/show checkboxes for each type of message, so that each can be hidden independently.

Add Rename Device button

As per a request in #7 we can add a button that allows a device to be renamed. It might be well suited for the sidebar, somewhere around the device select.

Only start device info and list timers once authenticated

When a user is unauthenticated and sitting at the login page, the setInterval functions for getting devices and info start executing right away which produces console errors due to lack of auth. These could be moved to a function and executed as a part of the do_login() procedure.

Maximum terminal line limit

To limit browser resource consumption, it may be necessary to limit the total number of lines printed in the terminal area. This may be nice to have as an adjustable option.

Sidebar variable click should update table

Currently when a variable is clicked in the sidebar, it grabs the last known value from the device_vars object (which is updated every 10 seconds) and adds it to the terminal. Since the sidebar variables are drawn from the update_devices interval (which updates every 60 seconds) the variable table is often outdated compared to the device_vars object.

When a variable in the sidebar is clicked, the corresponding value in the table should be updated.

Implement user settings

After #22 is merged, these settings can be integrated under this issue:

  • Autoscroll functionality
  • Enter-to-submit functionality
  • Line ending to be sent

Add a new setting:

  • Local echo (in terminal)
  • Show scroll bars

Terminal area:

  • Add an option to the terminal area: timestamp toggle (top right corner perhaps)
  • Also in terminal area: clear contents button

General UI styling

Clean up the UI so that it's a bit more uniform. Element spacing, background colors, button styles, icons, etc. Mobile compatibility may fall under this task as well.

Login error: err.errorDescription is undefined

It appears that errorDescription is not always present on the Particle API responses - make sure this is handled properly before using the raw variable!

Error:  TypeError: err.errorDescription is undefined
Stack trace:
login_err@https://rawgit.com/emcniece/OakTerm/feat/i23-user-settings/assets/js/app.js:88:5
[142]</notify/</s@https://cdn.jsdelivr.net/particle-api-js/5/particle.min.js:290:1798
[142]</notify/<@https://cdn.jsdelivr.net/particle-api-js/5/particle.min.js:290:1924
[56]</flush@https://cdn.jsdelivr.net/particle-api-js/5/particle.min.js:118:374

Implement proper error handling and display

Need to gracefully handle Particle API call errors in the promise chains, communicate them to the user, and decide what to do next.

Some possibilities for that 'what to do next':

  • Retry, perhaps a set number of times
  • Display a modal, perhaps giving the user options to retry (or continue without retrying - might be easier to implement) or go back to the login screen, and perhaps automatically dismissing that modal if fresh events come in

See @434a90f for a possible framework for putting error handlers into the promise chains.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.