488 lines
15 KiB
Lua
488 lines
15 KiB
Lua
|
|
local composer = require( "composer" )
|
|
|
|
local json = require("json")
|
|
local widget = require("widget")
|
|
local AskiRepository = require("askirepository")
|
|
|
|
local scene = composer.newScene()
|
|
|
|
-- Globale Variablen für Datenabruf
|
|
local yesterdayDate = nil
|
|
local selectedDate = nil
|
|
|
|
-- -----------------------------------------------------------------------------------
|
|
-- Code outside of the scene event functions below will only be executed ONCE unless
|
|
-- the scene is removed entirely (not recycled) via "composer.removeScene()"
|
|
-- -----------------------------------------------------------------------------------
|
|
-- Hilfsfunktion: Hole Datum minus 7 Tage Datum
|
|
local function getCurrentAndLastDate(isCurrentDate)
|
|
local currentTime = os.time()
|
|
--print("Aktuelle Zeit (Timestamp): " .. currentTime)
|
|
local lastSevenDayTime = currentTime - (24 * 60 * 60 * 7) -- 7 Tage zurück
|
|
--print("Vor 7 Tagen Zeit (Timestamp): " .. lastSevenDayTime)
|
|
if isCurrentDate then
|
|
return os.date("*t", currentTime - (24 * 60 * 60)) -- minus 1 Tag für gestern
|
|
else
|
|
return os.date("*t", lastSevenDayTime - (24 * 60 * 60)) -- nochmal minus 1 Tag für vor 7 Tage
|
|
end
|
|
end
|
|
|
|
-- Hilfsfunktion: Formatiere Datum für API
|
|
local function formatDateForAPI(dateTable, isEndOfDay)
|
|
if isEndOfDay then
|
|
return string.format("%04d-%02d-%02dT23:59:59.999Z",
|
|
dateTable.year, dateTable.month, dateTable.day)
|
|
else
|
|
return string.format("%04d-%02d-%02dT00:00:00.000Z",
|
|
dateTable.year, dateTable.month, dateTable.day)
|
|
end
|
|
end
|
|
|
|
-- Go to Day scene02
|
|
local function gotoDay()
|
|
composer.gotoScene("scenen.scene01", {
|
|
effect = "fade",
|
|
time = 500
|
|
})
|
|
end
|
|
|
|
|
|
-- -----------------------------------------------------------------------------------
|
|
-- Scene event functions
|
|
-- -----------------------------------------------------------------------------------
|
|
|
|
-- create()
|
|
function scene:create( event )
|
|
|
|
local sceneGroup = self.view
|
|
-- Code here runs when the scene is first created but has not yet appeared on screen
|
|
-- Hintergrund
|
|
local bg = display.newRect(sceneGroup, display.contentCenterX, display.contentCenterY,
|
|
display.contentWidth, display.contentHeight)
|
|
bg:setFillColor(0.95, 0.95, 0.95)
|
|
|
|
-- aktuelles Tagesdatum -1 Tag für gestern
|
|
yesterdayDate = getCurrentAndLastDate(true)
|
|
-- Setze Standard-Datum auf vor 8 Tagen
|
|
selectedDate = getCurrentAndLastDate(false)
|
|
|
|
end
|
|
|
|
|
|
-- show()
|
|
function scene:show( event )
|
|
|
|
local sceneGroup = self.view
|
|
local phase = event.phase
|
|
|
|
if ( phase == "will" ) then
|
|
-- Code here runs when the scene is still off screen (but is about to come on screen)
|
|
|
|
elseif ( phase == "did" ) then
|
|
-- Hole die übergebenen Daten
|
|
local devices = event.params.devices
|
|
local currentCookie = event.params.cookie
|
|
-- Formatiere die Daten für die API
|
|
local fromDate = formatDateForAPI(selectedDate, false)
|
|
local toDate = formatDateForAPI(yesterdayDate, true)
|
|
local completedRequests = 0
|
|
local totalRequests = #devices
|
|
local currentDataSets = {}
|
|
|
|
-- Ausgabe der übergebenen Daten für Debugging
|
|
print("Scene02 ist jetzt sichtbar - Zeige Daten an")
|
|
print("Entspricht einer Woche von Gestern 7 Tage zurück")
|
|
print("Formatiertes Start-Datum für API: " .. fromDate)
|
|
print("Formatiertes End-Datum für API: " .. toDate)
|
|
print("Übergebene Geräte: " .. (devices and #devices or "Keine"))
|
|
print("Cookie:" .. tostring(currentCookie))
|
|
|
|
|
|
-- Funktion zur Anzeige des Balkendiagramms
|
|
local function displayBarChart(dataSets)
|
|
-- Entferne alte Diagramm-Anzeigen
|
|
for i = sceneGroup.numChildren, 1, -1 do
|
|
local child = sceneGroup[i]
|
|
if child._isChartDisplay then
|
|
child:removeSelf()
|
|
end
|
|
end
|
|
|
|
if not dataSets or type(dataSets) ~= "table" then
|
|
print("Keine Daten zum Anzeigen")
|
|
return
|
|
end
|
|
|
|
-- Definiere Farben für die Devices
|
|
local deviceColors = {
|
|
["Traun"] = {0.2, 0.4, 0.9}, -- Blau
|
|
["Lannach"] = {0.3, 0.8, 0.4}, -- Grün
|
|
["Sarleinsbach"] = {0.95, 0.6, 0.2} -- Orange
|
|
}
|
|
|
|
-- Parameter für das Diagramm
|
|
local chartX = display.contentCenterX
|
|
local chartY = 150
|
|
local chartWidth = display.contentWidth - 40
|
|
local chartHeight = 180
|
|
|
|
-- Hintergrund für das Diagramm
|
|
local chartBg = display.newRoundedRect(sceneGroup, chartX, chartY, chartWidth, chartHeight, 8)
|
|
chartBg:setFillColor(1, 1, 1)
|
|
chartBg.strokeWidth = 2
|
|
chartBg:setStrokeColor(0.7, 0.7, 0.7)
|
|
chartBg._isChartDisplay = true
|
|
|
|
-- Titel
|
|
local titleText = display.newText({
|
|
parent = sceneGroup,
|
|
text = "Wochenübersicht (kW)",
|
|
x = chartX,
|
|
y = chartY - chartHeight/2 - 15,
|
|
fontSize = 16,
|
|
font = native.systemFontBold
|
|
})
|
|
titleText:setFillColor(0.2, 0.2, 0.2)
|
|
titleText._isChartDisplay = true
|
|
|
|
-- Organisiere Daten nach Tag
|
|
local dayData = {} -- { ["2024-05-20"] = { Traun = value, Lannach = value, Sarleinsbach = value } }
|
|
local maxValue = 0
|
|
|
|
for deviceName, dataSet in pairs(dataSets) do
|
|
if not dataSet.error and type(dataSet) == "table" and #dataSet > 0 then
|
|
for _, meter in ipairs(dataSet) do
|
|
if meter.values and #meter.values > 0 then
|
|
for _, valueData in ipairs(meter.values) do
|
|
if valueData.value and valueData.datetime then
|
|
local date = valueData.datetime:match("(%d%d%d%d%-%d%d%-%d%d)")
|
|
if date then
|
|
if not dayData[date] then
|
|
dayData[date] = {}
|
|
end
|
|
dayData[date][deviceName] = valueData.value
|
|
maxValue = math.max(maxValue, valueData.value)
|
|
end
|
|
end
|
|
end
|
|
break -- Nur das erste Meter pro Device
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Sortiere Tage
|
|
local sortedDays = {}
|
|
for day in pairs(dayData) do
|
|
table.insert(sortedDays, day)
|
|
end
|
|
table.sort(sortedDays)
|
|
|
|
if #sortedDays == 0 then
|
|
local noDataText = display.newText({
|
|
parent = sceneGroup,
|
|
text = "Keine Daten zum Anzeigen",
|
|
x = chartX,
|
|
y = chartY,
|
|
fontSize = 14
|
|
})
|
|
noDataText:setFillColor(0.5, 0.5, 0.5)
|
|
noDataText._isChartDisplay = true
|
|
return
|
|
end
|
|
|
|
-- Berechne Diagramm-Parameter
|
|
local graphLeft = chartX - chartWidth/2 + 30
|
|
local graphRight = chartX + chartWidth/2 - 10
|
|
local graphTop = chartY - chartHeight/2 + 25
|
|
local graphBottom = chartY + chartHeight/2 - 35
|
|
local graphWidth = graphRight - graphLeft
|
|
local graphHeight = graphBottom - graphTop
|
|
|
|
-- Zeichne Y-Achse
|
|
local yAxis = display.newLine(sceneGroup, graphLeft, graphTop, graphLeft, graphBottom)
|
|
yAxis:setStrokeColor(0.5, 0.5, 0.5)
|
|
yAxis.strokeWidth = 2
|
|
yAxis._isChartDisplay = true
|
|
|
|
-- Zeichne X-Achse
|
|
local xAxis = display.newLine(sceneGroup, graphLeft, graphBottom, graphRight, graphBottom)
|
|
xAxis:setStrokeColor(0.5, 0.5, 0.5)
|
|
xAxis.strokeWidth = 2
|
|
xAxis._isChartDisplay = true
|
|
|
|
-- Y-Achsen-Beschriftung
|
|
local maxLabel = display.newText({
|
|
parent = sceneGroup,
|
|
text = string.format("%.0f", maxValue),
|
|
x = graphLeft - 15,
|
|
y = graphTop,
|
|
fontSize = 9
|
|
})
|
|
maxLabel:setFillColor(0.4, 0.4, 0.4)
|
|
maxLabel._isChartDisplay = true
|
|
|
|
local zeroLabel = display.newText({
|
|
parent = sceneGroup,
|
|
text = "0",
|
|
x = graphLeft - 15,
|
|
y = graphBottom,
|
|
fontSize = 9
|
|
})
|
|
zeroLabel:setFillColor(0.4, 0.4, 0.4)
|
|
zeroLabel._isChartDisplay = true
|
|
|
|
-- Zeichne Balken für jeden Tag
|
|
local dayWidth = graphWidth / #sortedDays
|
|
local barSpacing = 4
|
|
local barWidth = dayWidth - barSpacing * 2
|
|
|
|
for i, day in ipairs(sortedDays) do
|
|
local dayX = graphLeft + (i - 0.5) * dayWidth
|
|
local data = dayData[day]
|
|
|
|
-- Datum-Label
|
|
local dayLabel = display.newText({
|
|
parent = sceneGroup,
|
|
text = day:match("%d%d%-%d%d$"), -- Nur MM-DD
|
|
x = dayX,
|
|
y = graphBottom + 12,
|
|
fontSize = 8
|
|
})
|
|
dayLabel:setFillColor(0.3, 0.3, 0.3)
|
|
dayLabel._isChartDisplay = true
|
|
|
|
-- Berechne Gesamtwert für diesen Tag
|
|
local totalValue = 0
|
|
local deviceOrder = {"Traun", "Lannach", "Sarleinsbach"}
|
|
for _, deviceName in ipairs(deviceOrder) do
|
|
totalValue = totalValue + (data[deviceName] or 0)
|
|
end
|
|
|
|
if totalValue > 0 then
|
|
-- Zeichne gestapelten Balken - prozentual zur vollen Diagrammhöhe
|
|
local currentY = graphBottom
|
|
|
|
for _, deviceName in ipairs(deviceOrder) do
|
|
local value = data[deviceName] or 0
|
|
if value > 0 then
|
|
-- Berechne Segment-Höhe proportional zum Anteil am Gesamtwert
|
|
-- Balken nutzt die volle Höhe, Segmente sind prozentual
|
|
local percentage = value / totalValue
|
|
local segmentHeight = percentage * graphHeight
|
|
local segmentY = currentY - segmentHeight/2
|
|
|
|
-- Segment
|
|
local segment = display.newRect(sceneGroup, dayX, segmentY, barWidth, segmentHeight)
|
|
local color = deviceColors[deviceName] or {0.5, 0.5, 0.5}
|
|
segment:setFillColor(unpack(color))
|
|
segment.strokeWidth = 1
|
|
segment:setStrokeColor(0.9, 0.9, 0.9)
|
|
segment._isChartDisplay = true
|
|
|
|
-- Wert-Label im Segment (wenn genug Platz)
|
|
if segmentHeight > 12 then
|
|
local valueLabel = display.newText({
|
|
parent = sceneGroup,
|
|
text = string.format("%d\n%.0f%%", value, percentage * 100),
|
|
x = dayX,
|
|
y = segmentY,
|
|
fontSize = 7,
|
|
font = native.systemFontBold
|
|
})
|
|
valueLabel:setFillColor(1, 1, 1)
|
|
valueLabel._isChartDisplay = true
|
|
end
|
|
|
|
-- Bewege Y-Position nach oben für nächstes Segment
|
|
currentY = currentY - segmentHeight
|
|
end
|
|
end
|
|
|
|
-- Gesamtwert über dem Balken
|
|
local totalLabel = display.newText({
|
|
parent = sceneGroup,
|
|
text = string.format("%d", totalValue),
|
|
x = dayX,
|
|
y = graphTop - 8,
|
|
fontSize = 8,
|
|
font = native.systemFontBold
|
|
})
|
|
totalLabel:setFillColor(0.2, 0.2, 0.2)
|
|
totalLabel._isChartDisplay = true
|
|
end
|
|
end
|
|
|
|
-- Legende
|
|
local legendY = chartY + chartHeight/2 + 20
|
|
local legendDevices = {
|
|
{name = "Traun", color = deviceColors["Traun"]},
|
|
{name = "Lannach", color = deviceColors["Lannach"]},
|
|
{name = "Sarleinsbach", color = deviceColors["Sarleinsbach"]}
|
|
}
|
|
|
|
for i, legend in ipairs(legendDevices) do
|
|
local legendX = chartX - 90 + ((i - 1) * 90)
|
|
|
|
-- Farbbox
|
|
local colorBox = display.newRect(sceneGroup, legendX - 20, legendY, 12, 8)
|
|
colorBox:setFillColor(unpack(legend.color))
|
|
colorBox._isChartDisplay = true
|
|
|
|
-- Device Name
|
|
local legendText = display.newText({
|
|
parent = sceneGroup,
|
|
text = legend.name,
|
|
x = legendX + 10,
|
|
y = legendY,
|
|
fontSize = 10
|
|
})
|
|
legendText:setFillColor(0.3, 0.3, 0.3)
|
|
legendText._isChartDisplay = true
|
|
end
|
|
|
|
print("✓ Balkendiagramm erstellt für " .. #sortedDays .. " Tage")
|
|
end
|
|
|
|
-- Funktion zum Neuladen der Daten
|
|
local function reloadDataFromSevenDays()
|
|
if not currentCookie or not devices or not selectedDate then
|
|
print("Fehler: Cookie, Devices oder Datum fehlt")
|
|
return
|
|
end
|
|
|
|
print("Lade Daten von " ..
|
|
string.format("%04d-%02d-%02d", selectedDate.year, selectedDate.month, selectedDate.day) ..
|
|
" bis " ..
|
|
string.format("%04d-%02d-%02d", yesterdayDate.year, yesterdayDate.month, yesterdayDate.day) ..
|
|
" für " .. #devices .. " Geräte")
|
|
|
|
|
|
-- Lade-Anzeige
|
|
local loadingOverlay = display.newRect(sceneGroup, display.contentCenterX, display.contentCenterY,
|
|
display.contentWidth, display.contentHeight)
|
|
loadingOverlay:setFillColor(0, 0, 0, 0.5)
|
|
loadingOverlay._isDataDisplay = true
|
|
|
|
local loadingText = display.newText({
|
|
parent = sceneGroup,
|
|
text = "Lade Daten...",
|
|
x = display.contentCenterX,
|
|
y = display.contentCenterY,
|
|
fontSize = 20,
|
|
font = native.systemFontBold
|
|
})
|
|
loadingText:setFillColor(1, 1, 1)
|
|
loadingText._isDataDisplay = true
|
|
|
|
-- Callback für jeden Device
|
|
local function onDataComplete(deviceName, success, dataSet, response)
|
|
completedRequests = completedRequests + 1
|
|
|
|
if success then
|
|
currentDataSets[deviceName] = dataSet
|
|
else
|
|
currentDataSets[deviceName] = { error = tostring(response) }
|
|
end
|
|
|
|
if completedRequests >= totalRequests then
|
|
-- Entferne Lade-Anzeige
|
|
loadingOverlay:removeSelf()
|
|
loadingText:removeSelf()
|
|
|
|
-- Ausgabe der kompletten newDeviceDataSets Table
|
|
print("\n========== ALLE GELADENEN DATEN (newDeviceDataSets) ==========")
|
|
print("JSON-Format:")
|
|
print(json.prettify(currentDataSets))
|
|
print("===============================================================\n")
|
|
-- Zeige neue Daten
|
|
print("✓ Daten erfolgreich geladen")
|
|
|
|
-- Zeige Balkendiagramm
|
|
displayBarChart(currentDataSets)
|
|
end
|
|
end
|
|
|
|
-- Lade Daten für alle Devices
|
|
for i, device in ipairs(devices) do
|
|
local function deviceCallback(success, dataSet, response)
|
|
onDataComplete(device.name, success, dataSet, response)
|
|
end
|
|
|
|
AskiRepository.getPostHistoricData(
|
|
"https://api.portal.aski.at/historical_values/power",
|
|
device.deviceId,
|
|
fromDate,
|
|
toDate,
|
|
currentCookie,
|
|
deviceCallback
|
|
)
|
|
end
|
|
end
|
|
|
|
-- Code here runs when the scene is entirely on screen
|
|
reloadDataFromSevenDays() -- Lade die Daten direkt beim Anzeigen der Scene02
|
|
-- Inhalt Diagramm anzeigen
|
|
|
|
|
|
-- Button für zurück zu Scene01
|
|
-- Design für Tagesansicht Button
|
|
local dayBtn = widget.newButton({
|
|
label = "Vortagesansicht",
|
|
emboss = false,
|
|
shape = "roundedRect",
|
|
width = 200,
|
|
height = 40,
|
|
cornerRadius = 8,
|
|
fillColor = { default = { 0.3, 0.5, 0.8, 1 }, over = { 0.2, 0.4, 0.7, 1 } },
|
|
strokeColor = { default = { 0.2, 0.4, 0.2, 1 }, over = { 0.1, 0.3, 0.1, 1 } },
|
|
strokeWidth = 2,
|
|
labelColor = { default = { 1, 1, 1 }, over = { 1, 1, 1 } },
|
|
fontSize = 16,
|
|
x = display.contentCenterX,
|
|
y = 380
|
|
})
|
|
-- Event-Listener für Tagesansicht Button
|
|
sceneGroup:insert(dayBtn)
|
|
dayBtn:addEventListener("tap", gotoDay)
|
|
end
|
|
end
|
|
|
|
|
|
-- hide()
|
|
function scene:hide( event )
|
|
|
|
local sceneGroup = self.view
|
|
local phase = event.phase
|
|
|
|
if ( phase == "will" ) then
|
|
-- Code here runs when the scene is on screen (but is about to go off screen)
|
|
|
|
elseif ( phase == "did" ) then
|
|
-- Code here runs immediately after the scene goes entirely off screen
|
|
|
|
end
|
|
end
|
|
|
|
|
|
-- destroy()
|
|
function scene:destroy( event )
|
|
|
|
local sceneGroup = self.view
|
|
-- Code here runs prior to the removal of scene's view
|
|
print("Scene02 zerstört")
|
|
end
|
|
|
|
|
|
-- -----------------------------------------------------------------------------------
|
|
-- Scene event function listeners
|
|
-- -----------------------------------------------------------------------------------
|
|
scene:addEventListener( "create", scene )
|
|
scene:addEventListener( "show", scene )
|
|
scene:addEventListener( "hide", scene )
|
|
scene:addEventListener( "destroy", scene )
|
|
-- -----------------------------------------------------------------------------------
|
|
|
|
return scene
|