timex_datalink_caldav 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d31450cb602c3c36c121b4ffd2240664ed8f9e550388a246f1c5e169d12f7fa
4
- data.tar.gz: 351b4213184ca2652205c80e99b7997538385bd0fab7e6ee3f4a6a57f1977247
3
+ metadata.gz: e70c9183de68e607682770a4085c86d87ba3cc0d09105c6867b445a702d8808d
4
+ data.tar.gz: 6f4248f1fc332ce55c3c7b2850a8b49ccc42d6456301235225032fb2aeb1d80e
5
5
  SHA512:
6
- metadata.gz: '08bf1aa6454647ccc74a568cb0e54e5b9724394da3f0d8ff6a8bcb0205d63ddb35208b40d99cbdbdab0be8c6b6e5c0c6febdb9db3369c415bad5cfdba7be7cb5'
7
- data.tar.gz: c2334ebf8d3d5515f4bce340808b61cacdc39ccdeaad6d2b7e7e356340fb97cf049c0316d1587d37b74dad9f4a593dcf91be66399bbfc3075018ef023a0e267d
6
+ metadata.gz: 925f98b03e5797567d02b7640febc98338c0fc9dacffa0c5771d501c266c9ee4dc59667752b489db851a1f8fe9cc4d4349fab0094ceab3f8f3de4e339731be30
7
+ data.tar.gz: 7b78a3c9ae837604bff285c6584f1dc84ae32279ce3f99ee0e32d8037f37dd3832451f07aeacca2a5e102d4a2a4016c1b5c6bc570e2aa9d31f04167975a79288
data/Gemfile CHANGED
@@ -20,3 +20,7 @@ gem "icalendar-recurrence", "~> 1.1"
20
20
  gem "activesupport", "~> 7.0"
21
21
 
22
22
  gem "tzinfo", "~> 2.0"
