timex_datalink_client 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10adb86fd981a90fa2e764e3dd63bbaf0b1ffa1c37b5210d510ea6aa29b76edb
4
- data.tar.gz: 2d2b67b5b10a8a6bd7bbefd4423e9e7b661a4824649e241df0e4e3ca45e17290
3
+ metadata.gz: 6b3a3f3ffde31a9f937f60f6ebba23851aeacc62d49b5677f7769f7392a9a1ea
4
+ data.tar.gz: 5a9f3acd574998d9bf430225459bb7f98f2954b5e1f6fff038a240145c5ffef5
5
5
  SHA512:
6
- metadata.gz: 3a2fca239d0220cc056a7577ec7b17d1e4e9cf9f2c706af832b38ec0d0eb5522ae4df970369fa7be882eea686fa0da0bb0f063d86f8ce3736ba24c9c0f1acca2
7
- data.tar.gz: 4c8296686c59d03b8da2970cda23c8c525f2376a1d8bca2094c3b44955c37d59625d9cf8e9a8c9545a600421514e584a32e53d52fa537f40af0a035041f140b6
6
+ metadata.gz: 93da0a4e3153cbeb4feaae86574a25034e315d82182833f5b7822796d00cc6d4dbc8f57830bde9e906b6e21471a6829808f0e621b432937314cfefb2090d52f7
7
+ data.tar.gz: b4c516c8b2db51e393b60324ed0d723f00562f34823a6e43c62b07c276b6bfae7edaa9c4f8ab338af6b2fe395afed38729f4e072381d977baef6371c08370802
@@ -4,6 +4,7 @@ class TimexDatalinkClient
4
4
  class Helpers
5
5
  module CharEncoders
6
6
  CHARS = "0123456789abcdefghijklmnopqrstuvwxyz !\"#$%&'()*+,-./:\\;=@?_|<>[]"
7
+ CHARS_PROTOCOL_6 = "0123456789 abcdefghijklmnopqrstuvwxyz!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
7
8
  EEPROM_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz !\"#$%&'()*+,-./:\\;=@?_|<>["
8
9
  PHONE_CHARS = "0123456789cfhpw "
9
10
  INVALID_CHAR = " "
@@ -19,6 +20,10 @@ class TimexDatalinkClient
19
20
  end
20
21
  end
21
22
 
23
+ def protocol_6_chars_for(string_chars, length: nil, pad: false)
24
+ chars_for(string_chars, char_map: CHARS_PROTOCOL_6, length:, pad:)
25
+ end
26
+
22
27
  def eeprom_chars_for(string_chars, length: 31)
23
28
  chars = chars_for(string_chars, char_map: EEPROM_CHARS, length:).append(EEPROM_TERMINATOR)
24
29
 
@@ -24,7 +24,7 @@ class TimexDatalinkClient
24
24
  # Create a TimeName instance.
25
25
  #
26
26
  # @param zone [Integer] Time zone number (1 or 2).
27
- # @param name [String] Name of time zone (3 chars max)
27
+ # @param name [String] Name of time zone (3 chars max).
28
28
  # @return [TimeName] TimeName instance.
29
29
  def initialize(zone:, name:)
30
30
  @zone = zone
@@ -49,8 +49,12 @@ class TimexDatalinkClient
49
49
 
50
50
  private
51
51
 
52
+ def name_formatted
53
+ name || "tz#{zone}"
54
+ end
55
+
52
56
  def name_characters
53
- chars_for(name, length: 3, pad: true)
57
+ chars_for(name_formatted, length: 3, pad: true)
54
58
  end
55
59
  end
56
60
  end
@@ -33,7 +33,7 @@ class TimexDatalinkClient
33
33
  message: "%{value} is invalid! Valid date formats are #{DATE_FORMAT_MAP.keys}."
34
34
  }
35
35
 
36
- attr_accessor :zone, :is_24h, :date_format, :time
36
+ attr_accessor :zone, :is_24h, :date_format, :time, :name
37
37
 
38
38
  # Create a Time instance.
39
39
  #
@@ -42,7 +42,7 @@ class TimexDatalinkClient
42
42
  # @param date_format ["%_m-%d-%y", "%_d-%m-%y", "%y-%m-%d", "%_m.%d.%y", "%_d.%m.%y", "%y.%m.%d"] Date format
43
43
  # (represented by Time#strftime format).
44
44
  # @param time [::Time] Time to set (including time zone).
45
- # @param name [String, nil] Name of time zone (defaults to zone from time; 3 chars max)
45
+ # @param name [String, nil] Name of time zone (defaults to zone from time; 3 chars max).
46
46
  # @return [Time] Time instance.
