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/css/coffee.css b/coffee_scale/static/css/coffee.css index 2594006..de6d2be 100644 --- a/coffee_scale/static/css/coffee.css +++ b/coffee_scale/static/css/coffee.css @@ -47,7 +47,7 @@ body { background: green; border-radius: 10px; } -#brewtime{ +.brewtime{ text-align:right; position:absolute; right:0px; @@ -62,10 +62,13 @@ body { font-size:4vw; color: #333; } +.layertwo{ + display: None; +} noscript{ color:red; } -#text{ +.text{ color:green; position:absolute; top:50%; @@ -102,9 +105,9 @@ noscript{ } @keyframes coffeeready { 0% {background-color:white;} - 25% {background-color:green;} + 25% {background-color:rgb(100, 255, 100);} 50% {background-color:white;} - 75% {background-color:green;} + 75% {background-color:rgb(100, 255, 100);} 100% {background-color:white;} } @keyframes unknown { diff --git a/coffee_scale/static/js/coffee.js b/coffee_scale/static/js/coffee.js index 6b9b4a6..f885408 100644 --- a/coffee_scale/static/js/coffee.js +++ b/coffee_scale/static/js/coffee.js @@ -1,130 +1,161 @@ -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(); +// eslint-disable-next-line no-undef +var client = new Paho.MQTT.Client("sika.sahkoinsinoorikilta.fi", 9001, username); +client.onMessageArrived = function (message) { + // eslint-disable-next-line no-console + 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); // eslint-disable-line no-console + 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"); // eslint-disable-line no-console + //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(){ + $("body").addClass("coffeeready"); + // autoclear animation class in 10s + setTimeout(function(){$("body").removeClass("coffeeready");}, 10000); +} +function hotEffect(ev){ + var opa = Math.max(100 - ev.detail / 90000,0); + $("#upper").css({opacity: opa/100}); +} +function brewAnimStart(){ + $(".text").addClass("brewing"); + $(".layerone").hide(); + $(".layertwo").show(); + +} +function brewAnimEnd(){ + $(".text").removeClass("brewing"); + $(".layertwo").hide(); + $(".layerone").show(); +} +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(){ + var now = new Date(); + var timeDiff = Math.max(now.getTime() - lastBrew.getTime(), 0); + var eve = new CustomEvent("dtUpdate", {'detail': timeDiff}); + window.dispatchEvent(eve); +} +function updateBrewTime(ev){ + var timeDiff = ev.detail; + var timeStr; + if (timeDiff < 3600000){ + timeStr = Math.round(timeDiff / 60000) + ' min' + } else if (timeDiff < 10000* 3600 * 1000){ // 1000h + timeStr = '~' + Math.round(timeDiff / 3600000 * 2) / 2 + ' h'; + } else { + timeStr = "???" + } + $("#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); + window.addEventListener("dtUpdate", updateBrewTime); + window.addEventListener("dtUpdate", hotEffect); + + //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..b09a25d 100644 --- a/coffee_scale/templates/coffee.html +++ b/coffee_scale/templates/coffee.html @@ -10,13 +10,16 @@ +