Merge branch 'master' of

Starbeamrainbowlabs 4 years ago
commit 0cd1032a3b
Signed by: sbrl
GPG Key ID: 1BE5172E637709C2
  1. 7
  2. 94
  3. 13
  4. 6
  5. 13
  6. 2
      design/Application Structure.drawio.svg
  7. 36
  8. 2

@ -1,5 +1,12 @@
# Changelog
## v0.5.3 - 7th March 2019
- Tweak x axis labels
- Build script: Improve first-time setup experience
## v0.5.2 - 1st March 2019
- [API] Added `version` action
## v0.5.1 - 26th February 2019
- Fixed issue with non-linear sensor reading reports ([#15](

@ -25,7 +25,9 @@ In order to run this program, you'll need the following:
The client-side code requires building. Currently, no pre-built versions are available (though these can be provided upon request), so this must be done from source. A build script is available, however, which automates the process - as explained below.
### System Requirements
- PHP-enabled web server (Nginx is recommended, but Apache works too)
- PHP-enabled web server
- _Nginx_ + _PHP-FPM_ is recommended
- Apache works too
- MariaDB database with the appropriate table structure pre-loaded
- PHP 7+
- Node.JS (preferably 10+) + npm 6+ (for installing & building the client-side app code)
@ -33,53 +35,101 @@ The client-side code requires building. Currently, no pre-built versions are ava
- PHP modules:
- `pdo-mysql` (for the database connection)
### Building From Source
The build script ensures that everything it does will not go outside the current directory (i.e. all dependencies are installed locally).
### Installation
1. Start by cloning this repository:
git clone
To build from source, start off by cloning this repository.
Then run the `setup` and `setup-dev` build commands from the root of the repository like this:
2. Change the ownership to allow your web server user to access the created directory. Usually, the web server will be running under the `www-data` user:
./build setup setup-dev
sudo chown -R www-data:www-data path/to/Air-Quality-Web
This will initialise any git submodules and install both the server-side and client-side dependencies. Once done, all you need to do is build the client-side code:
3. `cd` into the root of the cloned repository. Then install the dependencies (these are installed locally):
./build client
./build setup setup-dev
For development purposes, the `client-watch` command is available.
In production, you probably want to do this instead:
4. Build the client-side application:
# For development, run this:
./build client
# For production, run this:
NODE_ENV=production ./build client
# If you're actively working on the codebase and need to auto-recompile on every change, run this:
./build client-watch
This will take longer, as it causes the build system to additionally minify the client code, saving a significant amount of bandwidth and speeding up loading times.
5. Edit `data/settings.toml` to enter your database credentials.
You can edit other settings here too. See `settings.default.toml` for the settings you can change, but do **not** edit `settings.default.toml`! Edit `data/settings.toml` instead. You'll probably want to give the entire default settings file a careful read.
### Configuration
#### Web server
Configure your php-enabled web server to:
- ...serve the root directory of this repository in PHP
- ...block access to the `data` directory (created on first page load)
- able to write to the repository root (technically only the `data` directory requires write access)
- Example: `sudo chown -R www-data:www-data path/to/repository_root`
#### Application
Some configuration must be done before the application is ready for use.
6. Configure your web server to serve the root of the repository you've cloned if you haven't already. Skip this step if you cloned the repository into a directory that your web server already serves.
The first time `api.php` is called from a browser, it will create a new blank configuration file at `data/settings.toml`, if it doesn't already exist. See the `settings.default.toml` file in this repository for a list of configurable settings, but do **not** edit `settings.default.toml`!
Instead, enter your configuration details into `data/settings.toml`, which overrides `settings.default.toml`. In particular, you'll probably want to change the settings under the `[database]` header - but ensure you give the entire file a careful read.
7. Disallow public access to the private `data` directory.
In Nginx:
# put this inside the "server { }" block:
location ^~ /path/to/data/directory {
deny all;
In Apache:
# Create a file with this content inside the data/ directory
Require all denied
8. Test the application with an API call. If this returns valid JSON, then you've set it up correctly
9. (Optional) Setup HTTPS:
sudo apt install certbot
# On Nginx:
sudo certbot --nginx --domain
# On Apache:
sudo certbot --apache --domain
## API
The server-side API is accessed through `api.php`, and supports a number of GET parameters. The most important of these is the `action` parameter, Which determines what the API will do. The following values are supported:
### version
> Returns the version of the application.
_No parameters are currently supported by this action._
### fetch-data
> Fetches air quality data from the system for a specific data type at a specific date and time.


@ -86,6 +86,19 @@ function task_setup {
composer install --no-dev;
task_end $?;
if [ ! -d "./data" ]; then
task_begin "Setting up initial data folder";
mkdir "./data"; chmod 0700 "./data";
echo -e "# -------[ Custom Settings File - Last updated $(date) ]-------" >"./data/settings.toml";
echo -e '[database]\nusername = "INSERT_DATABASE_USERNAME_HERE"\npassword = "INSERT_DATABASE_PASSWORD_HERE";' >>"./data/settings.toml";
echo -e "${HC}Don't forget to edit './data/settings.toml' to specify the database username and password${RS}";
echo -e "";
task_end $?;
stage_end $?;

@ -12,6 +12,7 @@ import Chart from '../../node_modules/chart.js/dist/Chart.bundle.min.js';
import GetFromUrl from './Helpers/GetFromUrl.mjs';
import GetContainingElement from './Helpers/GetContainingElement.mjs';
import Postify from './Helpers/Postify.mjs';
import { human_duration_unit } from './Helpers/DateHelper.mjs';
class DeviceReadingDisplay {
@ -238,7 +239,7 @@ class DeviceReadingDisplay {
type: "time",
time: {
format: "YYYY-MM-DD HH:mm",
tooltipFormat: 'll HH:mm'
tooltipFormat: 'll HH:mm',
scaleLabel: {
display: true,
@ -354,7 +355,8 @@ class DeviceReadingDisplay {
// Update the x axis labels
// = => point.t);
this.chart.options.scales.xAxes[0].time.unit = human_duration_unit(this.end_time.diff(this.start_time));
console.log(`New unit: ${this.chart.options.scales.xAxes[0].time.unit}`);
// Update the chart

@ -0,0 +1,13 @@
"use strict";
function human_duration_unit(milliseconds) {
let seconds = Math.floor(milliseconds / 1000);
if(seconds <= 60) return "second";
if(seconds <= 60*60) return "minute";
if(seconds <= 60*60*24) return "hour";
if(seconds <= 60*60*24*45) return "day";
if(seconds <= 60*60*24*365) return "month";
return "year";
export { human_duration_unit };

File diff suppressed because one or more lines are too long


Width:  |  Height:  |  Size: 47 KiB

@ -0,0 +1,36 @@
namespace AirQuality\Actions;
use \SBRL\TomlConfig;
use \AirQuality\PerfFormatter;
class Version implements IAction {
/** @var TomlConfig */
private $settings;
public function __construct(
TomlConfig $in_settings) {
$this->settings = $in_settings;
public function handle() : bool {
global $start_time;
$start_handle = microtime(true);
// 1: Parse markdown
$result = file_get_contents(ROOT_DIR."version");
// 2: Send response
header("content-length: " . strlen($result));
header("content-type: text/plain");
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null));
return true;

@ -1 +1 @@