Also, have a look at our:
or chat to us in real time on:
- IRC (#veebprojects on irc.libera.chat)
A physical progress bar based on Google Calendar events
License: GNU General Public License v3.0
Occasionally I get a "False" reading to the working hours/working variable and the display won't show anything. My understanding was that when connected to the google calendar API that the working hours are set by the events on the calendar rather than the set hours in the config or clockin/out times so I'm not understanding how events could be outside working hours in that case. I did make sure the clockin/out times in the config file were definitely before / after any calendar events, and the current time is within the working hours set by the events. I've tried to debug, but I can't quite seem to find the issue. Any ideas would be much appreciated!
I think the latest fix has some issues with indentation :
wdt = machine.WDT(timeout=8000) # enable watchdog timer with a timeout of 8s
while True:
while True:
Also i saw you removed some parameters from the "hourtoindex" function, but not everywhere, i thik that might also be an issue
index = hourtoindex(hour, clockin, clockout)
Hi there. Fist of all i like your project it looks great! I've already replicated part of it:)
But for the part of the integration with Google Calendar API i'm having some issues.
Now that the code has migrated from OAUTH and a local server to directly accessing the Google Calendar API i can't get it working because in My Google Calendar API / Credentials section i only have two options: OAuth 2.0 Client IDs and Service Accounts.
Is this a coutry based limitation, or maybe i'm not looking in the right section of the configuration?
thanks!
The events are not beeing displayed on the bar.
I think the calendar is correctly integrated because the start and end times are adopted.
I don't know if it is an mistake on my end or in the code. Please let me know if you have any idea how to ficsit.
config.py:
CALENDAR = "###############################################@group.calendar.google.com"
APIKEY = "##############################"
TIMEZONE = "Europe/Berlin"
# Lighting
PIXELS = 240
GPIOPIN = 15
BARCOL = (0, 20, 0)
EVENTCOL = (0, 20, 20) # list of tuples used as meeting colours
FLIP = False # Flip display (set to True if the strip runs from right to left)
GOOGLECALBOOL = True # Boolean for whether to check google calendar page
IGNORE_HARDCODED = True # Set to True if you want Clock in at the start of first meeting and Clockout at end of last meeting
SCHEDULE = { # This doesn't get used if IGNORE_HARDCODED is True. Othewise, it's the working hours for the week
"monday": [
{
"clockin": "0",
"clockout": "0"
}
],
"tuesday": [
{
"clockin": "0",
"clockout": "0"
}
],
"wednesday": [
{
"clockin": "0",
"clockout": "0"
}
],
"thursday": [
{
"clockin": "0",
"clockout": "0"
}
],
"friday": [
{
"clockin": "0",
"clockout": "0"
}
],
"saturday": [
{
"clockin": "0",
"clockout": "0"
}
],
"sunday": [
{
"clockin": "0",
"clockout": "0"
}
]
}
main.py:
"""
Progress Bar. Takes a pico W and a light strip to make a physical progress bar.
PoC, fok and make it better
Copyright (C) 2023 Veeb Projects https://veeb.ch
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 <https://www.gnu.org/licenses/>
"""
from phew import access_point, connect_to_wifi, is_connected_to_wifi, dns, server
from phew.template import render_template
import machine
import _thread
import utime
import time
import network
import config
import urequests
import neopixel
import math
import os
import json
# Time with daylight savings time and time zone factored in, edit this to fit where you are
worldtimeurl = "https://timeapi.io/api/TimeZone/zone?timezone=" + config.TIMEZONE
# The ID of the public Google Calendar to be used
calendar = config.CALENDAR
# The API key for google... Not sure why it is needed, but it seems to be
api_key = config.APIKEY
n = config.PIXELS # Number of pixels on strip
p = config.GPIOPIN # GPIO pin that data line of lights is connected to
barcolor = config.BARCOL # RGB for bar color
eventcollist = config.EVENTCOL# RGB for event color
schedule = config.SCHEDULE # Working hours in config file (only used if google calendar not used)
flip = config.FLIP
googlecalbool = config.GOOGLECALBOOL
led = machine.Pin("LED", machine.Pin.OUT)
led.off()
led.on()
time.sleep(1)
eventbool = False # Initialising, no need to edit
checkevery = 10 # Number of cycles before refreshing schedule/clocking times
AP_NAME = "pi pico"
AP_DOMAIN = "pipico.net"
AP_TEMPLATE_PATH = "ap_templates"
WIFI_FILE = "wifi.json"
def machine_reset():
utime.sleep(1)
print("Resetting...")
machine.reset()
def get_today_appointment_times(calendar_id, api_key, tz):
# Get the date from the RTC
rtc = machine.RTC()
year, month, day, _, hour, minute, _, _ = rtc.datetime()
# Format the date
date = "{:04d}-{:02d}-{:02d}".format(year, month, day)
# Format the request URL
url = f"https://www.googleapis.com/calendar/v3/calendars/{calendar_id}/events"
url += f"?timeMin={date}T00:00:00Z&timeMax={date}T23:59:59Z&timeZone={tz}&key={api_key}"
# Send the request
response = urequests.get(url)
data = response.json()
# Extract the appointment times
appointment_times = []
for item in data.get("items", []):
if item["status"] == "cancelled":
continue
start = item["start"].get("dateTime", item["start"].get("date"))
appointment_times.append(start)
start = item["end"].get("dateTime", item["end"].get("date"))
appointment_times.append(start)
print(appointment_times)
return appointment_times
def whatday(weekday):
dayindex = int(weekday)
nameofday = [
'monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
'sunday']
day = nameofday[dayindex]
return day
def set_time(worldtimeurl):
print('Grab time:',worldtimeurl)
response = urequests.get(worldtimeurl)
# parse JSON
parsed = response.json()
datetime_str = str(parsed["currentLocalTime"])
year = int(datetime_str[0:4])
month = int(datetime_str[5:7])
day = int(datetime_str[8:10])
hour = int(datetime_str[11:13])
minute = int(datetime_str[14:16])
second = int(datetime_str[17:19])
offset = int(parsed["currentUtcOffset"]["seconds"])/3600
# update internal RTC
machine.RTC().datetime((year,
month,
day,
0,
hour,
minute,
second,
0))
dow = time.localtime()[6]
return dow,offset
def bar(np, upto, clockin, clockout):
barupto = hourtoindex(upto, clockin, clockout)
for i in range(barupto):
np[i] = barcolor
def flipit(np,n):
temp=[0]*n
for i in range(n):
temp[i]=np[i]
for i in range(n):
np[i]=temp[n-1-i]
return np
def timetohour(time_string):
# Extract the time portion from the string
if time_string.count('-') == 2:
time_part = time_string.split("T")[1].split("+")[0]
else:
time_part = time_string.split("T")[1].split("-")[0]
# Split the time into hours, minutes, and seconds
hours, minutes, seconds = time_part.split(":")
parsed_time = int(hours)+int(minutes)/60+int(seconds)/3600
return parsed_time
def addevents(np, response, clockin, clockout):
indexes = []
for x in response:
hour = timetohour(x)
index = hourtoindex(hour, clockin, clockout)
if valid(index):
indexes.append(index)
#pop out pairs of values and paint in meetings
try:
index = 0
while True:
end = indexes.pop()
start= indexes.pop()
for i in range(start,end):
if valid(i):
np[i] = eventcollist[index % len(eventcollist)]
index = (index + 1)
except:
pass
def valid(index):
valid = False
if index <= n and index >= 0:
valid = True
return valid
def off(np):
print('Turn off all LEDs')
for i in range(n):
np[i] = (0, 0, 0)
np.write()
def hourtoindex(hoursin, clockin, clockout):
index = int(math.floor(n*(hoursin - clockin)/(clockout-clockin)))
if index < 0 or index > n:
index = -1
return index
def eventnow(hoursin, response):
event = False
for x in response:
hour = timetohour(x)
if abs(hour - hoursin) < 30/3600:
event = True
return event
def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
r = g = b = 0
elif pos < 85:
r = int(pos * 3)
g = int(255 - pos * 3)
b = 0
elif pos < 170:
pos -= 85
r = int(255 - pos * 3)
g = 0
b = int(pos * 3)
else:
pos -= 170
r = 0
g = int(pos * 3)
b = int(255 - pos * 3)
return (r, g, b)
def rainbow_cycle(np):
print ('Rainbow!')
for j in range(255):
for i in range(n):
pixel_index = (i * 256 // n) + j
np[i] = wheel(pixel_index & 255)
np.write()
def atwork(clockin, clockout, time):
work = False
if (time >= clockin) & (time <clockout):
work = True
return work
def breathe(np, seconds):
n = 0
index = 0
sleeptime = .05
breathespeed = .1
cycles = seconds/sleeptime
while index < cycles:
val = int((255/2)*(1+math.sin(n)))
for j in range(144):
np[j]=(val, val , val) # Set LED to a converted HSV value
np.write()
n = (n + breathespeed ) % 360
time.sleep(sleeptime)
index = index + 1
def sorted_appointments(array):
# This is just a placeholder for when/if the google api sends garbled times
array=sorted(array)
return array
def application_mode():
global clockin, clockout
print("Entering application mode.")
count = 1
# When you plug in, update rather than wait until the stroke of the next minute
print("Connected to WiFi")
np = neopixel.NeoPixel(machine.Pin(p), n)
rainbow_cycle(np)
time.sleep(1)
off(np)
led.off()
dow, offset = set_time(worldtimeurl)
checkindex = 0
appointment_times = []
print('Begin endless loop')
while True:
try:
# wipe led clean before adding stuff
for i in range(n):
np[i] = (0, 0, 0)
eventbool = False
checkindex = checkindex + 1
now = time.gmtime()
hoursin = float(now[3])+float(now[4])/60 + float(now[5])/3600 # hours into the day
dayname = whatday(int(now[6]))
if checkindex == 1:
clockin = float(schedule[dayname][0]['clockin'])
clockout = float(schedule[dayname][0]['clockout'])
if googlecalbool is True: # overwrite clockin/clockout times if Google Calendar is to be used
appointment_times = []
clockin = 0
clockout = 0
eventbool = False
print('Updating from Google Calendar')
try:
appointment_times = get_today_appointment_times(calendar, api_key, config.TIMEZONE)
appointment_times = sorted_appointments(appointment_times)
print(appointment_times)
eventbool = eventnow(hoursin, appointment_times[::2]) # only the even elements (starttimes)
if config.IGNORE_HARDCODED is True:
clockin = timetohour(appointment_times[0])
clockout = timetohour(appointment_times[len(appointment_times)-1])
except:
print('Scheduling issues')
working = atwork(clockin, clockout, hoursin)
print(working, clockin, clockout, hoursin)
if working is True:
print('Pour yourself a cup of ambition')
# Draw the events
addevents(np, appointment_times, clockin, clockout)
# Draw the bar
bar(np, hoursin, clockin, clockout)
if eventbool is True:
# If an event is starting, breathe LEDs
breathe(np, 30)
else:
# Toggle the end led of the bar
count = (count + 1) % 2
# The value used to toggle lights
ledindex = min(hourtoindex(hoursin, clockin, clockout), n)
np[ledindex] = tuple(z*count for z in barcolor)
# Just the tip of the bar
if abs(hoursin - clockout) < 10/3600: # If we're within 10 seconds of clockout reset
machine.reset()
if flip == True:
np = flipit(np,n)
print('Flipped')
# reset the google check index if needed
if (checkindex > checkevery):
checkindex = 0
np.write()
time.sleep(1)
except Exception as e:
print('Exception:',e)
off(np)
time.sleep(1)
machine.reset()
except KeyboardInterrupt:
off(np)
def setup_mode():
print("Entering setup mode...")
def ap_index(request):
if request.headers.get("host") != AP_DOMAIN:
return render_template(f"{AP_TEMPLATE_PATH}/redirect.html", domain=AP_DOMAIN)
return render_template(f"{AP_TEMPLATE_PATH}/index.html")
def ap_configure(request):
print("Saving wifi credentials...")
with open(WIFI_FILE, "w") as f:
json.dump(request.form, f)
f.close()
# Reboot from new thread after we have responded to the user.
_thread.start_new_thread(machine_reset, ())
return render_template(f"{AP_TEMPLATE_PATH}/configured.html", ssid=request.form["ssid"])
def ap_catch_all(request):
if request.headers.get("host") != AP_DOMAIN:
return render_template(f"{AP_TEMPLATE_PATH}/redirect.html", domain=AP_DOMAIN)
return "Not found.", 404
server.add_route("/", handler=ap_index, methods=["GET"])
server.add_route("/configure", handler=ap_configure, methods=["POST"])
server.set_callback(ap_catch_all)
ap = access_point(AP_NAME)
ip = ap.ifconfig()[0]
dns.run_catchall(ip)
server.run()
# Main Logic
# Figure out which mode to start up in...
try:
os.stat(WIFI_FILE)
# File was found, attempt to connect to wifi...
with open(WIFI_FILE) as f:
wifi_credentials = json.load(f)
ip_address = connect_to_wifi(wifi_credentials["ssid"], wifi_credentials["password"])
if not is_connected_to_wifi():
# Bad configuration, delete the credentials file, reboot
# into setup mode to get new credentials from the user.
print("Bad wifi connection!")
print(wifi_credentials)
os.remove(WIFI_FILE)
machine_reset()
print(f"Connected to wifi, IP address {ip_address}")
application_mode() # Contains all the progress bar code
except Exception:
# Either no wifi configuration file found, or something went wrong,
# so go into setup mode.
setup_mode()
Is it possible to make the day longer for a spesific time in the calendar, because school doesnt end always at 13:00
I want to to an improvement with setting up WIFI credentials
If a pushbutton is pressed the board could enter in AP mode and offer a simple page where WIFI credentials are set, then getback on normal mode
What do you think about it? I could try writing the code :)
Awesome project!
One problem I noted was that on line 115 in timetohour(time_string):
# Extract the time portion from the string time_part = time_string.split("T")[1].split("+")[0]
gives an error because my time zone is - not + from Z:
Connected to WiFi Turn off all LEDs Grab time: https://timeapi.io/api/TimeZone/zone?timeZone=America/Vancouver (2023, 5, 16, 21, 43, 13, 1, 136) Begin endless loop https://www.googleapis.com/calendar/v3/calendars/mycalendarurl%40group.calendar.google.com/events?timeMin=2023-05-16T00:00:00-07:00&timeMax=2023-05-16T23:59:59-07:00&key=MyKeyGoesHere ['2023-05-16T22:00:00-07:00', '2023-05-16T22:45:00-07:00', '2023-05-16T21:15:00-07:00', '2023-05-16T22:00:00-07:00'] too many values to unpack (expected 3) Turn off all LEDs
So I changed the "+" to a "-" in the time_part calculation, and it no longer errored-out. I realize Micropython doesn't have the full datetime, and your elegant workaround might need some tweaking or documenting.
One other thing is that the get_today_appointment_times function (line 45) uses Z rather than local times, so my -07:00 from Z would often omit events and offset the ones that still fell within the workday window:
url = f"https://www.googleapis.com/calendar/v3/calendars/{calendar_id}/events" url += f"?timeMin={date}T00:00:00Z&timeMax={date}T23:59:59Z&key={api_key}"
Again, with no datetime, it makes dealing with time zones a pain. My workaround was to change the Z to my Z offset:
url = f"https://www.googleapis.com/calendar/v3/calendars/{calendar_id}/events" url += f"?timeMin={date}T00:00:00-07:00&timeMax={date}T23:59:59-07:00&key={api_key}"
But that's cheating!
Thanks for posting such great ideas - my 9-year-old is building sploosh for her school's hydroponics lab.
I've created the following tutorial, if you like you can include it on your page:
https://medium.com/@ionescu.dan84/workday-progressbar-with-google-calendar-integration-b266aabd32a8
So if i understand correct for the Google Calendar intergration to work one needs to have:
Have i understood correct? I am asking because i am want to write an instructable (tutoral) for the project
Hello! I REALLY want to make this project work, but I am struggling a bit with the google calendar. I was able to do the OAuth credentials, I have the GCal API Key, I see that it functions without errors once its pulled with my code, but the light isn't showing the progress bar/events. The WIFI connection is good, the lights/connections are fine as they show the rainbow initially, but then once it pulls events (which it prints), the light goes dark. I checked the time zone and that is ok as well. Calendar is public. It just prints the following:
pdating from Google Calendar https://www.googleapis.com/calendar/v3/calendars/[email protected]/events?timeMin=2023-06-03T00:00:00Z&timeMax=2023-06-03T23:59:59Z&timeZone=America/Los_Angeles&key=*** ['2023-06-03T12:00:00-07:00', '2023-06-03T12:30:00-07:00', '2023-06-03T13:00:00-07:00', '2023-06-03T14:00:00-07:00', '2023-06-03T14:30:00-07:00', '2023-06-03T15:30:00-07:00', '2023-06-03T16:00:00-07:00', '2023-06-03T17:00:00-07:00'] Scheduling issues appointment_times: ['2023-06-03T12:00:00-07:00', '2023-06-03T12:30:00-07:00', '2023-06-03T13:00:00-07:00', '2023-06-03T14:00:00-07:00', '2023-06-03T14:30:00-07:00', '2023-06-03T15:30:00-07:00', '2023-06-03T16:00:00-07:00', '2023-06-03T17:00:00-07:00'] Scheduling issues appointment_times: [] Scheduling issues appointment_times: [] Scheduling issues appointment_times: [] Scheduling issues
Any help would be much appreciated!
Can I use a 10 meter led strip? Or is it limited length?
I have tested the bar with Flip = True in config.py and it does not work
Logs:
MicroPython v1.20.0 on 2023-04-26; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
%Run -c $EDITOR_CONTENT
2023-06-30 08:34:58 [debug / 153kB] - got ip address
Connected to wifi, IP address 192.168.1.110
Entering application mode.
Rainbow!
Connected to WiFi
Turn off all LEDs
Grab time: https://timeapi.io/api/TimeZone/zone?timezone=Europe/Bucharest
Begin endless loop
Updating from Google Calendar
['2023-06-30T08:00:00+03:00', '2023-06-30T08:15:00+03:00', '2023-06-30T19:00:00+03:00', '2023-06-30T19:15:00+03:00']
['2023-06-30T08:00:00+03:00', '2023-06-30T08:15:00+03:00', '2023-06-30T19:00:00+03:00', '2023-06-30T19:15:00+03:00']
clockin: 8.0
Pour yourself a cup of ambition
Exception: bytearray index out of range
Turn off all LEDs
Connection lost (device reports readiness to read but returned no data (device disconnected or multiple access on port?))
Use Stop/Restart to reconnect.
Logs when its working (but with flip=False):
MicroPython v1.20.0 on 2023-04-26; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
%Run -c $EDITOR_CONTENT
2023-06-28 11:38:47 [debug / 150kB] - got ip address
Connected to wifi, IP address 192.168.1.110
Entering application mode.
Rainbow!
Connected to WiFi
Turn off all LEDs
Grab time: https://timeapi.io/api/TimeZone/zone?timezone=Europe/Bucharest
Begin endless loop
Updating from Google Calendar
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00']
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
Updating from Google Calendar
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00']
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
['2023-06-19T08:30:00+03:00', '2023-06-19T09:30:00+03:00', '2023-06-19T21:30:00+03:00', '2023-06-19T21:35:00+03:00', '2023-06-27T21:30:00+03:00', '2023-06-27T21:45:00+03:00', '2023-06-28T08:30:00+03:00', '2023-06-28T09:00:00+03:00']
clockin: 8.5
When you use reocurring events, the eventbool doesnt get set to true, when clockout should trigger, the pico doesnt reboot and the LEDs dont turn off.
I think I have a good idea but I don't know how to implement it. When an Event is active, you cannot see the progress of the Event.
In my case I chose to set events in the breaks and not the Lessons.
Here is my idea:
We make a progress bar for the whole day in (0, 0, 10) and a second progress bar for the Event in (0, 10, 0). So when a Event is active, the two bars are layerd over each other. When a Pixel is used by both, it should be (0, 10, 10).
When this mode is enabled, events should not be marked on the bar, when none is active.
I hope you like this idea, just implement it when you want, no need to do this soon.
If you don't like it, close this issue.
What do you think of a simple GUI for setting parameters like 9-5 intervals each day?
One way it could work would be:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.