47
47
  def initialize(zone:, is_24h:, date_format:, time:, name: nil)
48
48
  @zone = zone
@@ -79,12 +79,12 @@ class TimexDatalinkClient
79
79
 
80
80
  private
81
81
 
82
- def name
83
- @name || time.zone.downcase
82
+ def name_formatted
83
+ name || time.zone || "tz#{zone}"
84
84
  end
85
85
 
86
86
  def name_characters
87
- chars_for(name, length: 3, pad: true)
87
+ chars_for(name_formatted, length: 3, pad: true)
88
88
  end
89
89
 
90
90
  def year_mod_1900
@@ -33,7 +33,7 @@ class TimexDatalinkClient
33
33
  message: "%{value} is invalid! Valid date formats are #{DATE_FORMAT_MAP.keys}."
34
34
  }
35
35
 
36
- attr_accessor :zone, :is_24h, :date_format, :time
36
+ attr_accessor :zone, :is_24h, :date_format, :time, :name
37
37
 
38
38
  # Create a Time instance.
39
39
  #
@@ -42,7 +42,7 @@ class TimexDatalinkClient
42
42
  # @param date_format ["%_m-%d-%y", "%_d-%m-%y", "%y-%m-%d", "%_m.%d.%y", "%_d.%m.%y", "%y.%m.%d"] Date format
43
43
  # (represented by Time#strftime format).
44
44
  # @param time [::Time] Time to set (including time zone).
45
- # @param name [String, nil] Name of time zone (defaults to zone from time; 3 chars max)
45
+ # @param name [String, nil] Name of time zone (defaults to zone from time; 3 chars max).
46
46
  # @return [Time] Time instance.
47
47
  def initialize(zone:, is_24h:, date_format:, time:, name: nil)
48
48
  @zone = zone
@@ -79,12 +79,12 @@ class TimexDatalinkClient
79
79
 
80
80
  private
81
81
 
82
- def name
83
- @name || time.zone.downcase
82
+ def name_formatted
83
+ name || time.zone || "tz#{zone}"
84
84
  end
85
85
 
86
86
  def name_characters
87
- chars_for(name, length: 3, pad: true)
87
+ chars_for(name_formatted, length: 3, pad: true)
88
88
  end
89
89
 
