Commit 736761dc authored by Malthe R. Husted's avatar Malthe R. Husted

initial commit

parents
# Created by https://www.gitignore.io/api/node,linux,visualstudio,visualstudiocode
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# SQL
# database.sqlite
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Uncomment the next line to ignore your web deploy settings.
# By default, sensitive information, such as encrypted password
# should be stored in the .pubxml.user file.
#*.pubxml
*.pubxml.user
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Typescript v1 declaration files
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
### VisualStudio Patch ###
# By default, sensitive information, such as encrypted password
# should be stored in the .pubxml.user file.
# End of https://www.gitignore.io/api/node,linux,visualstudio,visualstudiocode
\ No newline at end of file
'use strict'
const path = require('path')
const express = require('express')
const exphbs = require('express-handlebars')
const bodyParser = require('body-parser')
const Weather = require('../db/db').Weather
const app = express()
const port = 3000
const sensorLib = require('node-dht-sensor')
app.engine('.hbs', exphbs({
defaultLayout: 'main',
extname: '.hbs',
layoutsDir: path.join(__dirname, '../views/layouts')
}))
app.set('view engine', '.hbs')
app.set('views', path.join(__dirname, '../views'))
app.use(bodyParser.urlencoded({ extended: true }))
/* Setup static resources */
app.use(express.static(path.join(__dirname, '../public')))
/* Redirect to /weather */
app.get('/', (request, response, next) => {
response.redirect('/weather')
})
app.get('/weather', (request, response, next) => {
Weather.current((err, weathers) => {
if (err) return next(err)
response.render('weather', {
weathers: weathers
})
})
})
/* Retrieve all data from db and send to json */
app.get('/data', (request, response, next) => {
if (request.accepts('application/json') && !request.accepts('text/html')) {
Weather.all((err, data) => {
if (err) return next(err)
response.contentType('application/json')
response.end(JSON.stringify(data))
})
}
})
/* Retrieve data from a given period */
app.get('/partialdata', (request, response, next) => {
if (request.accepts('application/json') && !request.accepts('text/html')) {
Weather.partial((err, data) => {
if (err) return next(err)
response.contentType('application/json')
response.end(JSON.stringify(data))
})
}
})
/* Retrieve newest weather data from db. Used for updating content dynamically */
app.get('/currentdata', (request, response, next) => {
if (request.accepts('application/json') && !request.accepts('text/html')) {
Weather.current((err, data) => {
if (err) return next(err)
response.contentType('application/json')
response.end(JSON.stringify(data))
})
}
})
/* Sensor stuff */
sensorLib.initialize(11, 4)
setInterval(() => {
read()
}, 10000)
/* Prints current weather data to console, useful for debugging */
function read () {
let readout = sensorLib.read()
let currentTemp = readout.temperature.toFixed(2)
let currentHumidity = readout.humidity.toFixed(2)
console.log('Temperature: ' + currentTemp + 'C, ' +
'humidity: ' + currentHumidity + '%')
/* Add weather entry to db */
const weather = { temp: currentTemp, humidity: currentHumidity }
Weather.create(weather, (err, next) => {
if (err) return next(err)
})
};
/* Message upon exit */
process.on('SIGINT', () => {
clearInterval(interval)
console.log('Bye, bye!')
process.exit()
})
/* Start message, port given is public ip address for the pi. */
app.listen(port, (err) => {
if (err) return console.error(`An error occurred: ${err}`)
console.log(`Listening on http://192.168.1.61:${port}/`)
})
const sqlite3 = require('sqlite3').verbose()
const path = require('path')
const dbName = path.join(__dirname, 'database.sqlite')
const db = new sqlite3.Database(dbName)
/* Gets the date */
function getDate () {
let date = new Date()
let yy = date.getFullYear()
let mm = date.getMonth() + 1
let dd = date.getDate()
let hour = date.getHours()
let min = date.getMinutes()
let sec = date.getSeconds()
/* prefix with 0 if below 10. */
if (mm < 10) {
mm = `0${mm}`
}
if (dd < 10) {
dd = `0${dd}`
}
if (hour < 10) {
hour = `0${hour}`
}
if (min < 10) {
min = `0${min}`
}
if (sec < 10) {
sec = `0${sec}`
}
return `${hour}:${min}:${sec} - ${dd}/${mm}/${yy}`
}
/* Creates one tables in the database */
db.serialize(() => {
const sql = `
CREATE TABLE IF NOT EXISTS weather
(id integer primary key, temp, humidity, date)`
db.run(sql)
})
/* Class with DML statements for Weather */
class Weather {
static all (callback) {
db.all('SELECT * FROM weather ORDER BY id ASC', callback)
}
/* Selects measurements from the last 30 minutes, 180 measurements in total */
static partial (callback) {
db.all('SELECT * FROM weather WHERE id > (SELECT max(id)-180 FROM weather)', callback)
}
/* Selects the latest measurement from the sensor */
static current (callback) {
db.all('SELECT * FROM weather WHERE id = (SELECT max(id) FROM weather)', callback)
}
static create (Weather, callback) {
const sql = 'INSERT INTO weather(temp, humidity, date) VALUES (?, ?, ?)'
db.run(sql, Weather.temp, Weather.humidity, getDate(), callback)
}
}
module.exports = db
module.exports.Weather = Weather
'use strict'
require('./app/index')
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "weather-station",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Malthe R. Husted",
"license": "ISC",
"description": "Simple server displaying current temperature and humidity with the use of database.",
"dependencies": {
"body-parser": "^1.18.2",
"chart.js": "^2.7.2",
"express": "^4.16.2",
"express-handlebars": "^3.0.0",
"handlebars": "^4.0.11",
"node-dht-sensor": "^0.0.34",
"sqlite3": "^3.1.13",
"standard": "^11.0.1"
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
'use strict'
var ctx = document.getElementById('myChart').getContext('2d')
let myChart
/* Display data right away! */
dataFetch()
/* Create interval to update data periodically */
const interval = setInterval(() => {
currentData()
}, 10000)
/* Grab data and use to create chart */
function dataFetch () {
fetch('/partialdata', {
method: 'get',
headers: {
'Accept': 'application/json'
}
}).then((response) => {
response.json().then((data) => {
makeMyChart(data)
updateData(data)
})
})
}
/* Update current weather data, along with weather chart */
function currentData () {
fetch('/currentdata', {
method: 'get',
headers: {
'Accept': 'application/json'
}
}).then((response) => {
response.json().then((data) => {
updateData(data)
updateChart(data)
})
})
}
/* Push new data to chart and update it. count is neccessary to avoid bugs with the new data points */
function updateChart (data) {
let count = 1
myChart.data.datasets.forEach(function (dataset) {
if (count === 1) {
dataset.data.push(data[0].temp)
myChart.data.labels.push(data[0].date)
} else if (count === -1) {
dataset.data.push(data[0].humidity)
}
count *= -1
})
myChart.update()
}
/* Create arrys of data to use in chart */
function makeMyData (data) {
const labels = []
const temperature = []
const humidity = []
for (let c of data) {
labels.push(c.date)
temperature.push(c.temp)
humidity.push(c.humidity)
}
return { labels, temperature, humidity }
}
/* Retrieves latest data from db and updates latest weather on frontpage */
function updateData (data) {
const date = []
const temperature = []
const humidity = []
for (let c of data) {
date.push(c.date)
temperature.push(c.temp)
humidity.push(c.humidity)
}
document.querySelector('#temperature').innerHTML = data[data.length - 1].temp + '&#8451'
document.querySelector('#humidity').innerHTML = data[data.length - 1].humidity + '%'
document.querySelector('#date').innerHTML = data[data.length - 1].date
}
/* Create line chart and format it appropriately */
function makeMyChart (data) {
const myData = makeMyData(data)
myChart = new Chart(ctx, {
type: 'line',
data: {
labels: myData.labels,
datasets: [{
label: 'Temperature [C]',
data: myData.temperature,
borderColor: 'red',
backgroundColor: ['rgba(255, 0, 0, 0.2)'],
yAxisID: 'temperature'
}, {
label: 'Humidity [%]',
data: myData.humidity,
backgroundColor: ['rgba(54, 162, 235, 0.2)'],
borderColor: ['rgba(54, 162, 235, 1)'],
yAxisID: 'humidity'
}
]
},
/* format mousehover, axes, and labels */
options: {
responsive: true,
hover: {
mode: 'nearest',
intersect: true
},
scales: {
/* Format x axes */
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Time'
},
display: true,
id: 'timestamp',
ticks: {
display: false
}
}],
/* Format temperature axes */
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Temperature [C]'
},
display: true,
id: 'temperature',
position: 'left',
ticks: {
min: Math.min.apply(this, myData.temperature) - 5,
max: Math.max.apply(this, myData.temperature) + 5
}
}, /* Format humidity axes */
{
scaleLabel: {
display: true,
labelString: 'Humidity [%]'
},
gridLines: {
display: false
},
display: true,
id: 'humidity',
position: 'right',
ticks: {
min: Math.min.apply(this, myData.humidity) - 5,
max: Math.max.apply(this, myData.humidity) + 10
}
}
]
}
}
})
}
/* Body formatting */
body{
background-color: #e7eff6;
margin: 0;
padding: 0;
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
/* Header */