Konubinix' opinionated web of thoughts

More Clever Night Light in Kivy

Fleeting

Following more clever night light, but using my a python runtime on android, so that I can use it when travelling.

import os
from datetime import datetime, timedelta

import math
import time
import requests
from kivy.app import App
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
from kivy.properties import BooleanProperty, NumericProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.spinner import Spinner
from plyer import orientation
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.progressbar import ProgressBar
from kivy.uix.screenmanager import Screen, ScreenManager, ScreenManagerException
from kivy.core.audio import SoundLoader

from helpers.osc import oschandler, to_service
from helpers.wakelock import WakeLock
from helpers.notify import notify
from android.runnable import run_on_ui_thread
from logging import getLogger
from kivy.lang import Builder

logger = getLogger(__name__)

pink = (1, 0.75, 0.8)
orange = (1, 0.5, 0)
cyan = (0, 1, 1)
white = (1, 1, 1)

class SettingsScreen(Screen):
    steps = NumericProperty(4)
    step_time = NumericProperty(20)
    rest_time = NumericProperty(10)
    total_time = StringProperty("Total: 0s")

    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size

    def __init__(self, **kwargs):
        super(SettingsScreen, self).__init__(**kwargs)
        with self.canvas.before:
            self.color = Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size, pos=self.pos)

        self.bind(size=self._update_rect, pos=self._update_rect)

        layout = BoxLayout(orientation='vertical')
        self.add_widget(layout)

        almost_layout = BoxLayout(size_hint=(1, 0.15))
        layout.add_widget(almost_layout)
        almost_layout.add_widget(
            Label(text="Almost", color=(0, 0, 0, 1))
        )
        self.almost_hour = Spinner(
            text="7",
            values=[str(i) for i in range(0, 24)],  # numbers 0 to 100
            size_hint=(None, None),
            size=(200, 44),
            pos_hint={'center_x': .5, 'center_y': .5}
        )
        almost_layout.add_widget(self.almost_hour)
        self.almost_minute = Spinner(
            text='30',
            values=[str(i) for i in range(0, 60)],  # numbers 0 to 100
            size_hint=(None, None),
            size=(200, 44),
            pos_hint={'center_x': .5, 'center_y': .5}
        )
        almost_layout.add_widget(self.almost_minute)

        day_layout = BoxLayout(size_hint=(1, 0.15))
        layout.add_widget(day_layout)
        day_layout.add_widget(
            Label(text="Day", color=(0, 0, 0, 1))
        )
        self.day_hour = Spinner(
            text="8",
            values=[str(i) for i in range(0, 24)],  # numbers 0 to 100
            size_hint=(None, None),
            size=(200, 44),
            pos_hint={'center_x': .5, 'center_y': .5}
        )
        day_layout.add_widget(self.day_hour)
        self.day_minute = Spinner(
            text='0',
            values=[str(i) for i in range(0, 60)],  # numbers 0 to 100
            size_hint=(None, None),
            size=(200, 44),
            pos_hint={'center_x': .5, 'center_y': .5}
        )
        day_layout.add_widget(self.day_minute)

        self.go_button = Button(text="Go !!!",
                           background_color=(0, 0, 1, 1),
                           color=(1, 1, 1, 1),
                           size_hint=(1, 0.15), disabled=True)
        self.go_button.bind(on_press=self.start)
        layout.add_widget(self.go_button)

        @oschandler("service:ready")
        def _(_):
            self.go_button.disabled = False

    def start(self, *_):
        app = App.get_running_app()
        app.goto("nightlight:normal")
        normal = app.sm.get_screen('nightlight:normal')
        normal.almost_hour = int(self.almost_hour.text)
        normal.almost_minute = int(self.almost_minute.text)
        normal.day_hour = int(self.day_hour.text)
        normal.day_minute = int(self.day_minute.text)
        normal.start()

class NormalScreen(Screen):

    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size

    def __init__(self, **kwargs):
        super(NormalScreen, self).__init__(**kwargs)
        self.remaining_time = None
        self.almost_hour = 13
        self.almost_minute=37
        self.day_hour = 13
        self.day_minute=39

        with self.canvas.before:
            self.color = Color(0, 0, 0, 1)
            self.rect = Rectangle(size=self.size, pos=self.pos)

        self.bind(size=self._update_rect, pos=self._update_rect)

        layout = BoxLayout(orientation='vertical')
        self.add_widget(layout)

        current_layout = BoxLayout(size_hint=(1, 0.15))
        layout.add_widget(current_layout)
        self.current_hour = Label(
            text="7",
            font_size="50sp",
        )
        current_layout.add_widget(self.current_hour)
        self.sep = Label(
            text=":",
            font_size="50sp",
        )
        current_layout.add_widget(self.sep)
        self.current_minute = Label(
            text='30',
            font_size="50sp",
        )
        current_layout.add_widget(self.current_minute)

        self.keepon = WakeLock("nightlight:keepon", wakeup=False, partial=True)
        self.wakeup = WakeLock("nightlight:wakeup", wakeup=True, partial=False)
        self.keepon.acquire()
        self.update_display()

        @oschandler("clockwake:done:nightlight:almost")
        def _(_):
            self.almost()

        @oschandler("clockwake:done:nightlight:day")
        def _(_):
            self.day()

        self.bind(on_pre_enter=self.pre_enter_handler)

    def pre_enter_handler(self, _):
        # from plyer import orientation
        # orientation.set_landscape()

        from jnius import autoclass
        from android import mActivity

        View = autoclass('android.view.View')

        option = View.SYSTEM_UI_FLAG_FULLSCREEN
        from android.runnable import run_on_ui_thread

        @run_on_ui_thread
        def fs():
            logger.debug("Entering fullscreen mode")
            mActivity.getWindow().getDecorView().setSystemUiVisibility(option)

        fs()


    def update_display(self, *_):
        now = datetime.now()
        self.current_hour.text = str(now.hour).zfill(2)
        self.current_minute.text = str(now.minute).zfill(2)
        Clock.schedule_once(self.update_display, 61 - now.second)

    def start(self):
        now = datetime.now()
        new_date = now.replace(hour=self.almost_hour, minute=self.almost_minute,second=0)
        if now.hour > self.almost_hour or (now.hour == self.almost_hour and
                                           now.minute > self.almost_minute):
            new_date = new_date + timedelta(days=1)
        duration = (new_date - now).total_seconds()

        to_service("clockwake", {"duration": duration, "callback": "nightlight:almost"})

    def almost(self, *_):
        self.color.rgb = cyan
        self.current_hour.color = (0,0,0, 1)
        self.current_minute.color = (0,0,0, 1)
        self.sep.color = (0,0,0, 1)
        self.wakeup.acquire()
        now = datetime.now()
        new_date = now.replace(hour=self.day_hour, minute=self.day_minute,second=0)
        duration = (new_date - now).total_seconds()

        to_service("clockwake", {"duration": duration, "callback": "nightlight:day"})

    def day(self):
        self.color.rgb = white


class NightLightApp(App):

    def goto(self, screen):
        self.sm.current = screen

    def back(self):
        self.sm.current = 'nightlight:normal'

    @staticmethod
    def populate(sm):
        try:
            sm.get_screen('nightlight:normal')
            return
        except ScreenManagerException:
            pass # not populated yet

        sm.add_widget(SettingsScreen(name='nightlight:settings'))
        sm.add_widget(NormalScreen(name='nightlight:normal'))

    def build(self):
        self.sm = ScreenManager()
        self.populate(self.sm)
        return self.sm


def run():
    NightLightApp().run()