Skip to content

Commit

Permalink
Add timed stand functionallity
Browse files Browse the repository at this point in the history
  • Loading branch information
meck committed Mar 5, 2021
1 parent d369aba commit b6e79b4
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 14 deletions.
4 changes: 4 additions & 0 deletions Desk Controller.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
7D93E2A425AB244500F37364 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D93E2A325AB244500F37364 /* EventMonitor.swift */; };
7DA946FD25AD32BB000B4816 /* deskcontroller.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 7DA946FC25AD32BB000B4816 /* deskcontroller.sdef */; };
7DA9470025AD3462000B4816 /* MoveDeskCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DA946FF25AD3462000B4816 /* MoveDeskCommand.swift */; };
81293EBE25F24047007D3663 /* AutoStand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81293EBD25F24047007D3663 /* AutoStand.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -51,6 +52,7 @@
7DA946FF25AD3462000B4816 /* MoveDeskCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveDeskCommand.swift; sourceTree = "<group>"; };
7DA9470825AD8F9A000B4816 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
7DA9470A25AD9C93000B4816 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
81293EBD25F24047007D3663 /* AutoStand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoStand.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -122,6 +124,7 @@
7D93E28725AAB38B00F37364 /* AppDelegate.swift */,
7D93E28925AAB38B00F37364 /* ViewController.swift */,
7D81871F25ABE9FF0076DC19 /* BluetoothManager.swift */,
81293EBD25F24047007D3663 /* AutoStand.swift */,
7D0361A725AC42770051FB71 /* Desk control */,
7D93E2A325AB244500F37364 /* EventMonitor.swift */,
7D93E29F25AB203800F37364 /* TouchButton.swift */,
Expand Down Expand Up @@ -247,6 +250,7 @@
7D0361A225AC42610051FB71 /* PreferencesWindowController.swift in Sources */,
7D93E2A025AB203800F37364 /* TouchButton.swift in Sources */,
7D81872325ABF1500076DC19 /* Extensions.swift in Sources */,
81293EBE25F24047007D3663 /* AutoStand.swift in Sources */,
7D03619E25AC39B80051FB71 /* Preferences.swift in Sources */,
7D81872025ABE9FF0076DC19 /* BluetoothManager.swift in Sources */,
7D93E2A425AB244500F37364 /* EventMonitor.swift in Sources */,
Expand Down
55 changes: 55 additions & 0 deletions Desk Controller/AutoStand.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// AutoStand.swift
// Desk Controller
//
// Created by Johan Eklund on 2021-03-05.
//

import Foundation

