diff --git a/coffee_scale/__init__.py b/coffee_scale/__init__.py new file mode 100644 index 0000000..c2eaae7 --- /dev/null +++ b/coffee_scale/__init__.py @@ -0,0 +1,3 @@ +from . import mqtt + +mqtt.client.loop_start() diff --git a/coffee_scale/admin.py b/coffee_scale/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/coffee_scale/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/coffee_scale/apps.py b/coffee_scale/apps.py new file mode 100644 index 0000000..7785abc --- /dev/null +++ b/coffee_scale/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CoffeeScaleConfig(AppConfig): + name = 'coffee_scale' diff --git a/coffee_scale/migrations/__init__.py b/coffee_scale/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/coffee_scale/models.py b/coffee_scale/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/coffee_scale/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/coffee_scale/mqtt.py b/coffee_scale/mqtt.py new file mode 100644 index 0000000..01a1c1d --- /dev/null +++ b/coffee_scale/mqtt.py @@ -0,0 +1,65 @@ +import paho.mqtt.client as mqtt +import logging +import datetime +from collections import deque + +TOPIC_TEMP = "sik/kiltahuone/kahvivaaka/temperature" +TOPIC_WEIGHT = "sik/kiltahuone/kahvivaaka/weight" + +BREWING_DIFFERENTIAL = 60 +# setting down the pan creates at least 800 g of weight +BREWING_DIFFERENTIAL_ERROR_MAX = 800 +WEIGHT_DEQUE_LENGTH = 20 +weight_deque = deque(maxlen=WEIGHT_DEQUE_LENGTH) +latest = { + 'last_brew': datetime.datetime.now() +} + + +def on_connect(client, userdata, flags, rc): + logging.info('Subscribing to all topics on mqtt.sik.party.') + client.subscribe("#") + + +def on_message(client, userdata, msg): + if msg.topic == TOPIC_TEMP: + latest['temp'] = float(msg.payload.decode('utf-8')) + elif msg.topic == TOPIC_WEIGHT: + weight = float(msg.payload.decode('utf-8')) + latest['weight'] = weight + weight_deque.appendleft(weight) + + +def on_disconnect(client, userdata, rc): + client.loop_stop(force=False) + if rc != 0: + print("Unexpected disconnection.") + else: + print("Disconnected") + + +def get_latest(): + if len(weight_deque) > 2: + first = weight_deque[0] + last = weight_deque[len(weight_deque) - 1] + diff = first - last # reverse order + + if len(weight_deque) < WEIGHT_DEQUE_LENGTH: + brewing = False + else: + if BREWING_DIFFERENTIAL < diff < BREWING_DIFFERENTIAL_ERROR_MAX: + brewing = True + latest['last_brew'] = datetime.datetime.now() + else: + brewing = False + + latest['brewing'] = brewing + return latest + + +client = mqtt.Client() +client.on_connect = on_connect +client.on_message = on_message +client.on_disconnect = on_disconnect + +client.connect("mqtt.sik.party", 1883, 60) diff --git a/coffee_scale/static/img/coffeecup3.png b/coffee_scale/static/img/coffeecup3.png new file mode 100644 index 0000000..5170d5f Binary files /dev/null and b/coffee_scale/static/img/coffeecup3.png differ diff --git a/coffee_scale/static/img/smokes.png b/coffee_scale/static/img/smokes.png new file mode 100644 index 0000000..cb19720 Binary files /dev/null and b/coffee_scale/static/img/smokes.png differ diff --git a/coffee_scale/templates/coffee.html b/coffee_scale/templates/coffee.html new file mode 100644 index 0000000..f34d20d --- /dev/null +++ b/coffee_scale/templates/coffee.html @@ -0,0 +1,292 @@ + + +Coffee Cups @Guild Room - AYY SIK RY + + + + + + + + + + + + + + + +
+ + + Kahvivaaka + +
+ +
+
+ +
+
+
+
+
+ + diff --git a/coffee_scale/tests.py b/coffee_scale/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/coffee_scale/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/coffee_scale/urls.py b/coffee_scale/urls.py new file mode 100644 index 0000000..b9c54e6 --- /dev/null +++ b/coffee_scale/urls.py @@ -0,0 +1,14 @@ +from django.conf.urls import url +from django.views.generic.base import RedirectView + +from .views import coffee_view, cups_view + +favicon_view = RedirectView.as_view(url='static/img/favicon.ico', permanent=True) + +urlpatterns = [ + + # landing page + url(r'^$', coffee_view), + url(r'^cups', cups_view), + +] diff --git a/coffee_scale/views.py b/coffee_scale/views.py new file mode 100644 index 0000000..affdd00 --- /dev/null +++ b/coffee_scale/views.py @@ -0,0 +1,40 @@ +from django.shortcuts import render +from django.http import JsonResponse + +import datetime +from .mqtt import get_latest + + +def get_cups_from_weight(weight): + if not weight: + return 0 + + EMPTY = 820 # grams + FULL = 2000 # grams + cups = 10 * (weight - EMPTY) / (FULL - EMPTY) + + cups = round(cups, 1) + if cups < 0: + cups = 0 + if cups > 10: + cups = 10 + + return cups + + +def coffee_view(request): + return render(request, 'coffee.html') + + +def cups_view(request): + now = datetime.datetime.now() + latest = get_latest() + cups = get_cups_from_weight(latest.get('weight')) + data = { + 'date': now, + 'cups': cups, + 'temp': latest.get('temp'), + 'last_brew': latest.get('last_brew'), + 'brewing': latest.get('brewing') + } + return JsonResponse(data) diff --git a/requirements.txt b/requirements.txt index ab2442a..3c028d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,3 +25,4 @@ 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 diff --git a/sikweb/settings-sample.py b/sikweb/settings-sample.py index 4de7034..1faf433 100644 --- a/sikweb/settings-sample.py +++ b/sikweb/settings-sample.py @@ -50,6 +50,7 @@ INSTALLED_APPS = [ 'webapp', 'members', 'infoscreen', + 'coffee_scale', 'rest_framework', 'django_nose', 'bootstrap3', diff --git a/sikweb/urls.py b/sikweb/urls.py index 924c5b4..0113b57 100644 --- a/sikweb/urls.py +++ b/sikweb/urls.py @@ -25,11 +25,13 @@ from django.contrib.staticfiles import views as static_views import webapp.urls import infoscreen.urls import members.urls +import coffee_scale.urls urlpatterns = [ url(r'', include('webapp.urls')), url(r'^members/', include('members.urls')), url(r'^infoscreen/', include('infoscreen.urls')), + url(r'^coffee/', include('coffee_scale.urls')), # admin url(r'^admin/', admin.site.urls),