90
90
  def year_mod_1900
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ require "timex_datalink_client/helpers/char_encoders"
6
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
7
+
8
+ class TimexDatalinkClient
9
+ class Protocol6
10
+ class Alarm
11
+ include ActiveModel::Validations
12
+ include Helpers::CharEncoders
13
+ prepend Helpers::CrcPacketsWrapper
14
+
15
+ CPACKET_ALARM = 0x51
16
+
17
+ VALID_DAYS_IN_MONTH = {
18
+ 1 => 1..31,
19
+ 2 => 1..29,
20
+ 3 => 1..31,
21
+ 4 => 1..30,
22
+ 5 => 1..31,
23
+ 6 => 1..30,
24
+ 7 => 1..31,
25
+ 8 => 1..31,
26
+ 9 => 1..30,
27
+ 10 => 1..31,
28
+ 11 => 1..30,
29
+ 12 => 1..31
30
+ }.freeze
31
+
32
+ ALARM_STATUS_MAP = {
33
+ disarmed: 0,
34
+ armed: 1,
35
+ unused: 2
36
+ }.freeze
37
+
38
+ DEFAULT_TIME = Time.new(0, 1, 1, 6, 0)
39
+
40
+ attr_accessor :number, :status, :time, :message, :month, :day
41
+
42
+ validates :number, inclusion: {
43
+ in: 1..8,
44
+ message: "value %{value} is invalid! Valid number values are 1..8."
45
+ }
46
+
47
+ validates :status, inclusion: {
48
+ in: ALARM_STATUS_MAP.keys,
49
+ message: "%{value} is invalid! Valid date formats are #{ALARM_STATUS_MAP.keys}."
50
+ }
51
+
52
+ validates :month, inclusion: {
53
+ in: 1..12,
54
+ allow_nil: true,
55
+ message: "%{value} is invalid! Valid months are 1..12 and nil."
56
+ }
57
+
58
+ validates :day, inclusion: {
59
+ if: ->(alarm) { alarm.month.nil? },
60
+ in: 1..31,
61
+ allow_nil: true,
62
+ message: "%{value} is invalid! Valid days are 1..31 and nil."
63
+ }
64
+
65
+ validates :day, inclusion: {
66
+ if: ->(alarm) { alarm.day && alarm.month },
67
+ in: ->(alarm) { VALID_DAYS_IN_MONTH[alarm.month] },
68
+ message: ->(alarm, _attributes) do
69
+ "#{alarm.day} is invalid for month #{alarm.month}! " \
70
+ "Valid days are #{VALID_DAYS_IN_MONTH[alarm.month]} and nil when month is #{alarm.month}."
71
+ end
72
+ }
73
+
74
+ # Create an Alarm instance.
75
+ #
76
+ # @param number [Integer] Alarm number (from 1 to 8).
77
+ # @param status [Symbol] Alarm status (:armed, :disarmed, or :unused).
78
+ # @param time [::Time] Time of alarm.
79
+ # @param message [String] Alarm message text.
80
+ # @param month [Integer, nil] Month of alarm.
81
+ # @param day [Integer, nil] Day of alarm.
82
+ # @return [Alarm] Alarm instance.
83
+ def initialize(number:, status:, time: DEFAULT_TIME, message: "", month: nil, day: nil)
84
+ @number = number
85
+ @status = status
86
+ @time = time
87
+ @message = message
88
+ @month = month
89
+ @day = day
90
+ end
91
+
92
+ # Compile packets for an alarm.
93
+ #
94
+ # @raise [ActiveModel::ValidationError] One or more model values are invalid.
95
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
96
+ def packets
97
+ validate!
98
+
99
+ [
100
+ [
101
+ CPACKET_ALARM,
102
+ number,
103
+ time.hour,
104
+ time.min,
105
+ month.to_i,
106
+ day.to_i,
107
+ status_formatted,
108
+ message_characters,
109
+ ].flatten
110
+ ]
111
+ end
112
+
113
+ private
114
+
115
+ def message_characters
116
+ protocol_6_chars_for(message, length: 16, pad: true)
117
+ end
118
+
119
+ def status_formatted
120
+ ALARM_STATUS_MAP[status]
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timex_datalink_client/helpers/char_encoders"
4
+ require "timex_datalink_client/helpers/length_packet_wrapper"
5
+
6
+ class TimexDatalinkClient
7
+ class Protocol6
8
+ class Eeprom
9
+ class PhoneNumber
10
+ include Helpers::CharEncoders
11
+ prepend Helpers::LengthPacketWrapper
12
+
13
+ PHONE_DIGITS = 12
14
+ NAME_MAX_LENGTH = 31
15
+
16
+ NUMBER_NAME_DELIMITER = "-"
17
+ NUMBER_NAME_MAX_NUMBER_LENGTH = 30
18
+
19
+ TERMINATOR = 0x5c
20
+
21
+ attr_accessor :name, :number, :type
22
+
23
+ # Create a PhoneNumber instance.
24
+ #
25
+ # @param name [String] Name associated to phone number.
26
+ # @param number [String] Phone number text.
27
+ # @param type [String] Phone number type.
28
+ # @return [PhoneNumber] PhoneNumber instance.
29
+ def initialize(name:, number:, type: " ")
30
+ @name = name
31
+ @number = number
32
+ @type = type
33
+ end
34
+
35
+ # Compile a packet for a phone number.
36
+ #
37
+ # @return [Array<Integer>] Array of integers that represent bytes.
38
+ def packet
39
+ [
40
+ number_with_type_characters,
41
+ name_characters,
42
+ TERMINATOR
43
+ ].flatten
44
+ end
45
+
46
+ private
47
+
48
+ def number_with_type_characters
49
+ phone_chars_for(number_with_type_padded)
50
+ end
51
+
52
+ def name_characters
53
+ return protocol_6_chars_for(name_with_number) if number.length > PHONE_DIGITS
54
+
55
+ protocol_6_chars_for(name, length: NAME_MAX_LENGTH)
56
+ end
57
+
58
+ def number_with_type_padded
59
+ number_with_type = "#{number} #{type}"
60
+ number_with_type.rjust(PHONE_DIGITS)
61
+ end
62
+
63
+ def name_with_number
64
+ number_name = NUMBER_NAME_DELIMITER
65
+ number_name += number[PHONE_DIGITS..NUMBER_NAME_MAX_NUMBER_LENGTH - 1]
66
+
67
+ name_length = NUMBER_NAME_MAX_NUMBER_LENGTH - number_name.length
68
+ truncated_name = name[..name_length - 1]
69
+
70
+ "#{truncated_name}#{number_name}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timex_datalink_client/helpers/cpacket_paginator"
4
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
5
+ require "timex_datalink_client/helpers/lsb_msb_formatter"
6
+
7
+ class TimexDatalinkClient
8
+ class Protocol6
9
+ class Eeprom
10
+ include Helpers::CpacketPaginator
11
+ include Helpers::LsbMsbFormatter
12
+ prepend Helpers::CrcPacketsWrapper
13
+
14
+ CPACKET_CLEAR = [0x93, 0x01]
15
+ CPACKET_SECT = [0x90, 0x01]
16
+ CPACKET_DATA = [0x91, 0x01]
17
+ CPACKET_END = [0x92, 0x01]
18
+
19
+ CPACKET_DATA_LENGTH = 32
20
+
21
+ attr_accessor :phone_numbers
22
+
23
+ # Create an Eeprom instance.
24
+ #
25
+ # @param phone_numbers [Array<PhoneNumber>] Phone numbers to be added to EEPROM data.
26
+ # @return [Eeprom] Eeprom instance.
27
+ def initialize(phone_numbers: [])
28
+ @phone_numbers = phone_numbers
29
+ end
30
+
31
+ # Compile packets for EEPROM data.
32
+ #
33
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
34
+ def packets
35
+ [CPACKET_CLEAR, header] + payloads + [CPACKET_END]
36
+ end
37
+
38
+ private
39
+
40
+ def header
41
+ [
42
+ CPACKET_SECT,
43
+ payloads_length,
44
+ 0x04,
45
+ phone_numbers_length,
46
+ ].flatten
47
+ end
48
+
49
+ def payloads
50
+ paginate_cpackets(header: CPACKET_DATA, length: CPACKET_DATA_LENGTH, cpackets: all_packets)
51
+ end
52
+
53
+ def all_packets
54
+ phone_numbers.map(&:packet).flatten
55
+ end
56
+
57
+ def payloads_length
58
+ lsb_msb_format_for(payloads.length)
59
+ end
60
+
61
+ def phone_numbers_length
62
+ length_lsb = lsb_msb_format_for(phone_numbers.length).first
63
+
64
+ [length_lsb, 0]
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
4
+
5
+ class TimexDatalinkClient
6
+ class Protocol6
7
+ class End
8
+ prepend Helpers::CrcPacketsWrapper
9
+
10
+ CPACKET_SKIP = [0x21]
11
+
12
+ # Compile packets for data end command.
13
+ #
14
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
15
+ def packets
16
+ [CPACKET_SKIP]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
4
+
5
+ class TimexDatalinkClient
6
+ class Protocol6
7
+ class NightModeOptions
8
+ include ActiveModel::Validations
9
+ prepend Helpers::CrcPacketsWrapper
10
+
11
+ CPACKET_NIGHT_MODE = 0x72
12
+
13
+ validates :night_mode_deactivate_hours, inclusion: {
14
+ in: 3..12,
15
+ message: "%{value} is invalid! Valid night mode deactivate hour values are 3..12."
16
+ }
17
+
18
+ validates :indiglo_timeout_seconds, inclusion: {
19
+ in: 3..10,
20
+ message: "%{value} is invalid! Valid Indiglo timeout second values are 3..10."
21
+ }
22
+
23
+ attr_accessor :night_mode_deactivate_hours, :indiglo_timeout_seconds, :night_mode_on_notification
24
+
25
+ # Create a NightModeOptions instance.
26
+ #
27
+ # @param night_mode_deactivate_hours [Integer] Automatically deactivate night mode after specified hours.
28
+ # @param indiglo_timeout_seconds [Integer] Turn Indiglo light off after specified seconds after each button push.
29
+ # @param night_mode_on_notification [Boolean] Toggle activating night mode on any pager or alarm event.
30
+ # @return [NightModeOptions] NightModeOptions instance.
31
+ def initialize(night_mode_deactivate_hours: 8, indiglo_timeout_seconds: 4, night_mode_on_notification: false)
32
+ @night_mode_deactivate_hours = night_mode_deactivate_hours
33
+ @indiglo_timeout_seconds = indiglo_timeout_seconds
34
+ @night_mode_on_notification = night_mode_on_notification
35
+ end
36
+
37
+ # Compile packets for night mode options.
38
+ #
39
+ # @raise [ActiveModel::ValidationError] One or more model values are invalid.
40
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
41
+ def packets
42
+ validate!
43
+
44
+ [
45
+ [
46
+ CPACKET_NIGHT_MODE,
47
+ night_mode_on_notification_formatted,
48
+ night_mode_deactivate_hours,
49
+ indiglo_timeout_seconds
50
+ ]
51
+ ]
52
+ end
53
+
54
+ def night_mode_on_notification_formatted
55
+ night_mode_on_notification ? 1 : 0
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
4
+
5
+ class TimexDatalinkClient
6
+ class Protocol6
7
+ class PagerOptions
8
+ include ActiveModel::Validations
9
+ prepend Helpers::CrcPacketsWrapper
10
+
11
+ CPACKET_PAGER = 0x73
12
+ ALERT_SOUND_SILENT = 6
13
+
14
+ validates :on_hour, inclusion: {
15
+ in: 0..23,
16
+ message: "%{value} is invalid! Valid on hour values are 0..23."
17
+ }
18
+
19
+ validates :on_minute, inclusion: {
20
+ in: 0..59,
21
+ message: "%{value} is invalid! Valid on minute values are 0..59."
22
+ }
23
+
24
+ validates :off_hour, inclusion: {
25
+ in: 0..23,
26
+ message: "%{value} is invalid! Valid off hour values are 0..23."
27
+ }
28
+
29
+ validates :off_minute, inclusion: {
30
+ in: 0..59,
31
+ message: "%{value} is invalid! Valid off minute values are 0..59."
32
+ }
33
+
34
+ validates :alert_sound, inclusion: {
35
+ in: 0..5,
36
+ allow_nil: true,
37
+ message: "%{value} is invalid! Valid alert sound values are 0..5 and nil."
38
+ }
39
+
40
+ attr_accessor :auto_on_off, :on_hour, :on_minute, :off_hour, :off_minute, :alert_sound
41
+
42
+ # Create a PagerOptions instance.
43
+ #
44
+ # @param auto_on_off [Boolean] Toggle turning pager on and off every day.
45
+ # @param on_hour [Integer] Hour to turn pager on at.
46
+ # @param on_minute [Integer] Minute to turn pager on at.
47
+ # @param off_hour [Integer] Hour to turn pager off at.
48
+ # @param off_minute [Integer] Minute to turn pager off at.
49
+ # @param alert_sound [Integer, nil] Pager alert sound (0 to 5 or nil for silent).
50
+ # @return [PagerOptions] PagerOptions instance.
51
+ def initialize(auto_on_off: false, on_hour: 0, on_minute: 0, off_hour: 0, off_minute: 0, alert_sound: 0)
52
+ @auto_on_off = auto_on_off
53
+ @on_hour = on_hour
54
+ @on_minute = on_minute
55
+ @off_hour = off_hour
56
+ @off_minute = off_minute
57
+ @alert_sound = alert_sound
58
+ end
59
+
60
+ # Compile packets for pager options.
61
+ #
62
+ # @raise [ActiveModel::ValidationError] One or more model values are invalid.
63
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
64
+ def packets
65
+ validate!
66
+
67
+ [
68
+ [
69
+ CPACKET_PAGER,
70
+ auto_on_off_formatted,
71
+ on_hour,
72
+ on_minute,
73
+ off_hour,
74
+ off_minute,
75
+ alert_sound_formatted
76
+ ]
77
+ ]
78
+ end
79
+
80
+ def auto_on_off_formatted
81
+ auto_on_off ? 1 : 0
82
+ end
83
+
84
+ def alert_sound_formatted
85
+ alert_sound || ALERT_SOUND_SILENT
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
4
+
5
+ class TimexDatalinkClient
6
+ class Protocol6
7
+ class SoundScrollOptions
8
+ include ActiveModel::Validations
9
+ prepend Helpers::CrcPacketsWrapper
10
+
11
+ CPACKET_SOUND_SCROLL = 0x71
12
+
13
+ validates :scroll_speed, inclusion: {
14
+ in: 0..2,
15
+ message: "%{value} is invalid! Valid scroll speed values are 0..2."
16
+ }
17
+
18
+ attr_accessor :hourly_chime, :button_beep, :scroll_speed
19
+
20
+ # Create a SoundScrollOptions instance.
21
+ #
22
+ # @param hourly_chime [Boolean] Toggle hourly chime.
23
+ # @param button_beep [Boolean] Toggle button beep.
24
+ # @param scroll_speed [Integer] Message scroll speed (0 to 2).
25
+ # @return [SoundScrollOptions] SoundScrollOptions instance.
26
+ def initialize(hourly_chime: false, button_beep: false, scroll_speed: 1)
27
+ @hourly_chime = hourly_chime
28
+ @button_beep = button_beep
29
+ @scroll_speed = scroll_speed
30
+ end
31
+
32
+ # Compile packets for sound and scroll options.
33
+ #
34
+ # @raise [ActiveModel::ValidationError] One or more model values are invalid.
35
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
36
+ def packets
37
+ validate!
38
+
39
+ [
40
+ [
41
+ CPACKET_SOUND_SCROLL,
42
+ hourly_chime_formatted,
43
+ button_beep_formatted,
44
+ scroll_speed
45
+ ]
46
+ ]
47
+ end
48
+
49
+ def hourly_chime_formatted
50
+ hourly_chime ? 1 : 0
51
+ end
52
+
53
+ def button_beep_formatted
54
+ button_beep ? 1 : 0
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
4
+
5
+ class TimexDatalinkClient
6
+ class Protocol6
7
+ class Start
8
+ prepend Helpers::CrcPacketsWrapper
9
+
10
+ CPACKET_START = [0x20, 0x00, 0x00, 0x06]
11
+
12
+ # Compile packets for data start command.
13
+ #
14
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
15
+ def packets
16
+ [CPACKET_START]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TimexDatalinkClient
4
+ class Protocol6
5
+ class Sync
6
+ PING_BYTE = [0x78]
7
+ FAST_MODE_BYTE = [0x56]
8
+ SYNC_1_BYTE = [0x55]
9
+ SYNC_2_BYTE = [0xaa]
10
+
11
+ SYNC_2_LENGTH = 40
12
+
13
+ attr_accessor :length
14
+
15
+ # Create a Sync instance.
16
+ #
17
+ # @param length [Integer] Number of 0x55 sync bytes to use.
18
+ # @return [Sync] Sync instance.
19
+ def initialize(length: 300)
20
+ @length = length
21
+ end
22
+
23
+ # Compile packets for syncronization data.
24
+ #
25
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
26
+ def packets
27
+ [PING_BYTE + FAST_MODE_BYTE + render_sync_1 + render_sync_2]
28
+ end
29
+
30
+ private
31
+
32
+ def render_sync_1
33
+ SYNC_1_BYTE * length
34
+ end
35
+
36
+ def render_sync_2
37
+ SYNC_2_BYTE * SYNC_2_LENGTH
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ require "timex_datalink_client/helpers/char_encoders"
6
+ require "timex_datalink_client/helpers/crc_packets_wrapper"
7
+
8
+ class TimexDatalinkClient
9
+ class Protocol6
10
+ class Time
11
+ include ActiveModel::Validations
12
+ include Helpers::CharEncoders
13
+ prepend Helpers::CrcPacketsWrapper
14
+
15
+ CPACKET_TIME = [0x32].freeze
16
+ CPACKET_FLEX_TIME = [0x33].freeze
17
+
18
+ ZONE_OFFSET_MAP = {
19
+ -39600 => 0x15,
20
+ -36000 => 0x16,
21
+ -32400 => 0x17,
22
+ -28800 => 0x18,
23
+ -25200 => 0x19,
24
+ -21600 => 0x1a,
25
+ -18000 => 0x1b,
26
+ -14400 => 0x1c,
27
+ -12600 => 0x14,
28
+ -10800 => 0x1d,
29
+ -7200 => 0x1e,
30
+ -3600 => 0x1f,
31
+ 0 => 0x00,
32
+ 3600 => 0x01,
33
+ 7200 => 0x02,
34
+ 10800 => 0x03,
35
+ 12600 => 0x0d,
36
+ 14400 => 0x04,
37
+ 16200 => 0x0e,
38
+ 18000 => 0x05,
39
+ 19800 => 0x0f,
40
+ 20700 => 0x11,
41
+ 21600 => 0x06,
42
+ 23400 => 0x12,
43
+ 25200 => 0x07,
44
+ 28800 => 0x08,
45
+ 32400 => 0x09,
46
+ 34200 => 0x13,
47
+ 36000 => 0x0a,
48
+ 39600 => 0x0b,
49
+ 43200 => 0x0c
50
+ }.freeze
51
+
52
+ FLEX_TIME_ZONE = 0x10
53
+ FLEX_DST_VALUE = 0x08
54
+
55
+ DATE_FORMAT_MAP = {
56
+ "%_m-%d-%y" => 0x00,
57
+ "%_d-%m-%y" => 0x01,
58
+ "%y-%m-%d" => 0x02,
59
+ "%_m.%d.%y" => 0x04,
60
+ "%_d.%m.%y" => 0x05,
61
+ "%y.%m.%d" => 0x06
62
+ }.freeze
63
+
64
+ validates :zone, inclusion: {
65
+ in: 1..2,
66
+ message: "%{value} is invalid! Valid zones are 1..2."
67
+ }
68
+
69
+ validates :date_format, inclusion: {
70
+ in: DATE_FORMAT_MAP.keys,
71
+ message: "%{value} is invalid! Valid date formats are #{DATE_FORMAT_MAP.keys}."
72
+ }
73
+
74
+ validates :flex_time_zone, unless: :flex_time, inclusion: {
75
+ in: [false],
76
+ message: "cannot be enabled unless FLEXtime is also enabled."
77
+ }
78
+
79
+ validates :flex_dst, unless: :flex_time, inclusion: {
80
+ in: [false],
81
+ message: "cannot be enabled unless FLEXtime is also enabled."
82
+ }
83
+
84
+ attr_accessor :zone, :is_24h, :date_format, :time, :name, :flex_time, :flex_time_zone, :flex_dst
85
+
86
+ # Create a Time instance.
87
+ #
88
+ # @param zone [Integer] Time zone number (1 or 2).
89
+ # @param is_24h [Boolean] Toggle 24 hour time.
90
+ # @param date_format ["%_m-%d-%y", "%_d-%m-%y", "%y-%m-%d", "%_m.%d.%y", "%_d.%m.%y", "%y.%m.%d"] Date format
91
+ # (represented by Time#strftime format).
92
+ # @param time [::Time] Time to set (including time zone).
93
+ # @param name [String, nil] Name of time zone (defaults to zone from time; 3 chars max).
94
+ # @param flex_time [Boolean] Toggle using FLEXtime to set time and date.
95
+ # @param flex_time_zone [Boolean] Toggle using FLEXtime to set time zone.
96
+ # @param flex_dst [Boolean] Toggle using FLEXtime to apply daylight savings time offset.
97
+ # @return [Time] Time instance.
98
+ def initialize(
99
+ zone:, is_24h:, date_format:, time:, name: nil, flex_time: false, flex_time_zone: false, flex_dst: false
100
+ )
101
+ @zone = zone
102
+ @is_24h = is_24h
103
+ @date_format = date_format
104
+ @time = time
105
+ @name = name
106
+ @flex_time = flex_time
107
+ @flex_time_zone = flex_time_zone
108
+ @flex_dst = flex_dst
109
+ end
110
+
111
+ # Compile packets for a time.
112
+ #
113
+ # @raise [ActiveModel::ValidationError] One or more model values are invalid.
114
+ # @return [Array<Array<Integer>>] Two-dimensional array of integers that represent bytes.
115
+ def packets
116
+ validate!
117
+
118
+ [
119
+ [
120
+ cpacket,
121
+ zone,
122
+ second,
123
+ hour,
124
+ minute,
125
+ month,
126
+ day,
127
+ year_mod_1900,
128
+ name_characters,
129
+ wday_from_monday,
130
+ formatted_time_zone,
131
+ is_24h_value,
132
+ date_format_value
133
+ ].flatten
134
+ ]
135
+ end
136
+
137
+ private
138
+
139
+ def cpacket
140
+ flex_time ? CPACKET_FLEX_TIME : CPACKET_TIME
141
+ end
142
+
143
+ def second
144
+ flex_time ? 0 : formatted_time.sec
145
+ end
146
+
147
+ def hour
148
+ flex_time ? 0 : formatted_time.hour
149
+ end
150
+
151
+ def minute
152
+ flex_time ? 0 : formatted_time.min
153
+ end
154
+
155
+ def month
156
+ flex_time ? 0 : formatted_time.month
157
+ end
158
+
159
+ def day
160
+ flex_time ? 0 : formatted_time.day
161
+ end
162
+
163
+ def formatted_name
164
+ name || time.zone || "tz#{zone}"
165
+ end
166
+
167
+ def name_characters
168
+ protocol_6_chars_for(formatted_name, length: 3, pad: true)
169
+ end
170
+
171
+ def year_mod_1900
172
+ flex_time ? 0 : formatted_time.year % 100
173
+ end
174
+
175
+ def wday_from_monday
176
+ flex_time ? 0 : (formatted_time.wday + 6) % 7
177
+ end
178
+
179
+ def formatted_time
180
+ time.dst? ? time + 3600 : time
181
+ end
182
+
183
+ def formatted_utc_offset
184
+ time.dst? ? time.utc_offset - 3600 : time.utc_offset
185
+ end
186
+
187
+ def formatted_time_zone
188
+ flex_time_zone ? FLEX_TIME_ZONE : ZONE_OFFSET_MAP[formatted_utc_offset]
189
+ end
190
+
191
+ def is_24h_value
192
+ is_24h ? 2 : 1
193
+ end
194
+
195
+ def date_format_value
196
+ format = DATE_FORMAT_MAP.fetch(date_format)
197
+ format += FLEX_DST_VALUE if flex_dst
198
+
199
+ format
200
+ end
201
+ end
202
+ end
203
+ end
@@ -24,7 +24,7 @@ class TimexDatalinkClient
24
24
  # Create a TimeName instance.