class AutoStand: NSObject {

private var upTimer: Timer?
private var downTimer: Timer?


func update() {

upTimer?.invalidate()
downTimer?.invalidate()
//print("Invalidated auto timers.")

if Preferences.shared.automaticStandEnabled {
// Stand session always starts top of hour
let now = Date()
let oneHour:TimeInterval = 3600
let nextDown = now.nextHour
var nextUp = Date.init(timeInterval: -Preferences.shared.automaticStandPerHour, since: nextDown)

// Dont schedule in the past
if nextUp < now {
nextUp = now + oneHour
}

upTimer = Timer.init(fire: nextUp, interval: oneHour, repeats: true, block: {_ in

let lastEvent = CGEventSource.secondsSinceLastEventType(CGEventSourceStateID.hidSystemState, eventType: CGEventType(rawValue: ~0)!)

if lastEvent < Preferences.shared.automaticStandInactivity {
DeskController.shared?.moveToPosition(.stand)
}
// print("Fired up timer: \(Date())")
})

downTimer = Timer.init(fire: nextDown, interval: oneHour, repeats: true, block: {_ in
// Always return to sitting, even if inactive
DeskController.shared?.moveToPosition(.sit)
// print("Fired down timer: \(Date())")
})

RunLoop.main.add(upTimer!, forMode: .common)
RunLoop.main.add(downTimer!, forMode: .common)
// print("Scheduled timers:\n\tUp: \(nextUp)\n\tDown: \(nextDown)")
}
}
}
5 changes: 5 additions & 0 deletions Desk Controller/Desk control/DeskController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class DeskController: NSObject {

let desk: DeskPeripheral

let autoStand: AutoStand

let distanceOffset: Float = 0.5 // e.g if we're within this of the distance and it's currently moving then we can probably stop
let minDurationIncrements: TimeInterval = 0.5
var lastMoveTime: Date
Expand All @@ -45,6 +47,7 @@ class DeskController: NSObject {
self.desk = desk
self.lastMoveTime = Date().addingTimeInterval(-minDurationIncrements)
self.previousMovementIncrement = minMovementIncrements
self.autoStand = AutoStand()
super.init()

desk.onPositionChange = { position in
Expand All @@ -53,6 +56,8 @@ class DeskController: NSObject {
}

DeskController.shared = self

autoStand.update()
}

func onPositionChange(_ callback: @escaping (Float) -> Void) {
Expand Down
9 changes: 9 additions & 0 deletions Desk Controller/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,12 @@ extension Float {
}

}

extension Date {
public var nextHour: Date {
let calendar = Calendar.current
let minutes = calendar.component(.minute, from: self)
let components = DateComponents(hour: 1, minute: -minutes)
return calendar.date(byAdding: components, to: self) ?? self
}
}
49 changes: 49 additions & 0 deletions Desk Controller/Preferences/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class Preferences {

private let standingKey = "standingPositionValue"
private let sittingKey = "sittingPositionValue"

private let automaticStandKey = "automaticStandValue"
private let automaticStandInactivityKey = "automaticStandInactivityKey"
private let automaticStandEnabledKey = "automaticStandEnabledKey"

private let offsetKey = "positionOffsetValue"

Expand Down Expand Up @@ -53,6 +57,51 @@ class Preferences {
}
}

var automaticStandPerHour: TimeInterval {
get {
if let standTime = UserDefaults.standard.value(forKey: automaticStandKey) {
return standTime as! TimeInterval
}
return 10 * 60 // Default automatic stand
}

set {
//print("Save new Automatic stand per hour: \(newValue)")
UserDefaults.standard.setValue(newValue, forKey: automaticStandKey)
DeskController.shared?.autoStand.update()
}
}

var automaticStandInactivity: TimeInterval {
get {
if let inactiveTime = UserDefaults.standard.value(forKey: automaticStandInactivityKey) {
return inactiveTime as! TimeInterval
}
return 5 * 60 // Default min
}

set {
//print("Save new Automatic stand inactivity: \(newValue)")
UserDefaults.standard.setValue(newValue, forKey: automaticStandInactivityKey)
}
}

var automaticStandEnabled: Bool {
get {
if let autoStandEnabled = UserDefaults.standard.value(forKey: automaticStandEnabledKey) {
return autoStandEnabled as! Bool
}
return false // Default disabled
}

set {
//print("Saving automatic stand enabled \(newValue)")
UserDefaults.standard.setValue(newValue, forKey: automaticStandEnabledKey)
// Stop timers
DeskController.shared?.autoStand.update()
}
}

var positionOffset: Float {
get {
if let offset = UserDefaults.standard.value(forKey: offsetKey) {
Expand Down
36 changes: 36 additions & 0 deletions Desk Controller/Preferences/PreferencesWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ class PreferencesWindowController: NSWindowController {
@IBOutlet weak var unitsPopUpButton: NSPopUpButton!
@IBOutlet weak var currentHeightField: NSTextField?

@IBOutlet weak var autoStandEnabledCheckbox: NSButton!
@IBOutlet weak var autoStandIntervalStepper: NSStepper!
@IBOutlet weak var autoStandIntervalLabel: NSTextField!
@IBOutlet weak var autoStandInactiveStepper: NSStepper!
@IBOutlet weak var autoStandInactiveLabel: NSTextField!

@IBOutlet weak var openAtLoginCheckbox: NSButton!

static let sharedInstance = PreferencesWindowController(windowNibName: "PreferencesWindowController")
Expand Down Expand Up @@ -44,6 +50,8 @@ class PreferencesWindowController: NSWindowController {

unitsPopUpButton.selectItem(at: Preferences.shared.isMetric ? 0 : 1)

autoStandEnabledCheckbox.state = Preferences.shared.automaticStandEnabled ? .on : .off

updateLabels()

deskController?.onPositionChange({ [weak self] position in
Expand All @@ -69,6 +77,17 @@ class PreferencesWindowController: NSWindowController {
standingHeightField.stringValue = String(format: "%.1f", standingPosition)
sittingHeightField.stringValue = String(format: "%.1f", sittingPosition)

autoStandIntervalLabel.stringValue = String(format: "%.f",
Preferences.shared.automaticStandPerHour / 60)
autoStandInactiveLabel.stringValue = String(format: "%.f",
Preferences.shared.automaticStandInactivity / 60)

let autoEnabled = Preferences.shared.automaticStandEnabled
autoStandInactiveLabel.textColor = autoEnabled ? .labelColor : .disabledControlTextColor
autoStandIntervalLabel.textColor = autoEnabled ? .labelColor : .disabledControlTextColor
autoStandIntervalStepper.isEnabled = autoEnabled
autoStandInactiveStepper.isEnabled = autoEnabled

var offsetPosition = Preferences.shared.positionOffset + (deskPosition ?? 0)
if !Preferences.shared.isMetric {
offsetPosition = offsetPosition.convertToInches()
Expand Down Expand Up @@ -109,6 +128,23 @@ class PreferencesWindowController: NSWindowController {
}
}

@IBAction func toggledAutoStandCheckbox(_ sender: NSButton) {
Preferences.shared.automaticStandEnabled = sender.state == .on
updateLabels()
}

@IBAction func changedAutoStandStepper(_ sender: NSStepper) {
let newInterval = Double(autoStandIntervalStepper.intValue)
Preferences.shared.automaticStandPerHour = newInterval * 60
updateLabels()
}

@IBAction func changedAutoStandInactiveStepper(_ sender: NSStepper) {
let newInactive = Double(autoStandInactiveStepper.intValue)
Preferences.shared.automaticStandInactivity = newInactive * 60
updateLabels()
}

@IBAction func changedUnitsPopUpButton(_ sender: NSPopUpButton) {
Preferences.shared.isMetric = sender.titleOfSelectedItem == "cm"
updateLabels()
Expand Down
Loading

0 comments on commit b6e79b4

Please sign in to comment.