Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions blinker.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (blinker *BlinkerState) reinitialize() error {
if blinker.failures > blinker.maxFailures {
log.Fatalf("Unable to initialize blink(1): %v", err)
}
fmt.Fprint(dotOut, "X")
printDot("X")
} else {
blinker.failures = 0
}
Expand All @@ -121,22 +121,22 @@ func (blinker *BlinkerState) setState(state blink1.State) error {
if blinker.failures > 0 {
err := blinker.reinitialize()
if err != nil {
fmt.Fprintf(debugOut, "Reinitialize failed, error %v\n", err)
errorLog("Reinitialize failed, error %v\n", err)
return err
}
}
err := blinker.device.SetState(state)
if err != nil {
fmt.Fprintf(debugOut, "Re-initializing because of error %v\n", err)
errorLog("Re-initializing because of error %v\n", err)
err = blinker.reinitialize()
if err != nil {
fmt.Fprintf(debugOut, "Reinitialize failed, error %v\n", err)
errorLog("Reinitialize failed, error %v\n", err)
return err
}
// Try one more time before giving up for this pass.
err = blinker.device.SetState(state)
if err != nil {
fmt.Fprintf(debugOut, "Setting blinker state failed, error %v\n", err)
errorLog("Setting blinker state failed, error %v\n", err)
}
} else {
blinker.failures = 0
Expand All @@ -158,13 +158,13 @@ func (blinker *BlinkerState) patternRunner() {
select {
case newState := <-blinker.newState:
if newState != currentState || failing {
fmt.Fprintf(debugOut, "Changing from state %v to %v\n", currentState, newState)
debugLog("Changing from state %v to %v\n", currentState, newState)
currentState = newState
if newState.primaryFlash > 0 || newState.secondaryFlash > 0 {
ticker = time.After(time.Millisecond)
} else {
if ticker != nil {
fmt.Fprintf(debugOut, "Killing timer\n")
debugLog("Killing timer\n")
ticker = nil
}
state1 := newState.primary
Expand All @@ -176,11 +176,11 @@ func (blinker *BlinkerState) patternRunner() {
failing = (err1 != nil) || (err2 != nil)
}
} else {
fmt.Fprintf(debugOut, "Retaining state %v unchanged\n", newState)
debugLog("Retaining state %v unchanged\n", newState)
}

case <-ticker:
fmt.Fprintf(debugOut, "Timer fired\n")
verboseLog("Timer fired\n")
state1 := currentState.primary
state2 := currentState.secondary
if stateFlip {
Expand All @@ -207,7 +207,7 @@ func (blinker *BlinkerState) patternRunner() {
// We set state1 on LED 1 and state2 on LED 2. On an original (mk1) blink(1) state2 will be ignored.
state1.LED = blink1.LED1
state2.LED = blink1.LED2
fmt.Fprintf(debugOut, "Setting state (%v and %v)\n", state1, state2)
verboseLog("Setting state (%v and %v)\n", state1, state2)
err1 := blinker.setState(state1)
err2 := blinker.setState(state2)
failing = (err1 != nil) || (err2 != nil)
Expand All @@ -216,7 +216,7 @@ func (blinker *BlinkerState) patternRunner() {
if state1.Duration == 0 {
nextTick = state2.Duration
}
fmt.Fprintf(debugOut, "Next tick: %s\n", nextTick)
verboseLog("Next tick: %s\n", nextTick)
ticker = time.After(nextTick)
}
}
Expand All @@ -232,7 +232,7 @@ func signalHandler(blinker *BlinkerState) {
s := <-interrupt
if s == syscall.SIGQUIT {
fmt.Println("Turning on debug mode.")
debugOut = os.Stdout
debug = debugOn
continue
}
if blinker.failures == 0 {
Expand Down
68 changes: 52 additions & 16 deletions calblink.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"time"
Expand All @@ -27,6 +26,7 @@ import (

// flags
var debugFlag = flag.Bool("debug", false, "Show debug messages")
var verboseFlag = flag.Bool("verbose", false, "Show verbose debug messages (forces --debug to true)")
var clientSecretFlag = flag.String("clientsecret", "client_secret.json", "Path to JSON file containing client secret")
var calNameFlag = flag.String("calendar", "primary", "Name of calendar to base blinker on (overrides value in config file)")
var configFileFlag = flag.String("config", "conf.json", "Path to configuration file")
Expand All @@ -37,8 +37,16 @@ var showDotsFlag = flag.Bool("show_dots", true, "Whether to show progress dots a
var runAsServiceFlag = flag.Bool("runAsService", false, "Whether to run as a service or remain live in the current shell")
var serviceFlag = flag.String("service", "", "Control the system service.")

var debugOut io.Writer = io.Discard
var dotOut io.Writer = io.Discard
type debugLevel int

const (
debugOff debugLevel = iota
debugOn
debugVerbose
)

var debug debugLevel = debugOff
var dots bool = false

// Necessary status for running as a service
type program struct {
Expand All @@ -59,6 +67,30 @@ func setHourMinuteFromTime(t time.Time) time.Time {
return time.Date(now.Year(), now.Month(), now.Day(), t.Hour(), t.Minute(), 0, 0, now.Location())
}

// Log methods

func errorLog(format string, args ...any) {
log.Printf(format, args...)
}

func debugLog(format string, args ...any) {
if debug >= debugOn {
log.Printf(format, args...)
}
}

func verboseLog(format string, args ...any) {
if debug >= debugVerbose {
log.Printf(format, args...)
}
}

func printDot(s string) {
if dots {
fmt.Print(s)
}
}

// Print output methods

func usage() {
Expand All @@ -71,7 +103,11 @@ func main() {
flag.Parse()

if *debugFlag {
debugOut = os.Stdout
debug = debugOn
}

if *verboseFlag {
debug = debugVerbose
}

userPrefs := readUserPrefs()
Expand Down Expand Up @@ -102,7 +138,7 @@ func main() {
})

if userPrefs.ShowDots && !isService {
dotOut = os.Stdout
dots = true
}

prg := &program{
Expand Down Expand Up @@ -155,31 +191,31 @@ func runLoop(p *program) {
if userPrefs.SkipDays[weekday] {
tomorrow := tomorrow()
Black.Execute(blinkerState)
fmt.Fprintf(debugOut, "Sleeping until tomorrow (%v) because it's a skip day\n", tomorrow)
fmt.Fprint(dotOut, "~")
debugLog("Sleeping until tomorrow (%v) because it's a skip day\n", tomorrow)
printDot("~")
nextEvent = tomorrow
continue
}
if userPrefs.StartTime != nil {
start := setHourMinuteFromTime(*userPrefs.StartTime)
fmt.Fprintf(debugOut, "Start time: %v\n", start)
debugLog("Start time: %v\n", start)
if diff := time.Since(start); diff < 0 {
Black.Execute(blinkerState)
fmt.Fprintf(debugOut, "Sleeping %v because start time after now\n", -diff)
fmt.Fprint(dotOut, ">")
debugLog("Sleeping %v because start time after now\n", -diff)
printDot(">")
nextEvent = start
continue
}
}
if userPrefs.EndTime != nil {
end := setHourMinuteFromTime(*userPrefs.EndTime)
fmt.Fprintf(debugOut, "End time: %v\n", end)
debugLog("End time: %v\n", end)
if diff := time.Since(end); diff > 0 {
Black.Execute(blinkerState)
tomorrow := tomorrow()
untilTomorrow := tomorrow.Sub(now)
fmt.Fprintf(debugOut, "Sleeping %v until tomorrow because end time %v before now\n", untilTomorrow, diff)
fmt.Fprint(dotOut, "<")
debugLog("Sleeping %v until tomorrow because end time %v before now\n", untilTomorrow, diff)
printDot("<")
nextEvent = tomorrow
continue
}
Expand All @@ -192,8 +228,8 @@ func runLoop(p *program) {
if failures > failureRetries {
MagentaFlash.Execute(blinkerState)
}
fmt.Fprintf(os.Stderr, "Error receiving events from server:\n%v\n", err)
fmt.Fprint(dotOut, ",")
errorLog("Error receiving events from server:\n%v\n", err)
printDot(",")
nextEvent = now.Add(time.Duration(userPrefs.PollInterval) * time.Second)
continue
} else {
Expand All @@ -202,7 +238,7 @@ func runLoop(p *program) {
blinkState := blinkStateForEvent(next, userPrefs.PriorityFlashSide)

blinkState.Execute(blinkerState)
fmt.Fprint(dotOut, ".")
printDot(".")
nextEvent = now.Add(time.Duration(userPrefs.PollInterval) * time.Second)
}
}
Expand Down
45 changes: 22 additions & 23 deletions calendar.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package main

import (
"fmt"
"log"
"sort"
"strings"
Expand All @@ -33,8 +32,8 @@ func eventHasAcceptableResponse(item *calendar.Event, responseState ResponseStat
return responseState.CheckStatus(attendee.ResponseStatus)
}
}
fmt.Fprintf(debugOut, "No self attendee found for %v\n", item)
fmt.Fprintf(debugOut, "Attendees: %v\n", item.Attendees)
debugLog("No self attendee found for %v\n", item)
debugLog("Attendees: %v\n", item.Attendees)
return true
}

Expand All @@ -44,7 +43,7 @@ func eventExcludedByPrefs(item string, userPrefs *UserPrefs) bool {
}
for _, prefix := range userPrefs.ExcludePrefixes {
if strings.HasPrefix(item, prefix) {
fmt.Fprintf(debugOut, "Skipping event '%v' due to prefix match '%v'\n", item, prefix)
debugLog("Skipping event '%v' due to prefix match '%v'\n", item, prefix)
return true
}
}
Expand All @@ -63,14 +62,14 @@ func nextEvent(items []*calendar.Event, locations []WorkSite, userPrefs *UserPre

for _, prefLocation := range userPrefs.WorkingLocations {
if locationSet[prefLocation] {
fmt.Fprintf(debugOut, "Found matching location: %v\n", prefLocation)
debugLog("Found matching location: %v\n", prefLocation)
match = true
break
}
}

if !match {
fmt.Fprintf(debugOut, "Skipping all events due to no matching locations in %v\n", locations)
debugLog("Skipping all events due to no matching locations in %v\n", locations)
return events
}
}
Expand All @@ -85,7 +84,7 @@ func nextEvent(items []*calendar.Event, locations []WorkSite, userPrefs *UserPre
}
}
}
fmt.Fprintf(debugOut, "nextEvent returning %d events\n", len(events))
debugLog("nextEvent returning %d events\n", len(events))
return events
}

Expand Down Expand Up @@ -125,15 +124,15 @@ func blinkStateForEvent(next []*calendar.Event, priority int) CalendarState {
}
if (priority == 1 && blinkState.primaryFlash == 0 && blinkState.secondaryFlash > 0) ||
(priority == 2 && blinkState.primaryFlash > 0 && blinkState.secondaryFlash == 0) {
fmt.Fprintf(debugOut, "Swapping")
debugLog("Swapping")
blinkState = SwapState(blinkState)
}
}
fmt.Fprintf(debugOut, "Event %v, time %v, delta %v, state %v\n", event.Summary, startTime, delta, blinkState.Name)
debugLog("Event %v, time %v, delta %v, state %v\n", event.Summary, startTime, delta, blinkState.Name)
// Set priority. If priority is set, and the other light is flashing but the priority one isn't, swap them.

} else {
fmt.Println(err)
errorLog("%v\n", err)
break
}
}
Expand All @@ -158,19 +157,19 @@ func fetchEvents(now time.Time, srv *calendar.Service, userPrefs *UserPrefs) ([]
}
for _, event := range events.Items {
if event.EventType == "workingLocation" {
// The calendar event can return three or more working locations:
// The calendar event can return three or more working locations:
// 1. The recurring one for the given day of the week
// 2. The override for that particular day
// 3. Any time overrides that are currently set for specific hours of the day.
//
//
// The logic here isn't complicated enough to manage matching events to
// a location, so instead, gather the latest all-date event and all
// time overrides. Any event that matches one of those will have an
// acceptable location.
isAllDay := (event.Start.DateTime == "")
thisCreated, err := time.Parse(time.RFC3339, event.Created)
if err != nil || (thisCreated.Before(locationCreated) && isAllDay) {
fmt.Fprintf(debugOut, "Skipping location event %v because it's before the current one\n", event.Summary)
debugLog("Skipping location event %v because it's before the current one\n", event.Summary)
continue
}
locationProperties := event.WorkingLocationProperties
Expand All @@ -186,33 +185,33 @@ func fetchEvents(now time.Time, srv *calendar.Service, userPrefs *UserPrefs) ([]
location = WorkSite{SiteType: locationType, Name: locationString}
locationCreated = thisCreated
} else {
fmt.Fprintf(debugOut, "Location Override detected: calendar %v, location %v", calendar, location)
debugLog("Location Override detected: calendar %v, location %v", calendar, location)
locations = append(locations, WorkSite{SiteType: locationType, Name: locationString})
}
fmt.Fprintf(debugOut, "Location detected: calendar %v, location %v\n", calendar, location)
debugLog("Location detected: calendar %v, location %v\n", calendar, location)
} else if event.EventType == "outOfOffice" {
// OOO events don't use an empty start time to indicate an all-day event.
// Instead, check if the start is before our current window and the end
// is after it ends, and if so, skip this entire calendar.
eventStart, err1 := time.Parse(time.RFC3339, event.Start.DateTime)
eventEnd, err2 := time.Parse(time.RFC3339, event.End.DateTime)
if err1 != nil || err2 != nil {
fmt.Fprintf(debugOut, "Skipping event %v because of time parse errors: %v, %v\n", event.Summary, err1, err2)
debugLog("Skipping event %v because of time parse errors: %v, %v\n", event.Summary, err1, err2)
}
if eventStart.Before(now) && eventEnd.After(endTime) {
fmt.Fprintf(debugOut, "Skipping calendar %v due to OOO\n", calendar)
debugLog("Skipping calendar %v due to OOO\n", calendar)
skip = true
break
} else {
fmt.Fprintf(debugOut, "Not applying OOO event %v to calendar %v\n", event, calendar)
debugLog("Not applying OOO event %v to calendar %v\n", event, calendar)
}
}
}
if !skip {
if !locationCreated.IsZero() {
fmt.Fprintf(debugOut, "Adding final location %v\n", location)
debugLog("Adding final location %v\n", location)
locations = append(locations, location)
fmt.Fprintf(debugOut, "Locations: %v\n", locations)
debugLog("Locations: %v\n", locations)
}
allEvents = append(allEvents, events.Items...)
}
Expand All @@ -223,15 +222,15 @@ func fetchEvents(now time.Time, srv *calendar.Service, userPrefs *UserPrefs) ([]
seen := make(map[string]bool)
for _, event := range allEvents {
if seen[event.Id] {
fmt.Fprintf(debugOut, "Skipping duplicate event with ID %v\n", event.Id)
debugLog("Skipping duplicate event with ID %v\n", event.Id)
continue
}
if event.EventType == "workingLocation" || event.EventType == "outOfOffice" {
fmt.Fprintf(debugOut, "Skipping working location/OOO event %v\n", event.Summary)
debugLog("Skipping working location/OOO event %v\n", event.Summary)
continue
}
if event.Start.DateTime == "" {
fmt.Fprintf(debugOut, "Skipping all-day event %v\n", event.Summary)
debugLog("Skipping all-day event %v\n", event.Summary)
continue
}
filtered = append(filtered, event)
Expand Down
Loading