Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e6b4153e6 | |||
| 66e86ab621 |
@@ -0,0 +1,74 @@
|
|||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.conf import settings
|
||||||
|
from coffee_scale.models import Brewing
|
||||||
|
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import random
|
||||||
|
|
||||||
|
brewing = False
|
||||||
|
lastbrew = timezone.now()
|
||||||
|
lastcups = 0
|
||||||
|
|
||||||
|
|
||||||
|
def on_connect(client, userdata, flags, rc):
|
||||||
|
if rc != 0:
|
||||||
|
print("Failed to connect with result code: {}".format(rc))
|
||||||
|
|
||||||
|
print("Connected with result code: {}".format(rc))
|
||||||
|
client.subscribe(settings.MQTT_SETTINGS.HOST.TOPICS.CUPS)
|
||||||
|
client.subscribe(settings.MQTT_SETTINGS.HOST.TOPICS.BREWING)
|
||||||
|
client.subscribe(settings.MQTT_SETTINGS.HOST.TOPICS.BREW_TIME)
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(client, userdata, message):
|
||||||
|
print("%s %s".format(message.topic, message.payload.decode()))
|
||||||
|
|
||||||
|
|
||||||
|
def on_message_cups(client, userdata, message):
|
||||||
|
cups = int(message.payload.decode())
|
||||||
|
print("cups: {}".format(cups))
|
||||||
|
print("{}".format(timezone.now()))
|
||||||
|
|
||||||
|
# checks if new coffee was brewed so we don't add the same brewing again to db
|
||||||
|
global lastcups # ;/ have to use global to store state instead of class
|
||||||
|
if cups > lastcups:
|
||||||
|
new_brew = Brewing(cups=cups, time=timezone.now())
|
||||||
|
print(new_brew.time)
|
||||||
|
new_brew.save()
|
||||||
|
lastcups = cups
|
||||||
|
|
||||||
|
|
||||||
|
def on_message_brewtime(client, userdata, message):
|
||||||
|
brewtime = datetime.fromtimestamp(int(message.payload.decode()))
|
||||||
|
print("brewtime: {}".format(brewtime))
|
||||||
|
|
||||||
|
|
||||||
|
def on_message_brewing(client, userdata, message):
|
||||||
|
brewing = bool(int(message.payload.decode()))
|
||||||
|
print("brewing: {}".format(brewing))
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Fetches coffee mqtt messages"
|
||||||
|
|
||||||
|
def add_arquments(self, parser):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
self.username = "coffee-user-%d".format(random.randint(0, 100))
|
||||||
|
self.client = mqtt.Client("coffee")
|
||||||
|
self.client.username_pw_set(self.username, password=None)
|
||||||
|
self.client.tls_set()
|
||||||
|
|
||||||
|
# callbacks for different topics
|
||||||
|
self.client.message_callback_add(settings.MQTT_SETTINGS.HOST.TOPICS.BREW_TIME, on_message_brewtime)
|
||||||
|
self.client.message_callback_add(settings.MQTT_SETTINGS.HOST.TOPICS.CUPS, on_message_cups)
|
||||||
|
self.client.message_callback_add(settings.MQTT_SETTINGS.HOST.TOPICS.BREWING, on_message_brewing)
|
||||||
|
|
||||||
|
# self.client.connect("localhost", port=1883) # used for local testing
|
||||||
|
self.client.connect(settings.MQTT_SETTINGS.HOST, port=settings.MQTT_SETTINGS.PORT)
|
||||||
|
self.client.on_message = on_message
|
||||||
|
self.client.on_connect = on_connect
|
||||||
|
while True:
|
||||||
|
self.client.loop()
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11 on 2017-11-19 10:55
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Brewing',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('cups', models.PositiveSmallIntegerField()),
|
||||||
|
('time', models.DateTimeField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
# Create your models here.
|
|
||||||
|
class Brewing(models.Model):
|
||||||
|
cups = models.PositiveSmallIntegerField()
|
||||||
|
time = models.DateTimeField()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from infoscreen.models import Rotation, InfoItem, InfoInstance
|
|||||||
from infoscreen.models import ImageInfoItem, ExternalImageInfoItem, ABBInfoItem
|
from infoscreen.models import ImageInfoItem, ExternalImageInfoItem, ABBInfoItem
|
||||||
from infoscreen.models import ExternalWebsiteInfoItem
|
from infoscreen.models import ExternalWebsiteInfoItem
|
||||||
from infoscreen.models import VideoInfoItem
|
from infoscreen.models import VideoInfoItem
|
||||||
|
from infoscreen.models import CoffeeStatsInfoItem
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
admin.site.register(Rotation)
|
admin.site.register(Rotation)
|
||||||
@@ -15,3 +16,4 @@ admin.site.register(ABBInfoItem)
|
|||||||
admin.site.register(InfoInstance)
|
admin.site.register(InfoInstance)
|
||||||
admin.site.register(ExternalWebsiteInfoItem)
|
admin.site.register(ExternalWebsiteInfoItem)
|
||||||
admin.site.register(VideoInfoItem)
|
admin.site.register(VideoInfoItem)
|
||||||
|
admin.site.register(CoffeeStatsInfoItem)
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11 on 2017-11-19 14:41
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('infoscreen', '0006_delete_hsldatamodel'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CoffeeStatsInfoItem',
|
||||||
|
fields=[
|
||||||
|
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||||
|
],
|
||||||
|
bases=('infoscreen.infoitem',),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -269,6 +269,21 @@ class HslInfoItem(InfoItem):
|
|||||||
return "/static/html/hsl_create.html"
|
return "/static/html/hsl_create.html"
|
||||||
|
|
||||||
|
|
||||||
|
class CoffeeStatsInfoItem(InfoItem):
|
||||||
|
"""Class for Coffee statistics Infoscreen item."""
|
||||||
|
|
||||||
|
display_name = _("Coffee statistics")
|
||||||
|
|
||||||
|
def get_template_url(self):
|
||||||
|
"""Return HSL infoitem template url."""
|
||||||
|
return "/static/html/coffee_stats.html"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_create_template_url():
|
||||||
|
"""Call create HSL infoitem template url command."""
|
||||||
|
return "/static/html/coffee_stats_create.html"
|
||||||
|
|
||||||
|
|
||||||
class ExternalImageInfoItem(InfoItem):
|
class ExternalImageInfoItem(InfoItem):
|
||||||
"""Class for External Image Infoscreen item."""
|
"""Class for External Image Infoscreen item."""
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<link rel="stylesheet" href="/static/css/hsl.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/locale/fi.js"></script>
|
||||||
|
<script type="text/javascript" src="dygraph.js"></script>
|
||||||
|
<link rel="stylesheet" src="dygraph.css" /></head>
|
||||||
|
|
||||||
|
<div class="container" ng-controller="CoffeeStatsController">
|
||||||
|
<div id="div_g"></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<div ng-controller="infoadmin_coffeestatsitem_create" style="margin-top:20px;">
|
||||||
|
<div>
|
||||||
|
Create new item to show coffee statistics. Name is used only as identifier
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" ng-model="item.name"></input>
|
||||||
|
</div>
|
||||||
|
<input type="button" class="btn btn-success" ng-click="send()" value="create"></input>
|
||||||
|
</div>
|
||||||
|
<!--maybe later add option to choose data range, daily, monthly...etc.
|
||||||
@@ -186,5 +186,6 @@ var simple_controllers = [
|
|||||||
"hslitem",
|
"hslitem",
|
||||||
"websiteitem",
|
"websiteitem",
|
||||||
"apyitem",
|
"apyitem",
|
||||||
|
"coffeestatsitem",
|
||||||
];
|
];
|
||||||
_.each(simple_controllers, controllerGenerator);
|
_.each(simple_controllers, controllerGenerator);
|
||||||
|
|||||||
@@ -142,5 +142,19 @@ app.controller('timetableCtrl',
|
|||||||
|
|
||||||
update_clock();
|
update_clock();
|
||||||
load();
|
load();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('CoffeeStatsController', function($scope, $http) {
|
||||||
|
function load() {
|
||||||
|
$http.get('/infoscreen/coffee_data')
|
||||||
|
.then(function(response) {
|
||||||
|
const g = new Dygraph(document.getElementById('div_g'), response.data, {
|
||||||
|
drawPoints: true,
|
||||||
|
valueRange: [0, 10],
|
||||||
|
labels: ['Time', 'Cups']
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
load();
|
||||||
|
});
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ from infoscreen.views import CurrentHSLView
|
|||||||
from infoscreen.views import createApyItem
|
from infoscreen.views import createApyItem
|
||||||
from infoscreen.views import hsl_timetable_settings
|
from infoscreen.views import hsl_timetable_settings
|
||||||
from infoscreen.views import get_apy_json
|
from infoscreen.views import get_apy_json
|
||||||
|
from infoscreen.views import createCoffeeStatsItem
|
||||||
|
from infoscreen.views import CoffeeStatsView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', default),
|
url(r'^$', default),
|
||||||
@@ -45,6 +47,7 @@ urlpatterns = [
|
|||||||
url(r'^create_sossoitem$', createSossoItem),
|
url(r'^create_sossoitem$', createSossoItem),
|
||||||
url(r'^create_eventitem$', createEventItem),
|
url(r'^create_eventitem$', createEventItem),
|
||||||
url(r'^create_hslitem$', createHslItem),
|
url(r'^create_hslitem$', createHslItem),
|
||||||
|
url(r'^create_coffeestatsitem$', createCoffeeStatsItem),
|
||||||
url(r'^create_apyitem$', createApyItem),
|
url(r'^create_apyitem$', createApyItem),
|
||||||
url(r'^create_websiteitem$', createExternalWebsiteItem),
|
url(r'^create_websiteitem$', createExternalWebsiteItem),
|
||||||
url(r'^create_rotation$', create_rotation),
|
url(r'^create_rotation$', create_rotation),
|
||||||
@@ -52,5 +55,6 @@ urlpatterns = [
|
|||||||
url(r'^hsl_data$', CurrentHSLView),
|
url(r'^hsl_data$', CurrentHSLView),
|
||||||
url(r'^hsl_data/settings$', hsl_timetable_settings),
|
url(r'^hsl_data/settings$', hsl_timetable_settings),
|
||||||
url(r'^apyjson', get_apy_json),
|
url(r'^apyjson', get_apy_json),
|
||||||
|
url(r'^coffee_data$', CoffeeStatsView),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from infoscreen.models import ExternalWebsiteInfoItem
|
|||||||
from infoscreen.models import ImageUploadForm
|
from infoscreen.models import ImageUploadForm
|
||||||
from infoscreen.models import ApyInfoItem
|
from infoscreen.models import ApyInfoItem
|
||||||
from infoscreen.models import VideoInfoItem
|
from infoscreen.models import VideoInfoItem
|
||||||
|
from infoscreen.models import CoffeeStatsInfoItem
|
||||||
|
|
||||||
|
|
||||||
@login_required(login_url='/login')
|
@login_required(login_url='/login')
|
||||||
@@ -188,3 +189,4 @@ createExternalWebsiteItem = create_item_generator(ExternalWebsiteInfoItem)
|
|||||||
createEventItem = create_item_generator(EventInfoItem)
|
createEventItem = create_item_generator(EventInfoItem)
|
||||||
createApyItem = create_item_generator(ApyInfoItem)
|
createApyItem = create_item_generator(ApyInfoItem)
|
||||||
createVideoItem = create_item_generator(VideoInfoItem)
|
createVideoItem = create_item_generator(VideoInfoItem)
|
||||||
|
createCoffeeStatsItem = create_item_generator(CoffeeStatsInfoItem)
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ from django.shortcuts import render
|
|||||||
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
|
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.db.models.functions import ExtractWeek
|
||||||
|
|
||||||
from infoscreen.models import Rotation, InfoItem, InfoInstance
|
from infoscreen.models import Rotation, InfoItem, InfoInstance
|
||||||
|
from coffee_scale.models import Brewing
|
||||||
from infoscreen.hsl_fetcher import fetch as hsl_fetch
|
from infoscreen.hsl_fetcher import fetch as hsl_fetch
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -99,3 +102,15 @@ def CurrentHSLView(request, *args, **kwargs):
|
|||||||
return JsonResponse(error, status=200)
|
return JsonResponse(error, status=200)
|
||||||
|
|
||||||
return JsonResponse(api_resp, status=200, safe=False)
|
return JsonResponse(api_resp, status=200, safe=False)
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
def CoffeeStatsView(request, *args, **kwargs):
|
||||||
|
# stats_data = Brewing.objects.annotate(
|
||||||
|
# week=ExtractWeek('time')).values('week').get(time__week=timezone.now().isocalendar()[2])
|
||||||
|
# do filtering here based on the data we want to show
|
||||||
|
stats_data = {}
|
||||||
|
for entry in Brewing.objects.all():
|
||||||
|
stats_data[entry.id] = {'cups': entry.cups, 'time': entry.time}
|
||||||
|
|
||||||
|
return JsonResponse(stats_data, status=200, safe=False)
|
||||||
|
|||||||
+4
-1
@@ -14,11 +14,14 @@
|
|||||||
"author": "SIK ry",
|
"author": "SIK ry",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"dygraphs": "^2.0.0",
|
||||||
"eslint": "3.19.0",
|
"eslint": "3.19.0",
|
||||||
"remark-cli": "^4.0.0",
|
"remark-cli": "^4.0.0",
|
||||||
"remark-preset-lint-recommended": "^3.0.1"
|
"remark-preset-lint-recommended": "^3.0.1"
|
||||||
},
|
},
|
||||||
"remarkConfig": {
|
"remarkConfig": {
|
||||||
"plugins": ["remark-preset-lint-recommended"]
|
"plugins": [
|
||||||
|
"remark-preset-lint-recommended"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,3 +33,4 @@ django-import-export==0.5.1
|
|||||||
django-password-reset==1.0
|
django-password-reset==1.0
|
||||||
pyexcel==0.5.6
|
pyexcel==0.5.6
|
||||||
pyexcel-xlsx==0.5.2
|
pyexcel-xlsx==0.5.2
|
||||||
|
paho-mqtt==1.3.1
|
||||||
|
|||||||
Reference in New Issue
Block a user