25
25
  #
26
26
  # @param zone [Integer] Time zone number (1 or 2).
27
- # @param name [String] Name of time zone (3 chars max)
27
+ # @param name [String] Name of time zone (3 chars max).
28
28
  # @return [TimeName] TimeName instance.
29
29
  def initialize(zone:, name:)
30
30
  @zone = zone
@@ -49,8 +49,12 @@ class TimexDatalinkClient
49
49
 
50
50
  private
51
51
 
52
+ def name_formatted
53
+ name || "tz#{zone}"
54
+ end
55
+
52
56
  def name_characters
53
- chars_for(name, length: 3, pad: true)
57
+ chars_for(name_formatted, length: 3, pad: true)
54
58
  end
55
59
  end
56
60
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TimexDatalinkClient
4
- VERSION = "0.11.0"
4
+ VERSION = "0.12.0"
5
5
  end
@@ -43,6 +43,17 @@ require "timex_datalink_client/protocol_4/sync"
43
43
  require "timex_datalink_client/protocol_4/time"
44
44
  require "timex_datalink_client/protocol_4/wrist_app"
45
45
 
46
+ require "timex_datalink_client/protocol_6/alarm"
47
+ require "timex_datalink_client/protocol_6/eeprom"
48
+ require "timex_datalink_client/protocol_6/eeprom/phone_number"
49
+ require "timex_datalink_client/protocol_6/end"
50
+ require "timex_datalink_client/protocol_6/night_mode_options"
51
+ require "timex_datalink_client/protocol_6/pager_options"
52
+ require "timex_datalink_client/protocol_6/sound_scroll_options"
53
+ require "timex_datalink_client/protocol_6/start"
54
+ require "timex_datalink_client/protocol_6/sync"
55
+ require "timex_datalink_client/protocol_6/time"
56
+
46
57
  require "timex_datalink_client/protocol_7/eeprom"
