290 lines
12 KiB
Python
290 lines
12 KiB
Python
# main.py
|
|
#
|
|
# Copyright 2023 Nikola Mitrojevic, Emanuel Loos & Tobias Bramböck
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
# Import statements
|
|
import kivy
|
|
import numpy as np
|
|
import matplotlib as mpl
|
|
import matplotlib.pyplot as plt
|
|
from datetime import datetime, date, timedelta
|
|
from kivy.app import App
|
|
from kivy.lang import Builder
|
|
from kivy.uix.boxlayout import BoxLayout
|
|
from kivy.uix.floatlayout import FloatLayout
|
|
from kivy.uix.button import Button
|
|
from kivy.uix.dropdown import DropDown
|
|
from kivy.uix.textinput import TextInput
|
|
from kivy.uix.label import Label
|
|
from kivy.uix.screenmanager import ScreenManager, Screen
|
|
from kivy.uix.togglebutton import ToggleButton
|
|
from kivy.uix.scrollview import ScrollView
|
|
from kivy.garden.matplotlib import FigureCanvasKivyAgg
|
|
from awattar import AwattarClient
|
|
|
|
# Create AwattarClient instance
|
|
client = AwattarClient('AT')
|
|
|
|
# Set initial date and get energy prices
|
|
date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
data = client.request(date)
|
|
hours = np.arange(24)
|
|
prices = np.array([item.marketprice/10 for item in data])
|
|
|
|
# Define DiagramWidget class
|
|
class DiagramWidget(FigureCanvasKivyAgg):
|
|
def __init__(self, figure, **kwargs):
|
|
super().__init__(figure, **kwargs)
|
|
self.ax = self.figure.axes[0]
|
|
figure.patch.set_facecolor('black')
|
|
self.minimum_height = 100 # Set a minimum height for the widget
|
|
self.ax.set_facecolor('black') # set background color to black
|
|
self.ax.tick_params(colors='white') # set tick color to white
|
|
for spine in self.ax.spines.values():
|
|
spine.set_edgecolor('white') # set spine edge color to white
|
|
spine.set_facecolor('black') # set spine face color to none
|
|
|
|
# Define MyBoxLayout widget
|
|
class MyBoxLayout(BoxLayout):
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
|
|
# Set widget properties
|
|
self.orientation = 'vertical'
|
|
self.background_color = (0, 0, 0, 1) # set background color to black
|
|
label_button_layout = BoxLayout(orientation='horizontal', size_hint_y=None, height='50dp')
|
|
label_button_layout.add_widget(Label(text='[b]Strompreise & Stromerzeugung[/b]', markup=True, halign='left', valign='top', font_size='20sp', size_hint=(self.width, None), size=(0, 100), pos_hint={'x':0,'y':0}))
|
|
picture_button = Button(size_hint=(None, None), size=('50dp', '60dp'), pos_hint={'x': 0.8, 'y': 0})
|
|
picture_button.background_normal = 'Einstellungsrad.svg'
|
|
picture_button.bind(on_release=self.open_settings)
|
|
label_button_layout.add_widget(picture_button)
|
|
self.add_widget(label_button_layout)
|
|
self.date_label = Label(text=date.strftime('%d.%m.%Y'), font_size='20sp', halign='left', valign='middle', size_hint=(1, None), height='50dp', pos_hint={'top': 1.0, 'x': 0.02})
|
|
self.add_widget(self.date_label)
|
|
|
|
# Define color map based on energy prices
|
|
cmap = mpl.colors.ListedColormap(['blue', 'green', 'yellow', 'red'])
|
|
bounds = [-float('inf'), 0, 5, 20, float('inf')] # Include 0 in color map bounds
|
|
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
|
|
color_map = cmap(norm(prices))
|
|
|
|
# Create plot figure
|
|
self.fig, self.ax = plt.subplots(figsize=(10, 5))
|
|
self.fig.subplots_adjust(left=0.075, bottom=0.175) # Adjust bottom margin to make space for buttons
|
|
|
|
# Add bars to plot using color map
|
|
for i in range(len(hours)):
|
|
self.ax.bar(hours[i], prices[i], width=0.5, edgecolor='white', linewidth=0.5, color=color_map[i])
|
|
|
|
# Set plot limits and ticks
|
|
self.ax.set(xlim=(-1, 24), xticks=hours)
|
|
|
|
# Create DiagramWidget and add it to the ScrollView
|
|
self.diagram_widget = DiagramWidget(figure=self.fig)
|
|
scroll_view = ScrollView(size_hint=(1, 0.6)) # Make ScrollView take up 60% of screen height
|
|
scroll_view.add_widget(self.diagram_widget)
|
|
self.add_widget(scroll_view)
|
|
|
|
# Add toggle buttons
|
|
toggle_box = BoxLayout(orientation='horizontal', size_hint=(1, 0.1)) # Make toggle_box take up 10% of screen height
|
|
toggle_box.spacing = '5dp'
|
|
toggle_box.padding = '5dp'
|
|
toggle_button1 = ToggleButton(text='Netto/Brutto')
|
|
toggle_button2 = ToggleButton(text='Stromherkunft einblenden')
|
|
toggle_box.add_widget(toggle_button1)
|
|
toggle_box.add_widget(toggle_button2)
|
|
self.add_widget(toggle_box)
|
|
|
|
# Add navigation buttons
|
|
button_box = BoxLayout(orientation='horizontal', size_hint=(1, 0.1)) # Make button_box take up 10% of screen height
|
|
button_box.spacing = '5dp'
|
|
button_box.padding = '5dp'
|
|
prev_button = ToggleButton(text='Prev Day')
|
|
prev_button.bind(on_press=self.previous_day)
|
|
next_button = ToggleButton(text='Next Day')
|
|
next_button.bind(on_press=self.next_day)
|
|
button_box.add_widget(prev_button)
|
|
button_box.add_widget(next_button)
|
|
self.add_widget(button_box)
|
|
|
|
# Update energy prices and plot
|
|
self.update_prices()
|
|
|
|
# Update energy prices and plot
|
|
def update_prices(self):
|
|
global prices, date
|
|
date_prices = client.request(date)
|
|
prices = np.array([item.marketprice/10 for item in date_prices])
|
|
|
|
# Define color map based on energy prices
|
|
cmap = mpl.colors.ListedColormap(['blue', 'green', 'yellow', 'red'])
|
|
bounds = [-float('inf'), 0, 5, 20, float('inf')] # Include 0 in color map bounds
|
|
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
|
|
color_map = cmap(norm(prices))
|
|
|
|
# Clear existing plot and add bars using color map
|
|
self.ax.clear()
|
|
for i in range(len(hours)):
|
|
self.ax.bar(hours[i], prices[i], width=0.5, edgecolor='white', linewidth=0.5, color=color_map[i])
|
|
|
|
# Set plot limits and ticks
|
|
self.ax.set(xlim=(-1, 24), xticks=hours)
|
|
|
|
self.date_label.text = date.strftime('%d.%m.%Y')
|
|
self.diagram_widget.draw()
|
|
|
|
# Navigate to previous day and update energy prices and plot
|
|
def previous_day(self, instance):
|
|
global date
|
|
date = date - timedelta(days=1)
|
|
self.update_prices()
|
|
|
|
# Navigate to next day and update energy prices and plot
|
|
def next_day(self, instance):
|
|
global date
|
|
date = date + timedelta(days=1)
|
|
self.update_prices()
|
|
|
|
# Dynamically adjust height of ScrollView and DiagramWidget
|
|
def on_size(self, *args):
|
|
self.diagram_widget.height = max(self.height * 0.6 - 100, self.diagram_widget.minimum_height) # Adjust height to make space for toggle_box and button_box
|
|
self.diagram_widget.width = self.width
|
|
|
|
def open_settings(self, instance):
|
|
app = App.get_running_app()
|
|
app.root.current = 'settings'
|
|
|
|
class SettingsScreen(Screen):
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
self.dropdown = DropDown()
|
|
self.create_dropdown()
|
|
self.create_input_boxes()
|
|
|
|
def create_dropdown(self):
|
|
main_button = Button(text='Select Option', size_hint=(None, None), size=(0, 0), pos_hint={'x': -50, 'y': -50})
|
|
main_button.bind(on_release=self.dropdown.open)
|
|
self.dropdown.bind(on_select=self.select_option)
|
|
|
|
options = ['Option 1', 'Option 2', 'Option 3']
|
|
|
|
for option in options:
|
|
btn = Button(text=option, size_hint_y=None, height=44)
|
|
btn.bind(on_release=lambda btn: self.dropdown.select(btn.text))
|
|
self.dropdown.add_widget(btn)
|
|
|
|
self.add_widget(main_button)
|
|
|
|
def select_option(self, instance, text):
|
|
print('Selected option:', text)
|
|
|
|
def create_input_boxes(self):
|
|
scroll_view = ScrollView()
|
|
input_box_layout = BoxLayout(orientation='vertical', size_hint_y=None, spacing=10, padding=10)
|
|
input_box_layout.bind(minimum_height=input_box_layout.setter('height'))
|
|
|
|
|
|
latitude = TextInput(hint_text=f'Breitengrad[°]', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(latitude)
|
|
gps_latitude_button = Button(text='GPS', size_hint=(None, None), size=('50dp', '60dp'), pos_hint={'x': 0, 'y': 0})
|
|
input_box_layout.add_widget(gps_latitude_button)
|
|
|
|
longitude = TextInput(hint_text=f'Längengrad[°]', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(longitude)
|
|
gps_longitude_button = Button(text='GPS', size_hint=(None, None), size=('50dp', '60dp'), pos_hint={'x': 0, 'y': 0})
|
|
input_box_layout.add_widget(gps_longitude_button)
|
|
|
|
azimuth = TextInput(hint_text=f'Azimut', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(azimuth)
|
|
|
|
tilt = TextInput(hint_text=f'Neigung', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(tilt)
|
|
|
|
peak = TextInput(hint_text=f'Peakleistung der Zellen', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(peak)
|
|
|
|
surface = TextInput(hint_text=f'Fläche der Zellen', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(surface)
|
|
|
|
efficiency = TextInput(hint_text=f'Wirkungsgrad der Zellen', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(efficiency)
|
|
|
|
temp_coeff = TextInput(hint_text=f'Temperaturkoeffizient', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(temp_coeff)
|
|
|
|
efficiency_dif = TextInput(hint_text=f'Effizienz diffuse Strahlung', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(efficiency_dif)
|
|
|
|
albedo = TextInput(hint_text=f'Albedo', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(albedo)
|
|
|
|
invert_perf = TextInput(hint_text=f'Wechselrichterleistung', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(invert_perf)
|
|
|
|
invert_eff = TextInput(hint_text=f'Wechselrichtereffizient', size_hint=(1, None), height='40dp')
|
|
input_box_layout.add_widget(invert_eff)
|
|
|
|
|
|
scroll_view.add_widget(input_box_layout)
|
|
|
|
# Create a vertical box layout to hold the "Back" button and the scroll view
|
|
content_layout = BoxLayout(orientation='vertical')
|
|
|
|
# Create the "Back" button
|
|
back_button = Button(text='Back', size_hint=(None, None), size=('100dp', '50dp'))
|
|
back_button.bind(on_release=self.go_back)
|
|
|
|
# Add the "Back" button to the content layout
|
|
content_layout.add_widget(back_button)
|
|
|
|
# Add the scroll view to the content layout
|
|
content_layout.add_widget(scroll_view)
|
|
|
|
# Add the content layout to the screen
|
|
self.add_widget(content_layout)
|
|
|
|
def go_back(self, instance):
|
|
app = App.get_running_app()
|
|
app.root.current = 'WattWatchMain'
|
|
|
|
# Define WattWatchMain widget
|
|
class WattWatchMain(Screen):
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
layout = MyBoxLayout()
|
|
self.add_widget(layout)
|
|
|
|
# Define WattWatchApp class
|
|
class WattWatchApp(App):
|
|
title = 'WattWatch'
|
|
icon = 'wwicon.png'
|
|
|
|
def build(self):
|
|
sm = ScreenManager()
|
|
my_screen = WattWatchMain(name='WattWatchMain')
|
|
sm.add_widget(my_screen)
|
|
|
|
settings_screen = SettingsScreen(name='settings')
|
|
sm.add_widget(settings_screen)
|
|
|
|
sm.current = 'WattWatchMain'
|
|
return sm
|
|
|
|
# Run the app
|
|
if __name__ == '__main__':
|
|
WattWatchApp().run() |