23
+
24
+ gem "yaml", "~> 0.2.1"
25
+
26
+ gem "open-uri", "~> 0.3.0"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- timex_datalink_caldav (0.2.0)
4
+ timex_datalink_caldav (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -21,6 +21,7 @@ GEM
21
21
  nokogiri
22
22
  concurrent-ruby (1.2.2)
23
23
  crc (0.4.2)
24
+ date (3.3.3)
24
25
  diff-lcs (1.5.0)
25
26
  domain_name (0.5.20190701)
26
27
  unf (>= 0.0.5, < 1.0.0)
@@ -51,6 +52,10 @@ GEM
51
52
  minitest (5.18.0)
52
53
  nokogiri (1.14.4-arm64-darwin)
53
54
  racc (~> 1.4)
55
+ open-uri (0.3.0)
56
+ stringio
57
+ time
58
+ uri
54
59
  public_suffix (5.0.1)
55
60
  racc (1.6.2)
56
61
  rake (13.0.6)
@@ -69,6 +74,9 @@ GEM
69
74
  rspec-support (3.12.0)
70
75
  rubyserial (0.6.0)
71
76
  ffi (~> 1.9, >= 1.9.3)
77
+ stringio (3.0.6)
78
+ time (0.2.2)
79
+ date
72
80
  timex_datalink_client (0.11.0)
73
81
  activemodel (~> 7.0.4)
74
82
  crc (~> 0.4.2)
@@ -79,6 +87,8 @@ GEM
79
87
  unf (0.1.4)
80
88
  unf_ext
81
89
  unf_ext (0.0.8.2)
90
+ uri (0.12.1)
91
+ yaml (0.2.1)
82
92
 
83
93
  PLATFORMS
84
94
  arm64-darwin-22
@@ -88,11 +98,13 @@ DEPENDENCIES
88
98
  calendav (~> 0.4.0)
89
99
  icalendar (~> 2.8)
90
100
  icalendar-recurrence (~> 1.1)
101
+ open-uri (~> 0.3.0)
91
102
  rake (~> 13.0)
92
103
  rspec (~> 3.12)
93
104
  timex_datalink_caldav!
94
105
  timex_datalink_client (~> 0.11.0)
95
106
  tzinfo (~> 2.0)
107
+ yaml (~> 0.2.1)
96
108
 
97
109
  BUNDLED WITH
98
110
  2.4.10
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Willy Hardy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # TimexDatalinkCaldav
2
2
 
3
- TimexDatalinkCaldav is a simple Ruby gem designed to sync events from a CalDAV server to a Timex Datalink watch. It can also be used as a standalone command-line interface (CLI) tool.
3
+ TimexDatalinkCaldav is a simple Ruby gem designed to sync events from a CalDAV server or an ical formatted ics file to a Timex Datalink watch. It can also be used as a standalone command-line interface (CLI) tool.
4
+
5
+ ## Pre-requisites
6
+
7
+ If you need to install Ruby, follow the Ruby installation instructions [here](https://www.ruby-lang.org/en/documentation/installation/).
4
8
 
5
9
  ## Installation
6
10
 
@@ -13,9 +17,7 @@ gem install timex_datalink_caldav
13
17
  Or add this line to your application's Gemfile:
14
18
 
15
19
  ```ruby
16
- source "https://rubygems.pkg.github.com/wjhrdy" do
17
- gem "timex_datalink_caldav"
18
- end
20
+ gem "timex_datalink_caldav"
19
21
  ```
20
22
 
21
23
  And then execute:
@@ -33,26 +35,47 @@ Here's an example of how to use the tool in your Ruby code:
33
35
  ```ruby
34
36
  require 'timex_datalink_caldav'
35
37
 
36
- client = TimexDatalinkCaldav::Client.new(your_username, your_password, your_server_uri, your_device)
37
- client.sync_to_watch
38
+ client = TimexDatalinkCaldav::Client.new(your_username, your_password, your_server_uri, your_device, your_protocol_version, days_forward)
39
+
40
+ client.parse_events
41
+ client.write_to_watch
38
42
  ```
39
43
 
40
44
  ### As a CLI Tool
41
45
 
42
- After installing the gem, you can use it as a CLI tool:
46
+ After installing the gem, you can use it as a CLI tool. You can specify the CalDAV server details directly on the command line:
43
47
 
44
48
  ```sh
45
- timex_datalink_caldav -u https://caldavendpoint.com -n your_username -p your_password -d your_device
49
+ timex_datalink_caldav -u https://caldavendpoint.com -n your_username -p your_password -d your_device -a your_protocol_version -f days_forward
46
50
  ```
47
51
 
48
- Please replace `caldavendpoint.com` `your_username`, `your_password`, and `your_device` with your actual CalDAV server, username, password, and serial device respectively.
52
+ Please replace `https://caldavendpoint.com`, `your_username`, `your_password`, `your_device`, `your_protocol_version`, and `days_forward` with your actual CalDAV server URI, username, password, serial device, protocol version, and number of days to look forward for events, respectively.
53
+
54
+ Or you can provide these details in a configuration file:
55
+
56
+ ```sh
57
+ timex_datalink_caldav -c config.yml -a 1 -d /dev/tty.usbmodem0000000000001 -f 7
58
+ ```
59
+
60
+ The configuration file should be a YAML file in the following format:
61
+
62
+ ```yaml
63
+ endpoints:
64
+ - uri: https://www.google.com/calendar/dav/email@gmail.com/events
65
+ user: email@gmail.com
66
+ password: app_password
67
+ - uri: https://caldavendpoint2.com
68
+ user: your_username2
69
+ password: your_password2
70
+ - uri: https://icalendpoint.com/example.ics
71
+ ```
49
72
 
50
- The device is a serial device that flashes an led when it receives data. On Linux, this is usually `/dev/ttyUSB0`. On macOS, this is usually `/dev/cu.usbserial-0001`. On Windows, this is usually `COM1`.
73
+ The device is a serial device that flashes an led when it receives data. On Linux, this is usually `/dev/tty*`. On macOS, this is usually `$(ls /dev/tty.usbmodem* | head -n 1)`. On Windows, this is usually `COM1`.
51
74
 
52
- If you want to use this I highly recommend pairing it with the Raspberry Pi Pico and [this project](https://github.com/famiclone6502/DIY_Datalink_Adapter). It is the cheapest and easiest way to get a serial device that works with the Timex Datalink watch.
75
+ If you want to use this, I highly recommend pairing it with the Raspberry Pi Pico and [this project](https://github.com/famiclone6502/DIY_Datalink_Adapter). It is the cheapest and easiest way to get a serial device that works with the Timex Datalink watch.
53
76
 
54
- ## Note
77
+ ## Notes
55
78
 
56
- Ensure you have the necessary dependencies installed on your system and you have the correct permissions to access the specified device.
79
+ - This gem is not affiliated with Timex, nor is it affiliated with any CalDAV server. It is simply a tool that I wrote to sync my events from my CalDAV server to my Timex Datalink watch.
57
80
 
58
- The tool currently filters down to events that have attendees and converts event times to Eastern Standard Time (EST). Events are sorted by time before syncing to the watch.
81
+ - This gem uses the anniversary feature for full day events, and the appointments feature for events with a start and end time.
@@ -0,0 +1,7 @@
1
+ # Examples
2
+
3
+ This directory contains sample configurations and code for the timex_datalink_caldav gem.
4
+
5
+ ## Configuration
6
+
7
+ `caldav.yaml.sample` is a sample configuration file for CalDAV endpoints. You should rename it to `caldav.yaml` and adjust the URI, username, and password for your endpoints.
@@ -0,0 +1,8 @@
1
+ endpoints:
2
+ - uri: https://example.com/caldav1
3
+ user: user1
4
+ password: password1
5
+ - uri: https://example.com/caldav2
6
+ user: user2
7
+ password: password2
8
+ - uri: https://example.com/example.ical
@@ -1,4 +1,5 @@
1
1
  require 'calendav'
2
+ require 'icalendar'
2
3
  require 'icalendar/recurrence'
3
4
  require 'timex_datalink_client'
4
5
  require 'active_support/time'
@@ -6,11 +7,12 @@ require 'tzinfo'
6
7
 
7
8
  module TimexDatalinkCaldav
8
9
  class Client
9
- def initialize(user, password, server_url, serial_device, protocol_version)
10
+ def initialize(user, password, server_url, serial_device, protocol_version, days_forward = 1)
10
11
  @user = user
11
12
  @password = password
12
13
  @server_url = server_url
13
14
  @serial_device = serial_device
15
+ @days_forward = days_forward
14
16
  @protocol_version = protocol_version.to_i
15
17
  @protocol_class = case @protocol_version
16
18
  when 1 then TimexDatalinkClient::Protocol1
@@ -33,49 +35,73 @@ module TimexDatalinkCaldav
33
35
  authentication: :basic_auth
34
36
  )
35
37
 
36
- # Create a new client with the credentials
37
- client = Calendav.client(credentials)
38
+ if @user && @password
39
+ # Create a new client with the credentials
40
+ client = Calendav.client(credentials)
38
41
 
39
- # Get events from the calendar for the next day
40
- client.events.list(@server_url, from: Time.now, to: Time.now + 24*60*60)
42
+ # Get events from the calendar for the next day
43
+ caldav_events = client.events.list(@server_url, from: Time.now, to: Time.now + @days_forward*24*60*60)
44
+ events = caldav_events.map { |event| Icalendar::Event.parse(event.calendar_data).first }
45
+ else
46
+ cal_file = URI.open(@server_url)
47
+ cals = Icalendar::Calendar.parse(cal_file)
48
+ cal = cals.first
49
+ events = cal.events
50
+ end
51
+
52
+ events
41
53
  end
42
54
 
43
- def sync_to_watch
55
+ def parse_events
44
56
  events = get_events
45
-
57
+
46
58
  appointments = []
59
+ anniversaries = []
47
60
  appointment_map = {} # Used to avoid duplicate appointments
48
-
61
+ anniversary_map = {} # Used to avoid duplicate anniversaries
62
+
49
63
  events.each do |event|
50
- ical_events = Icalendar::Event.parse(event.calendar_data)
51
- if ical_events.any?
52
- ical_event = ical_events.first
53
- if ical_event.attendee&.any? # Exclude events without attendees
54
- next_occurrence = ical_event.occurrences_between(Time.now, Time.now + 24*60*60).first
55
- if next_occurrence
56
- puts get_localzone
57
- est_time = next_occurrence.start_time.in_time_zone(get_localzone)
58
- key = "#{est_time}_#{ical_event.summary.to_s}"
59
- unless appointment_map[key] # Check if the event is already in the map
60
- puts "Adding appointment: #{ical_event.summary.to_s} at time #{est_time}"
61
- appointment = @protocol_class::Eeprom::Appointment.new(
62
- time: est_time,
63
- message: ical_event.summary.to_s
64
- )
65
- appointments << appointment
66
- appointment_map[key] = true
67
- end
64
+ next unless event
65
+
66
+ if event.dtstart && event.dtend && (event.dtend.to_date - event.dtstart.to_date == 1) && event.dtstart.to_datetime.hour == 0 && event.dtstart.to_datetime.min == 0 && event.dtend.to_datetime.hour == 0 && event.dtend.to_datetime.min == 0 # Checking if it is an all day event
67
+ occurrences = event.occurrences_between(Time.now, Time.now + @days_forward*24*60*60)
68
+ occurrences.each do |occurrence|
69
+ est_time = occurrence.start_time.in_time_zone(get_localzone)
70
+ key = "#{est_time}_#{event.summary.to_s}"
71
+ puts key
72
+ unless anniversary_map[key] # Check if the event is already in the map
73
+ puts "Adding anniversary: #{event.summary.to_s} at date #{event.dtstart.to_s}"
74
+ anniversary = @protocol_class::Eeprom::Anniversary.new(
75
+ time: event.dtstart.to_time,
76
+ anniversary: event.summary.to_s
77
+ )
78
+ anniversaries << anniversary
79
+ anniversary_map[key] = true
80
+ end
81
+ end
82
+ elsif event.dtstart && event.dtend
83
+ occurrences = event.occurrences_between(Time.now, Time.now + @days_forward*24*60*60)
84
+ occurrences.each do |occurrence|
85
+ est_time = occurrence.start_time.in_time_zone(get_localzone)
86
+ key = "#{est_time}_#{event.summary.to_s}"
87
+ unless appointment_map[key] # Check if the event is already in the map
88
+ puts "Adding appointment: #{event.summary.to_s} at time #{est_time}"
89
+ appointment = @protocol_class::Eeprom::Appointment.new(
90
+ time: est_time,
91
+ message: event.summary.to_s
92
+ )
93
+ appointments << appointment
94
+ appointment_map[key] = true
68
95
  end
69
96
  end
70
97
  end
71
98
  end
72
99
 
73
- # Sort the appointments by time
74
- appointments.sort_by! { |appointment| appointment.time }
75
- write_to_watch(appointments)
100
+ [appointments, anniversaries]
76
101
  end
77
102
 
78
- def write_to_watch(appointments)
103
+ def write_to_watch(appointments, anniversaries)
104
+ appointments.sort_by! { |appointment| appointment.time }
79
105
  # add 3 because it always seems to be about 3 seconds behind.
80
106
  time1 = Time.now + 3
81
107
  time2 = time1.dup.utc
@@ -128,6 +154,7 @@ module TimexDatalinkCaldav
128
154
  utc_time_name_model,
129
155
  @protocol_class::Eeprom.new(
130
156
  appointments: appointments,
157
+ anniversaries: anniversaries,
131
158
  appointment_notification_minutes: 5
132
159
  ),
133
160
  @protocol_class::End.new
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TimexDatalinkCaldav
4
- VERSION = "0.3.0"
4
+ VERSION = "1.0.0"
5
5
  end
@@ -1,5 +1,6 @@
1
- # frozen_string_literal: true
2
1
  require 'optparse'
2
+ require 'yaml'
3
+ require 'open-uri'
3
4
  require_relative "timex_datalink_caldav/client"
4
5
 
5
6
  module TimexDatalinkCaldav
@@ -9,27 +10,55 @@ module TimexDatalinkCaldav
9
10
  end
10
11
 
11
12
  def execute
12
- client = TimexDatalinkCaldav::Client.new(@options.fetch(:user), @options.fetch(:password), @options.fetch(:uri), @options.fetch(:device), @options.fetch(:api,1))
13
- client.sync_to_watch
13
+ all_appointments = []
14
+ all_anniversaries = []
15
+
16
+ @options[:endpoints].each do |endpoint|
17
+ client = TimexDatalinkCaldav::Client.new(endpoint[:user], endpoint[:password], endpoint[:uri], @options[:device], @options[:api], @options[:days_forward])
18
+ appointments, anniversaries = client.parse_events
19
+ all_appointments.concat(appointments) if appointments.any?
20
+ all_anniversaries.concat(anniversaries) if anniversaries.any?
21
+ end
22
+
23
+ if all_appointments.any? || all_anniversaries.any?
24
+ client = TimexDatalinkCaldav::Client.new(
25
+ @options[:endpoints][0][:user],
26
+ @options[:endpoints][0][:password],
27
+ @options[:endpoints][0][:uri],
28
+ @options[:device],
29
+ @options[:api],
30
+ @options[:days_forward]
31
+ )
32
+ client.write_to_watch(all_appointments, all_anniversaries)
33
+ end
14
34
  end
15
35
 
16
36
  private
17
37
 
18
38
  def parse_options(arguments)
19
- options = {}
39
+ options = { days_forward: 1 }
40
+ cli_endpoint = {}
20
41
  OptionParser.new do |opts|
21
42
  opts.banner = "Usage: timex_datalink_caldav [options]"
22
43
 
44
+ opts.on("-c", "--config FILE", "Configuration file") do |v|
45
+ raise ArgumentError, "Both CLI options and configuration file provided. Please provide only one." if cli_endpoint.any?
46
+ parsed_config = YAML.load_file(v)
47
+ options[:endpoints] = parsed_config['endpoints'].map do |endpoint|
48
+ endpoint.each_with_object({}) { |(k, v), result| result[k.to_sym] = v }
49
+ end
50
+ end
51
+
23
52
  opts.on("-u", "--uri URI", "CalDAV server URI") do |v|
24
- options[:uri] = v
53
+ cli_endpoint[:uri] = v
25
54
  end
26
55
 
27
56
  opts.on("-n", "--user USERNAME", "Username for CalDAV server") do |v|
28
- options[:user] = v
57
+ cli_endpoint[:user] = v
29
58
  end
30
59
 
31
60
  opts.on("-p", "--password PASSWORD", "Password for CalDAV server") do |v|
32
- options[:password] = v
61
+ cli_endpoint[:password] = v
33
62
  end
34
63
 
35
64
  opts.on("-d", "--device DEVICE", "Serial device for Timex Datalink watch") do |v|
@@ -39,8 +68,14 @@ module TimexDatalinkCaldav
39
68
  opts.on("-a", "--api PROTOCOL_VERSION", "Protocol Version") do |v|
40
69
  options[:api] = v
41
70
  end
71
+
72
+ opts.on("-f", "--forward DAYS", Integer, "Number of days to look forward for events") do |v|
73
+ options[:days_forward] = v
74
+ end
42
75
  end.parse!(arguments)
76
+
77
+ options[:endpoints] = [cli_endpoint] if cli_endpoint.any? and options[:endpoints].nil?
43
78
  options
44
79
  end
45
80
  end
46
- end
81
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timex_datalink_caldav
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willy Hardy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-17 00:00:00.000000000 Z
11
+ date: 2023-05-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'Adds a CLI and a feature to pull your next day of calendar events into
14
14
  the Timex Datalink watch. Note: Hardcoded protocol1 and EST timezone. At the moment.'
@@ -22,9 +22,12 @@ files:
22
22
  - CHANGELOG.md
23
23
  - Gemfile
24
24
  - Gemfile.lock
25
+ - LICENSE
25
26
  - README.md
26
27
  - Rakefile
27
28
  - bin/timex_datalink_caldav
29
+ - examples/README.md
30
+ - examples/caldav.yaml.sample
28
31
  - lib/timex_datalink_caldav.rb
29
32
  - lib/timex_datalink_caldav/client.rb
30
33
  - lib/timex_datalink_caldav/version.rb