47
58
  require "timex_datalink_client/protocol_7/eeprom/activity"
48
59
  require "timex_datalink_client/protocol_7/eeprom/calendar"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timex_datalink_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxwell Pray
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-27 00:00:00.000000000 Z
11
+ date: 2023-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -66,90 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.6.0
69
- - !ruby/object:Gem::Dependency
70
- name: mdl
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: 0.12.0
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: 0.12.0
83
- - !ruby/object:Gem::Dependency
84
- name: rspec
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 3.11.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 3.11.0
97
- - !ruby/object:Gem::Dependency
98
- name: rubocop
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: 1.41.1
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: 1.41.1
111
- - !ruby/object:Gem::Dependency
112
- name: rubocop-github
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: 0.20.0
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: 0.20.0
125
- - !ruby/object:Gem::Dependency
126
- name: tzinfo
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: 2.0.5
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: 2.0.5
139
- - !ruby/object:Gem::Dependency
140
- name: yard-junk
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: 0.0.9
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - "~>"
151
- - !ruby/object:Gem::Version
152
- version: 0.0.9
153
69
  description:
154
70
  email: synthead@gmail.com
155
71
  executables: []
@@ -201,6 +117,16 @@ files:
201
117
  - lib/timex_datalink_client/protocol_4/sync.rb
