diff --git a/coffee_scale/apps.py b/coffee_scale/apps.py
deleted file mode 100644
index 57c4c5b..0000000
--- a/coffee_scale/apps.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from django.apps import AppConfig
-
-import logging
-import sys
-
-from coffee_scale import mqtt
-
-
-class CoffeeScaleConfig(AppConfig):
- name = 'coffee_scale'
-
- def ready(self):
- if ('makemigrations' in sys.argv or 'migrate' in sys.argv):
- return
-
- try:
- logging.info('Connecting to MQTT (coffee scale) at {}...'.format(mqtt.HOST))
- logging.info('If there is no confirmation, the MQTT connection has probably failed.')
- mqtt.client.connect_async(mqtt.HOST, mqtt.PORT, 60)
- mqtt.client.loop_start()
- except Exception as ex:
- logging.error(ex)
- logging.error('Failed to connect to MQTT at {}'.format(mqtt.HOST))
diff --git a/coffee_scale/mqtt.py b/coffee_scale/mqtt.py
deleted file mode 100644
index 47a4592..0000000
--- a/coffee_scale/mqtt.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import paho.mqtt.client as mqtt
-import logging
-import datetime
-from collections import deque
-
-from django.conf import settings
-
-HOST = settings.MQTT_SETTINGS['HOST']
-PORT = settings.MQTT_SETTINGS['PORT']
-TOPICS = settings.MQTT_SETTINGS['TOPICS']
-latest = {}
-
-
-def on_connect(client, userdata, flags, rc):
- logging.info('Connected successfully to MQTT.')
- logging.info('Subscribing to all topics on {}.'.format(HOST))
- client.subscribe('sik/kiltahuone/kahvivaaka/#')
-
-
-def update_latest(msg):
- payload = msg.payload.decode('utf-8')
- if msg.topic == TOPICS['WEIGHT']:
- weight = float(payload)
- latest['weight'] = weight
- elif msg.topic == TOPICS['CUPS']:
- cups = float(payload)
- latest['cups'] = cups
- elif msg.topic == TOPICS['BREWING']:
- brewing = bool(int(payload))
- latest['brewing'] = brewing
- elif msg.topic == TOPICS['BREW_TIME']:
- brew_time = datetime.datetime.fromtimestamp(float(payload))
- latest['brew_time'] = brew_time
-
-
-def on_message(client, userdata, msg):
- try:
- update_latest(msg)
- except Exception as ex:
- logging.exception('Failed to parse MQTT payload.')
-
-
-def on_disconnect(client, userdata, rc):
- if rc != 0:
- logging.warning('MQTT unexpectedly disconnected.')
- else:
- client.loop_stop(force=False)
- logging.warning('MQTT disconnected.')
-
-
-def get_latest():
- return latest
-
-
-client = mqtt.Client()
-client.on_connect = on_connect
-client.on_message = on_message
-client.on_disconnect = on_disconnect
diff --git a/coffee_scale/static/js/coffee.js b/coffee_scale/static/js/coffee.js
index 6b9b4a6..95f338b 100644
--- a/coffee_scale/static/js/coffee.js
+++ b/coffee_scale/static/js/coffee.js
@@ -1,130 +1,139 @@
-var len = 0;
-var lastBrew = "∞";
-var brewtext = "";
+//Inner state
+var lastBrew = new Date(0);
+var brewing = false;
+var backoff = 2000;
-$(document).ready(function(){
- $('#text').bind("DOMSubtreeModified", resize);
- updateTime();
- setInterval(updateTime,1000);
- formatBrewTime();
- setInterval(formatBrewTime,10000);
-});
+//MQTT client config
+var username = "coffee-user-"+ Math.random();
+var client = new Paho.MQTT.Client("sika.sahkoinsinoorikilta.fi", 9001, username);
+client.onMessageArrived = function (message) {
+ console.log("Topic: "+message.destinationName+" msg: "+message.payloadString);
+ var ev = new CustomEvent(message.destinationName, {'detail': message.payloadString});
+ window.dispatchEvent(ev);
+}
-$(window).resize(resize);
-
-function fetchdata(data, status){
- if (typeof status !== 'undefined'){
- if (status == "success"){
- parseData(data);
+function reconnect(responseObject){
+ if (responseObject.errorCode !== 0) {
+ console.log("connection lost! Reason: "+responseObject.errorMessage);
+ setTimeout(function(){
+ client.connect({onSuccess:onConnect, useSSL:true, onFailure: reconnect});
+ }, backoff);
}
- else if (status == "error"){
- handleError();
- }
- }
}
-$.getJSON("/coffee/cups", fetchdata);
-setInterval(function() {
- $.getJSON("/coffee/cups", fetchdata);
-}, 2000);
-
-function formatBrewTime(){
- if (!brewtext && lastBrew instanceof Date){
- var now = new Date();
- var timeDiff = Math.max(now.getTime() - lastBrew.getTime(), 0);
- var tmp = (timeDiff < 3600000)
- ? Math.round(timeDiff / 60000) + ' min'
- : '~' + Math.round(timeDiff / 3600000 * 2) / 2 + ' h';
-
- $("#brewtime").html(tmp);
- } else {
- $("#brewtime").html(brewtext);
+function onConnect() {
+ console.log("MQTT connected");
+ //set and reset reconnector
+ client.onConnectionLost = reconnect
+ // subscribe to topics
+ client.subscribe("sik/kiltahuone/kahvivaaka/cups");
+ client.subscribe("sik/kiltahuone/kahvivaaka/brewing");
+ client.subscribe("sik/kiltahuone/kahvivaaka/brewtime");
}
+
+// data update and parse functions
+function parseCups(ev){
+ var cups = parseFloat(ev.detail).toFixed(1)
+ var cupsEvent = new CustomEvent("cupsChanged", {'detail': cups});
+ window.dispatchEvent(cupsEvent);
+}
+function updateCups(ev){
+ $("#text").text(ev.detail);
+}
+function updateScale(ev){
+ $("#scale2").css({width: Math.min(ev.detail/9*100,100) + '%'});
}
-function handleError() {
- setData("?", 0, 0, Number.MAX_VALUE, Number.MAX_VALUE);
+function tick(){
+ var ev = new CustomEvent("tick", {'detail': new Date()});
+ window.dispatchEvent(ev);
}
-function parseData(data) {
- if (data) {
- var date = new Date(data.date);
- lastBrew = new Date(data.last_brew);
- var now = new Date();
- var cups = data.cups;
- var brewing = data.brewing;
- var timeDiff = Math.max(now.getTime() - lastBrew.getTime(),0);
- var opa = Math.max(100 - timeDiff / 90000,0);
- setData(cups, data.temp, opa,now.getTime()-date.getTime(), timeDiff, brewing);
- }
- else{
- handleError();
- }
-}
-
-function setData(cups, temp, opa, timeFromUpdate, timeFromBrew, brewing){
- if (cups == 0) {
- opa = 0;
- }
- brewtext = "";
- $("#upper").css({opacity: opa/100});
- $("#scale2").css({width: Math.min(cups/9*100,100) + '%'});
- $("#text,body").removeClass();
-
- cups = Number(cups).toFixed(1);
-
- if(timeFromUpdate > 600000){
- cups = "?";
- brewtext = "∞";
- $("#text").addClass("unknown");
- }
- else if(brewing){
- cups = "+";
- brewtext = ":)";
- $("#text").addClass("brewing");
- }
- else if(cups <= 2){
- $("#text").addClass("hurry");
- }
- formatBrewTime();
- if($("#text").html() == "+" && !brewing)
- $("body").addClass("coffeeready");
-
- var cupsString = cups.toString();
- len = cupsString.length;
- $("#text").html(cups);
-}
-
-function updateTime(){
- var now = new Date();
+function updateTime(ev){
+ var now = ev.detail;
$("#time").html(formatTime(now.getHours(),now.getMinutes(),now.getSeconds()));
}
-function formatTime(hours, minutes, seconds){
- var str = "";
-
- if(hours < 10)
- str += "0";
- str += hours;
-
- str += ":";
-
- if(minutes < 10)
- str += "0";
- str += minutes;
-
- str += ":";
-
- if(seconds < 10)
- str += "0";
- str += seconds;
-
- return str;
+function coffeeLowEffect(ev){
+ ev.detail <= 2 ? $("#text").addClass("hurry") : $("#text").removeClass("hurry");
}
+function coffeeReadyEffect(ev){
+ $("body").addClass("coffeeready");
+ // autoclear animation class in 10s
+ setTimeout(() => {$("body").removeClass("coffeeready");}, 10000);
+}
+function brewAnimStart(ev){
+ $("#text").addClass("brewing");
+}
+function brewAnimEnd(ev){
+ $("#text").removeClass("brewing");
+}
+function brewNotifier(ev){
+ var new_brewing = parseInt(ev.detail);
+ if (new_brewing == 1 && brewing == 0){
+ window.dispatchEvent(new Event("brewStart"));
+ } else if (new_brewing == 0 && brewing == 1){
+ window.dispatchEvent(new Event("brewEnd"));
+ }
+ brewing = new_brewing;
+}
+function brewTimeParser(ev){
+ lastBrew = new Date(parseInt(ev.detail)*1000.0);
+}
+function updateBrewDiff(ev){
+ var now = new Date();
+ var timeDiff = Math.max(now.getTime() - lastBrew.getTime(), 0);
+ var timeStr = (timeDiff < 3600000)
+ ? Math.round(timeDiff / 60000) + ' min'
+ : '~' + Math.round(timeDiff / 3600000 * 2) / 2 + ' h';
+
+ $("#brewtime").html(timeStr);
+}
+
+// Helpers
+
+function nToS(num){
+ return num < 10 ? "0" + num : "" + num;
+}
+
+function formatTime(hours, minutes, seconds){
+ return nToS(hours)+":"+nToS(minutes)+":"+nToS(seconds)
+}
+
function resize(){
var w = $("#container").width();
var h = $("#container").height();
var s = w > h ? h : w;
- var font = s*0.8*0.38/Math.sqrt(len);
- $("#text").css({ top: s*0.16-font/2 + 'px', fontSize: font + 'px', marginLeft: -font*len*3/10 + 'px'});
+ var font = s * 0.8 * 0.38/Math.sqrt(3);
+ $("#text").css({ top: s*0.16-font/2 + 'px',
+ fontSize: font + 'px',
+ marginLeft: -font*3*3/10 + 'px'});
}
+
+// Init everything
+
+$(document).ready(function(){
+ client.connect({onSuccess:onConnect, useSSL:true, onFailure:reconnect});
+
+ //connect MQTT event listeners
+ window.addEventListener("sik/kiltahuone/kahvivaaka/cups", parseCups);
+ window.addEventListener("sik/kiltahuone/kahvivaaka/brewing", brewNotifier);
+ window.addEventListener("sik/kiltahuone/kahvivaaka/brewtime", brewTimeParser);
+
+ //connect other event listeners
+ window.addEventListener("cupsChanged", updateCups);
+ window.addEventListener("cupsChanged", coffeeLowEffect);
+ window.addEventListener("cupsChanged", updateScale);
+ window.addEventListener("cupsChanged", resize);
+ window.addEventListener("brewStart", brewAnimStart);
+ window.addEventListener("brewEnd", brewAnimEnd);
+ window.addEventListener("brewEnd", coffeeReadyEffect);
+ window.addEventListener("tick", updateTime);
+ window.addEventListener("tick", updateBrewDiff);
+
+ //start time based events
+ setInterval(tick, 100);
+ tick();
+
+});
+$(window).resize(resize);
diff --git a/coffee_scale/templates/coffee.html b/coffee_scale/templates/coffee.html
index 1c2fec9..faf215c 100644
--- a/coffee_scale/templates/coffee.html
+++ b/coffee_scale/templates/coffee.html
@@ -10,6 +10,8 @@
+
diff --git a/coffee_scale/tests.py b/coffee_scale/tests.py
index b1e5f9b..f9c051b 100644
--- a/coffee_scale/tests.py
+++ b/coffee_scale/tests.py
@@ -1,30 +1,2 @@
from django.test import TestCase, Client
from django.conf import settings
-
-from coffee_scale.mqtt import on_message
-
-HOST = settings.MQTT_SETTINGS['HOST']
-PORT = settings.MQTT_SETTINGS['PORT']
-TOPICS = settings.MQTT_SETTINGS['TOPICS']
-
-
-class MQTTTestCase(TestCase):
- """Tests MQTT functionality"""
-
- class MockMessage:
- def __init__(self, payload, topic):
- self.payload = payload
- self.topic = topic
-
- def setUp(self):
- payload = '10'.encode('utf-8')
- topic = TOPICS['CUPS']
- msg = MQTTTestCase.MockMessage(payload, topic)
-
- on_message(None, None, msg)
- self.c = Client()
-
- def test_receive_cups(self):
- response = self.c.get('/coffee/cups')
- payload = response.json()
- self.assertEquals(payload['cups'], 10)
diff --git a/coffee_scale/urls.py b/coffee_scale/urls.py
index b9c54e6..f750576 100644
--- a/coffee_scale/urls.py
+++ b/coffee_scale/urls.py
@@ -1,7 +1,7 @@
from django.conf.urls import url
from django.views.generic.base import RedirectView
-from .views import coffee_view, cups_view
+from .views import coffee_view
favicon_view = RedirectView.as_view(url='static/img/favicon.ico', permanent=True)
@@ -9,6 +9,4 @@ urlpatterns = [
# landing page
url(r'^$', coffee_view),
- url(r'^cups', cups_view),
-
]
diff --git a/coffee_scale/views.py b/coffee_scale/views.py
index 9b8ae08..b973641 100644
--- a/coffee_scale/views.py
+++ b/coffee_scale/views.py
@@ -3,25 +3,9 @@ from django.http import JsonResponse
from django.utils import timezone
-from .mqtt import get_latest
-import coffee_scale.mqtt # somehow this is needed
-
import logging
from django.conf import settings
def coffee_view(request):
return render(request, 'coffee.html')
-
-
-def cups_view(request):
- now = timezone.now()
- latest = get_latest()
- data = {
- 'date': now,
- 'cups': latest.get('cups'),
- 'last_brew': latest.get('brew_time'),
- 'brewing': latest.get('brewing'),
- 'weight': latest.get('weight')
- }
- return JsonResponse(data)
diff --git a/requirements.txt b/requirements.txt
index ef7a2fd..5e52874 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -25,7 +25,6 @@ dealer==2.0.5
django-modeltranslation==0.12.1
django-auditlog==0.4.3
django-phonenumber-field==1.3.0
-paho-mqtt==1.3.0
django-autocomplete-light==3.2.10
six==1.10.0
django-suit==0.2.25
diff --git a/sikweb/base.py b/sikweb/base.py
index c094344..527c645 100644
--- a/sikweb/base.py
+++ b/sikweb/base.py
@@ -78,7 +78,7 @@ INSTALLED_APPS = [
'webapp',
'members',
'infoscreen',
- 'coffee_scale.apps.CoffeeScaleConfig',
+ 'coffee_scale',
'rest_framework',
'django_nose',
'bootstrap3',