202
118
  - lib/timex_datalink_client/protocol_4/time.rb
203
119
  - lib/timex_datalink_client/protocol_4/wrist_app.rb
120
+ - lib/timex_datalink_client/protocol_6/alarm.rb
121
+ - lib/timex_datalink_client/protocol_6/eeprom.rb
122
+ - lib/timex_datalink_client/protocol_6/eeprom/phone_number.rb
123
+ - lib/timex_datalink_client/protocol_6/end.rb
124
+ - lib/timex_datalink_client/protocol_6/night_mode_options.rb
125
+ - lib/timex_datalink_client/protocol_6/pager_options.rb
126
+ - lib/timex_datalink_client/protocol_6/sound_scroll_options.rb
127
+ - lib/timex_datalink_client/protocol_6/start.rb
128
+ - lib/timex_datalink_client/protocol_6/sync.rb
129
+ - lib/timex_datalink_client/protocol_6/time.rb
204
130
  - lib/timex_datalink_client/protocol_7/eeprom.rb
205
131
  - lib/timex_datalink_client/protocol_7/eeprom/activity.rb
206
132
  - lib/timex_datalink_client/protocol_7/eeprom/calendar.rb
@@ -224,7 +150,7 @@ files:
224
150
  - lib/timex_datalink_client/protocol_9/time_name.rb
225
151
  - lib/timex_datalink_client/protocol_9/timer.rb
226
152
  - lib/timex_datalink_client/version.rb
227
- homepage: https://github.com/synthead/timex_datalink_client/tree/v0.11.0
153
+ homepage: https://github.com/synthead/timex_datalink_client/tree/v0.12.0
228
154
  licenses:
229
155
  - MIT
230
156
  metadata: {}
@@ -243,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
169
  - !ruby/object:Gem::Version
244
170
  version: '0'
245
171
  requirements: []
246
- rubygems_version: 3.3.7
172
+ rubygems_version: 3.4.10
247
173
  signing_key:
248
174
  specification_version: 4
249
175
  summary: Write data to Timex Datalink devices with an optical sensor